Textfile sicher sperren bei gleichzeitigem Zugriff?
remuen
- php
Ich lese die Daten aus einem Textfile ein, mutiere eine oder mehrere Zeilen und schreibe die mutierten Daten wieder zurück. In letzter Zeit ist zweimal das Problem aufgetreten, dass die Daten plötzlich nur noch unvollständig im Textfile vorhanden waren. Meine Vermutung: Wenn zwei oder mehr User gleichzeitig darauf zugreifen, kommt da was in Unordung.
Mein heutiges Script sieht so aus:
$statistics = file("$filename");
....
(Hier Daten in Array mutieren/aufbereiten)
$statfile = fopen ("$filename","w+");
flock($statfile, LOCK_EX);
for($b=0;$b<=$usermenge-1;$b++)
{
fputs ($statfile,"$fragenstat[$b]$nl");
}
flock($statfile,LOCK_UN);
fclose($statfile);
Das Datenchaos entsteht meiner Meinung nach dann, wenn ich mit file() die Daten einlese, während dem
ein zweites Script (ein zweiter User) gerade Daten ins gelockte File schreibt (ich kenne die Abläufe und das Verhalten der verschiedenen Funktionen leider zu wenig).
Wie kann ich verhindern, dass hier etwas passiert????
Überlegt habe ich mir, entweder das File anstelle mit file() direkt ins Array zu lesen, dies zeilenweise zu tun, das heisst beispielsweise so:
$statfile = fopen($filename, 'r');
flock($statfile, LOCK_EX);
while (!feof($statfile )) {
$fragenstat = fgets($statfile , 256);
.....
(Hier Daten aufbereiten/mutieren)
for($b=0;$b<=$usermenge-1;$b++)
{
fputs ($statfile,"$fragenstat[$b]$nl");
}
flock($statfile,LOCK_UN);
fclose($statfile);
Hier würde das Textfile schon beim Auslesen gelockt, d.h. ein zweites Script kann erst darauf zugreifen, wenn das Textfile wieder freigegeben wurde.
Meine Fragen:
Stimmt meine Überlegung resp. funktioniert diese Lösung zuverlässig, so dass mir im Textfile kein Chaos mehr entsteht?
Falls nein, wie müsste ich das Ganze lösen? Ev. mit einem eigenen Lockfile?
Vielen Dank im voraus für Eure Ratschläge!
Gruss
René
hi,
Ich lese die Daten aus einem Textfile ein, mutiere eine oder mehrere Zeilen und schreibe die mutierten Daten wieder zurück.
mutieren ...? klingt gefährlich :-)
Das Datenchaos entsteht meiner Meinung nach dann, wenn ich mit file() die Daten einlese, während dem
ein zweites Script (ein zweiter User) gerade Daten ins gelockte File schreibt (ich kenne die Abläufe und das Verhalten der verschiedenen Funktionen leider zu wenig).
dann mache dich doch erstmal zu ihrer funktions- und wirkungsweise im manual schlau.
gerade bei flock könnten auch die user-kommentare im manual interessant sein, http://www.php.net/manual/de/function.flock.php.
gruss,
wahsaga
Hallo
mutieren ...? klingt gefährlich :-)
Machst Du das nie in einer Datenbank oder in meinem Fall aus bestimmten Gründen in einem Textfile?
dann mache dich doch erstmal zu ihrer funktions- und wirkungsweise im manual schlau.
Danke für den Tipp, habe ich schon gemacht. Aber leider habe ich nicht beschrieben gefunden, was da genau wann abläuft. Also bleibt es einfach bei einer Vermutung, bis mir jemand was genaueres sagen kann.
Gruss,
René
Hello,
Ich lese die Daten aus einem Textfile ein, mutiere eine oder mehrere Zeilen und schreibe die mutierten Daten wieder zurück. In letzter Zeit ist zweimal das Problem aufgetreten, dass die Daten plötzlich nur noch unvollständig im Textfile vorhanden waren. Meine Vermutung: Wenn zwei oder mehr User gleichzeitig darauf zugreifen, kommt da was in Unordung.
Ja, da mal Dir mal ein Bild, was Du denn da tust...
(Phasenzeitdiagramm)
Mein heutiges Script sieht so aus:
$statistics = file("$filename");
Hier wurde die Datei ohne Rücksicht auf Sperren eingelesen. Das bedeutet, dass während dieses Lesevorganges ggf. jemand anders die Datei beschreiben hat.
....
(Hier Daten in Array mutieren/aufbereiten)$statfile = fopen ("$filename","w+");
Hier wurde die Datei einfach zum Schreiben geöffnet und damit zerstört, unabhängig davon, ob gerade jemand anders liest.
flock($statfile, LOCK_EX);
Hier wurde versucht, in die Locktabelle einen Lock einzutragen und solange zu warten (da LOCK_NB nicht addiert wurde) bis der Lock durchgeführt werden konnte.
for($b=0;$b<=$usermenge-1;$b++)
{
fputs ($statfile,"$fragenstat[$b]$nl");
}
Hier wird nun die Datei geschrieben.
flock($statfile,LOCK_UN);
...und unnötigerweise wieder freigegeben,
fclose($statfile);
denn die Freigabe erfolgt automatisch beim fclose().
Wenn es also nicht einen besonderen Grund dafür gibt, Dateihandles länger zu behalten, dann kann man sich das entsperren sparen.
Die Dateisperre findet mit der Funktion flock() in PHP und auch in C nur "advisory" statt, also absolut parallel zum fopen(), fread(), fwrite()... Die bekommen nichts von der Sperre mit. Die Sicherheit muss Deine Applikation leisten.
Wenn man also dieselbe Datei sperren will, die man lesen und beschreiben will, dann kann man nur non destructive open benutzen. Entweder legt man also vorher alle Dateien an, die man benutzen will und öffnet sie mit fopen(...,"r+") oder man benutzt fopen(...,"a+) und rewind() oder fseek().
Da man die Datei nicht löscht, bevor sie nei geschreiben wird, empfihlt es sich, den ggf. überstehenden Rest nach dem Neuschreiben mit ftruncate() wieder abzuschneiden.
Also öffen mit fopen(...,"a+");
Sperren mit flock($fh, LOCK_EX);
Zurückspulen mit fseek($fh,0,SEEK_SET);
Lesen mit fread();
Stream -> Satzarray
Bearbeitungen einfügen, anhängen etc...
Satzarray -> Stream
$len = strlen($stream);
zurückspulen mit fseek(), da der Dateizaieger vom Lesen noch am Ende steht.
Schreiben mit fwrite($fh,$data,$len)
Trimmen mit ftruncate($fh, $len);
Und schnell wieder schließen und damit freigeben mit fclose($fh);
Wenn man nun diese Dateien auch gleichzeitig durch das Programm anlegen und löschen lässt, kann jeder der Befehle "auf die Schnauze fallen", obwohl die Datei gesperrt war. Es ist nämlich leider möglich, eine gesperrte Datei zu löschen.
Es sollte also tunlichst keine andere Applikation auf die Dateien (schreibend, löschend) zugreifen, als dafür vorgesehen ist.
Liebe Grüße aus http://www.braunschweig.de
Tom
Hallo Tom
Vielen Dank für Deine hilfreichen Infos. Nun ist mir auch einiges klarer, denn
Hier wurde die Datei ohne Rücksicht auf Sperren eingelesen. Das bedeutet, dass während dieses Lesevorganges ggf. jemand anders die Datei beschreiben hat.
Hier wurde die Datei einfach zum Schreiben geöffnet und damit zerstört, unabhängig davon, ob gerade jemand anders liest.
genau das habe ich vermutet. War anfänglich kein Problem, da nur zwei, drei Spieler täglich das Quiz (darum geht es) spielten, gestern Abend z.B. jedoch gleich 5 Spieler gleichzeitig.
Da man die Datei nicht löscht, bevor sie nei geschreiben wird, empfihlt es sich, den ggf. überstehenden Rest nach dem Neuschreiben mit ftruncate() wieder abzuschneiden.
Im meinem konkreten Fall dürfte das zwar nicht nötig sein (es sind immer gleich viele Records mit gleicher Länge), aber sicher ist sicher ...
Es sollte also tunlichst keine andere Applikation auf die Dateien (schreibend, löschend) zugreifen, als dafür vorgesehen ist.
Das ist gewährleistet und das File ist auch gegen (böswillige) externe Zugriffe in einem eigenen Unterverzeichnis ziemlich gut geschützt mit .htaccess.
Zusatz-Frage: Gehe ich recht in der Annahme, dass, solange das File gesperrt ist, die weiteren Zugriffe anderer User automatisch in eine "Warteschlange" gesetzt werden, bis das File wieder entsperrt ist?
Dankende Grüsse
René
Hello,
genau das habe ich vermutet. War anfänglich kein Problem, da nur zwei, drei Spieler täglich das Quiz (darum geht es) spielten, gestern Abend z.B. jedoch gleich 5 Spieler gleichzeitig.
Bei vernünftig schnellen Servern kommen die Probleme meistens erst bei mehr als 50 Zugriffen in der Sekunde. Sven Rautenberg hatte da mal mein Demoscript http://bitworks.de/~selfHTML/speichern.php mit ca. 130 Zugriffen die Sekunde gekillt, bevor es die flocks erhalten hat.
Da man die Datei nicht löscht, bevor sie nei geschreiben wird, empfihlt es sich, den ggf. überstehenden Rest nach dem Neuschreiben mit ftruncate() wieder abzuschneiden.
Im meinem konkreten Fall dürfte das zwar nicht nötig sein (es sind immer gleich viele Records mit gleicher Länge), aber sicher ist sicher ...
Die gleiche Länge lässt sich nur gewährleisten, wenn man mit einer *_pad() Funktion immer brav auffüllt oder mit pack() und unpack() arbeitet. dann könntest Du auch satzweises Schreiben ermöglichen und Satzsperren (über eine andere Tabelle) setzen.
Zusatz-Frage: Gehe ich recht in der Annahme, dass, solange das File gesperrt ist, die weiteren Zugriffe anderer User automatisch in eine "Warteschlange" gesetzt werden, bis das File wieder entsperrt ist?
Das macht die Funktion flock(), wenn man sie ohne LOCK_NB benutzt. Die hält das Script, dass sperren will, solange an, wis sie true zurückgeben kann, oder bis TimeOut erreicht ist. Letzteres habe ich aber noch nicht ausprobiert, wie lange das effektiv dauert. Wenn man flock also auf die billige verwendet, kann man ggf. bei einem Deadlock (google mal danach) seine 150 Intanzen vom Apachen locker dicht machen.
Die TimeOut-Zeit ist sicher nur die Netto-rechenzeit und flock() geht zwischendurch immer in eine idle-Sequenz ( sleep() ), und die zählt nicht zur Rechenzeit oder sorgt sogar dafür, dass diese von vorne anfängt zu zählen.
Liebe Grüße aus http://www.braunschweig.de
Tom
Hi Tom
Nochmals herzlichen Dank. Als noch relativ unerfahrener PHP-Coder werde ich das alles inkl. Deinem Demo-Script nochmals in Ruhe "zu mir nehmen" und mein Script entsprechend anpassen.
Bei vernünftig schnellen Servern kommen die Probleme meistens erst bei mehr als 50 Zugriffen in der Sekunde.
Die 50 Zugriffe habe ich zwar noch nicht und der Server sollte eigentlich auch schnell genug sein, jedoch wenn unser von uns allen innigst geliebter Mr. Murphy es will, knallt's halt schon vorher. Lieber jetzt als wenn dann wirklich mal irgendwann 50 Spieler gleichzeitig dran sein sollten ...
Nochmals vielen Dank und liebe Grüsse
René