Infüweko Geschrieben 15. Februar 2010 Teilen Geschrieben 15. Februar 2010 (bearbeitet) Hallo, ich bin seit einem halben Jahr hinter einem Problem her, dass sich einfach nicht lösen lässt. System: Unix FreeBSD, PHP5 Beschreibung des Programmteils: Der untenstehende Code ist Teil einer Klasse, der exklusiven Zugriff auf named pipes steuert. Dazu wird ein Filelock-System eingesetzt, da Metaphoren-Lock nicht für derartige Belastungen, wie sie auf dieses System wirken, funktionieren. (Hinweis: pro Sekunde treffen mehrere Anfragen ein). Fehlerbeschreibung: Das System versagt in unregelmäßigen Abständen, da das Filelock nicht mehr durchgeführt werden kann. Sprich, die Funktion, die das Lock durchführt, verweigert einfach den Dienst, ein Locking-Mechanismus ist nichtmehr möglich und die geschützte Schnittstelle von außen überhaupt nicht mehr ansprechbar, da das Lock defekt ist. Durchgeführte Maßnahmen und Tests: - Neustart des Apache -> zeigt keine Wirkung (eigentlich sollten danach hängende Locks frei werden) - Während des Fehlers sind keine gesetzten Locks feststellbar, dennoch kann kein neues Lock gesetzt werden - Bei Auftreten des Fehlers wurde die lock Datei gelöscht und eine neue angelegt (neues file-inode). Das behob den Fehler ebenfalls nicht. Eine neue Datei mit ganz neuem Namen brachte ebenfalls keine Erkenntnisse. - In den Serverstatistiken ist nichts auffälliges zu sehen, Spitzenlasten gab es nicht zu den Fehlerzeiten - Komplettes Rebooten des Servers ist derzeit die einzige Lösung Anliegen: Hat irgendjemand eine leise Ahnung, warum das Filelock einfach zufällig versagt? Und warum ein Neustart des Apache die Locks nicht freigibt, sondern nur ein totaler Neustart des Servers hilft? Ich bin mehr als ratlos und kann dieses Problem auch nirgendwo beschrieben finden. Hier jetzt der Code, etwas vereinfacht. Das Script durchläuft mehrere Schleifen, um das Filelock zu bekommen. Danach bricht es ab und das Script stirbt kurz danach. Es gibt also keinen Flaschenhals. $this->flock_path = 'lock'; $this->flock_mode = 'w'; $this->flock_sleep_if_locked = 10; $this->flock_retry_if_locked = 50; private function lock_ask_answer($prepared_input) { $fp = fopen($this->flock_path,$this->flock_mode); $v = 0; $lock_success = FALSE; do { if(!$fp) $fp = fopen($this->flock_path,$this->flock_mode); else { if($v>0) { if($this->debug) echo '<font color="orange">v='.$v.': Lock-Datei ('.$this->flock_path.') konnte nicht genommen - nächster Versuch...</font><br>'; usleep($this->flock_sleep_if_locked); } $lock_success = flock($fp,LOCK_EX|LOCK_NB,$eWouldBlock); } ++$v; } while($v<=$this->flock_retry_if_locked AND (!$fp OR !$lock_success OR $eWouldBlock)); if(!$fp OR !$lock_success OR $eWouldBlock) { if($this->debug) echo '<font color="red">Lock-Datei konnte nicht genommen werden.</font><br>'; fclose($fp); return FALSE; } else { // Script gegen ungewollte Abbrüche blockieren ignore_user_abort(TRUE); // Hier wird in die named Pipes geschrieben und daraus ausgelesen...Codeteil wurde zur Übersicht entfernt flock($fp,LOCK_UN); fclose($fp); ignore_user_abort(FALSE); // Script wieder frei return $answer; } } [/PHP] Bearbeitet 15. Februar 2010 von Infüweko Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
flashpixx Geschrieben 15. Februar 2010 Teilen Geschrieben 15. Februar 2010 Deine PHP Version ist nicht ausreichend, es gibt mehrer 5er Versionen. Zusätzlich wären noch Log Auszügel relefant, so wie das eingesetzte Dateisystem (siehe PHP: flock - Manual da das ganze bei NFS nicht funktionieren wird). Hast Du Die Bugfixliste durch gesehen? Wofür benötigst Du eine Pipe? Ich halte das für eine unsichere Komponente im Script. Mich stört im Moment die Schleife, denn ich habe das Gefühl, dass sie unter gewissen Umständen dazu führt, dass Du non-stop versuchst Locks anzulegen. Liefere mehr Informationen! Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Infüweko Geschrieben 15. Februar 2010 Autor Teilen Geschrieben 15. Februar 2010 Hallo. Die PHP-Version ist 5.2.5 Dateisystem ist UFS2. Wobei das auch nicht die Ursache sein kann, denn das System läuft wochenlang und zeigt dann urplötzlich und scheinbar zufällig (diskutierbar) das verhalten. Mit falschem Dateisystem würde es ja nie funktionieren. Die Bugeinträge waren für mich wenig aufschlussreich, da dieses Verhalten nirgendwo beschrieben ist. Wie kommst du auf die Idee, dass immer ein neues Lock angelegt wird? Was die Pipes betrifft, da sind wir abhängig von der angekoppelten Software, die wir damit bedienen. Wir wissen, dass das nicht optimal ist, aber anders ist es nicht möglich, solang wir auf den Hersteller der entsprechenden Software angewiesen sind. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
flashpixx Geschrieben 15. Februar 2010 Teilen Geschrieben 15. Februar 2010 Die PHP-Version ist 5.2.5 Dateisystem ist UFS2. Die Bugeinträge waren für mich wenig aufschlussreich, da dieses Verhalten nirgendwo beschrieben ist. Also scheiden schon mal die gröbsten Fehler aus. Wie kommst du auf die Idee, dass immer ein neues Lock angelegt wird? Erfahrungswert :-P Ich versuche einmal technische Probleme, d.h. Bugs in der Installation auszuschließen (haben wir ja), d.h. meine nächste Vermutung wäre eben ein Bug in Eurem Code, der sich eben nur unter Last bemerkbar macht. Was die Pipes betrifft, da sind wir abhängig von der angekoppelten Software, die wir damit bedienen. Wir wissen, dass das nicht optimal ist, aber anders ist es nicht möglich, solang wir auf den Hersteller der entsprechenden Software angewiesen sind. Wäre die Frage, ob es ein Bug evtl von Eurer oder der Software des Herstellers ist. Mir geht folgendes durch den Kopf: Du rufst das erste Mal PHP Script auf, das setzt den Lock führt seine Aktionen auf der Pipe durch und gibt den Lock wieder frei (das wäre so bei geringer Last). Wenn nun Last anfällt, wird ein Lock erzeugt, aber die Aktionen des kompletten Scripts dauern bei Last länger, wenn nun die max. Execution Time von PHP erreicht ist, wird das Script beendet ohne den Lock frei zu geben. Die nächsten gestarteten Scripte sehen einen Lock und laufen in die while-Schleife und versuchen zu warten bis zu einen Lock setzen können bzw bis die max. Execution Time erreicht ist. Da aber das erste Script was den Lock setzt beendet wurde, ist der Lock nicht mehr freigegeben worden, d.h. alles was danach anfällt sieht einen bestehenden Lock. Der Sinn der Semaphore ist genau dieses, dass man zentral das ganze verwaltet, damit solche Probleme erst gar nicht auftreten. Meine Aussage, dass Du versuchst Locks anzulegen ist dadurch, dass eben das Script nicht als einzelne Instanz läuft, sondern eben mehrfach, je nach Anzahl der Zugriffe. Du versuchst nun über unterschiedliche Instanzen des Scriptes eine Synchronisation zu bauen für Deine Pipe. Ich gehe stark davon aus, dass das Lock bei Last nicht mehr freigegeben wird. Die Lösung wäre eben Semaphore zu verwenden, die über alle Scriptinstanzen verwendet werden, oder eben eine Schnittstelle zu benutzen, die mit konkurrierenden Zugriffen klar kommt. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Infüweko Geschrieben 18. Februar 2010 Autor Teilen Geschrieben 18. Februar 2010 Hallo, der Hinweis mit der Executiontime ist interessant. Dennoch stellt sich mir dann die Frage, warum keine serverseitige Freigabe erfolgt. Da bleibt ja nur die Variante übrig, dass es eine Verzögerung (keinen Ausfall) auf unbestimmte Zeit gibt. Dennoch müsste sich danach entweder das Lock selbst lösen (Client tot, Server räumt auf) oder der Server abstürzen (ebenfalls Aufräumen). Also auch nicht logisch. Semphoren fallen aus. Die funktionieren bei unserer Last nichtmehr zuverlässig, da gab es ähnliche Blockierungserscheinungen, die sich nur durch Neustart lösen liesen. Da trat es sogar noch häufiger auf. Ein Verzicht auf lock ist unmöglich, täglich schießen Millionen Datensätze durch die Pipes. Wie kann ich denn realisieren, dass bei Abbruch des Clienten ein Unlock stattfindet? Müsste sich doch eigentlich per shutdown_function regeln lassen. _destruct ist ja nutzlos, wenn das Script zuvor im timeout abbricht, da dann kein destruct erfolgt. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
flashpixx Geschrieben 18. Februar 2010 Teilen Geschrieben 18. Februar 2010 der Hinweis mit der Executiontime ist interessant. Dennoch stellt sich mir dann die Frage, warum keine serverseitige Freigabe erfolgt. Wie wäre es, wenn Du die Dokumentation dazu liest: PHP: Laufzeit-Konfiguration - Manual Semphoren fallen aus. Die funktionieren bei unserer Last nichtmehr zuverlässig, da gab es ähnliche Blockierungserscheinungen, die sich nur durch Neustart lösen liesen. Da trat es sogar noch häufiger auf. Ein Verzicht auf lock ist unmöglich, täglich schießen Millionen Datensätze durch die Pipes. Ich gehe davon aus, dass der Fehler nicht bei den Semaphoren liegt, denn das würde ja heißen, dass die meisten parallelen Algorithmen nicht mit dieser Anzahl an Datensätzen klar kommen würden. Ich gehe stark von einer Fehlkonfiguration des Server aus oder Euer Code ist schlecht programmiert. Ich bleibe weiterhin der Meinung, dass Semaphore das richtige Weg sind (oder eben der Einsatz einer Datenbank wobei das dann von der Anwendung unterstützt werden muss) und der Fehler in Deinem Code liegt Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Infüweko Geschrieben 18. Februar 2010 Autor Teilen Geschrieben 18. Februar 2010 Das Problem mit den Semaphoren ist, dass durch PHP kein Exklusivzugriff bei hoher Last gewährleistet ist. Es gibt dazu auch entsprechende Bugeinträge. Ursache: Holt man sich mit sem_get einen Semaphor und aquiriert ihn mit sem_acquire, so kann zwischen sem_get und sem_aquire ein anderer Prozess sem_get ausgeführt haben. Beide Prozesse haben also in Information, dass der Semaphor frei ist. Dadurch kommt es zur Blockierung des Systems. Wir haben max_acquire natürlich auf 1 gehabt und sämtliche Servereinstellungen geprüft. Solang alles unter Normallast läuft, ist alles ok, aber wehe dem, eine Spitzenlast tritt auf, dann gibt es sofort das PHP-bezogene Problem. Ist ja auch logisch, dass zwischen zwei Funktionen ein anderer Prozess eingreifen kann, wenn er schnell genug ist. Bei den Semaphoren habe ich ebenfalls schon Stunden investiert und das Internet durchkämmt, aber nirgendwo einen besseren Code gefunden als den, den ich mir erarbeitet hatte. Im Umkehrschluss müsste das ja sonst heißen, dass alle Scripte im Netz nicht richtig mit Semaphoren umgehen. Genau diese Scripte funktionieren aber genauso wenig, wie Eigenentwicklungen. Wenn es am Server liegen soll, so frage ich mich, was genau die Ursache. Mehr als shm aktivieren gibt es mM nach nicht. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
flashpixx Geschrieben 18. Februar 2010 Teilen Geschrieben 18. Februar 2010 Es gibt dazu auch entsprechende Bugeinträge. Kannst Du evtl auf PHP 6 migrieren? Wenn es am Server liegen soll, so frage ich mich, was genau die Ursache. Mehr als shm aktivieren gibt es mM nach nicht. Wie ist PHP im Webserver integriert? Als Modul oder führst Du das über CGI aus (sprich Du rufst den Interpreter auf)? Ein Aufruf des Interpreters ist immer langsamer, als ein Modul. Wie sieht es mit solchen Sachen wie Eaccelerator? Wie sieht es mit Load-Balancing des Servers aus, d.h. du stellst einen zweiten auf und verbindest Dich ebenfalls über eine eigen Pipe? Als Alternative wäre ja noch die Möglichkeit, dass Du die Anfragen, die über das Web kommen erst einmal selbst cachst z.B. in einer Datenbank und dann über einen Cronjob o.ä. diese nacheinander verarbeitet, so dass eben nicht das PHP Script den Lock durchführt, sondern ein externen Tool, was Du z.B. in C++ implementieren kannst. Du könntest dann ja auch mehrere Server aufstellen, die via Load-Balancing die Last verteilen und in eine gemeinsame Datenbank die Jobs schreiben, die dann zentral von einem weiteren Server verarbeitet werden Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Mr Unix Geschrieben 23. Februar 2010 Teilen Geschrieben 23. Februar 2010 $lock_success = flock($fp,LOCK_EX|LOCK_NB,$eWouldBlock); Hab von PHP zwar nicht so viel Ahnung, dafuer aber von C und FreeBSD. 1. Du verwendest LOCK_NB. Wenn viele Instanzen gleichzeitig auf die Ressource zurueckgreifen, dann kann es vorkommen, dass hier kein Lock stattfindet. -> Ein paar Millisekunden usleep(). Nochmal versuchen. Nicht geklappt? Wieder usleep(), usw... Du brichst hier momentan ab, wenn kein Lock stattfindet. -> Verwendest du nur LOCK_EX, dann wartet der Teil bis das Lock wieder frei ist und du bekommst nur eine Rueckgabe wenn ein Fehler aufgetreten ist oder die Operation erfolgreich war. 2. Forkst du irgendwo im Rest des Codes? Ein Lock ist an eine File attached. Oeffnest du einen fd, siehst das Ganze so aus: fd >file > vnode. Forkst du nun irgendwo im Programm, dann kopiert dir fork() all deine offenen file descriptors und damit auch die Locks. Das heisst sowohl Parent als auch Child besitzen den gleichen Lock. Gleiches gilt auch fuer deine Semaphoren und jede andere Art von IPC. Also entweder an anderer Stelle den fd oeffnen, nochmals locken oder auf LOCK_NB verzichten. mfg Unix Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Infüweko Geschrieben 23. Februar 2010 Autor Teilen Geschrieben 23. Februar 2010 Hallo, vielleicht sollte ich erwähnen, dass es sich bei dem von uns genutzten System um eine Liveabfrage handelt. D.h. der Softwarehersteller hat ein Programm geliefert, was auf dem Webserver läuft und über Pipes kommuniziert. Wir können dieses ansprechen, indem wir eine Frage-Pipe beschreiben und auf die Antwort in einer Antwort-Pipe warten, die das Programm beschreibt. Die von uns entwickelte Applikation ist ein Webdienst, der über einen einfachen Aufruf einer Webseite Daten von diesem Programm auf dem Server erhalten muss. Dazu erfolgt on-the-fly die Abfrage an das Programm und direkt auch die Antwort, danach wird die Schnittstelle für weitere Clienten freigegeben. Das ist auch der Grund, warum wir keine zeitintensiven MySQL-Caches oder gar Cronjobs einsetzen können - alles muss wie am Förderband in sekundenschnelle ausgeliefert werden. Damit kein Flaschenhals entsteht, wurde LOCK_EX und LOCK_NB genutzt, da sonst alle Clienten unter Umstände ewig warten würden. Bei hunderten Zugriffen pro Minute würde unser Server damit in wenigen Sekunden oder Minuten überlastet sein. Um den Flaschenhals zu vermeiden, fragen wir mit einer bestimmten Anzahl ab, ob LOCK erfolgreich war. Wenn nicht, wird es nochmal versucht, bis eben das Limit erreicht ist. Danach wartet das Script nochmal etwas länger und versucht es ebenfalls einige wenige Male nochmal von vorn. Wenn kein Reinkommen möglich ist, wird irgendwann einfach abgebrochen und der Nutzer darüber informiert, dass die Daten aktualisiert werden. Beim nächsten F5 ist dann meist Inhalt abrufbar. Heute morgen gab es erneut diesen Fehler, obwohl wir nun eine neue PHP-Version haben und auch das Programm selbst eine neue Version hat. Als letzte Idee kam mir heute, dass das Programm selbst aus irgendeinem Grund nichtmehr in die Antwort-Pipe schreibt, wodurch der aktive Client ewig auf die Antwort wartet und kein UNLOCK stattfindet. Ich werde mal testen, die Anfrage/Antwort-Stelle in ein timelimit zu kapseln. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.