Das Weblog kann Spuren von Apple-Begeisterung enthalten.

PCRE Backtrack Limit in PHP 5.2

9. Februar 2007

Eine Frage an die PHP-Experten unter meinen Lesern. Mal wieder, ja … Aber diesmal stecke ich echt fest und weiß keine Lösung!

Loudblog hat ziemliche Probleme mit PHP 5.2. Ich habe das jetzt soweit lokalisieren können, dass ich eine konkrete Frage stellen kann.

Für das Parsen und Dynamisieren der Loudblog-Templates verwende ich rekursive Schleifen mit Regular Expressions. Jaja, das soll man nicht, aber es ist jetzt nun mal so. Das Problem: Pro Seitenabruf werden locker über 200 preg_match_all() oder preg_replace() Aktionen aufgerufen. Doch PHP 5.2 sagt nach 128 solcher Aufrufe Tschüss und weigert sich, das Template weiterzuparsen.

Das Problem scheint das in PHP 5.2 neu hinzugekomme backtrack_limit zu sein. Was genau das ist und wie man das irgendwie zurücksetzen kann, weiß ich nicht. php.ini bringt mir nichts, denn Loudblog soll auch auf 08/15-Servern laufen, bei denen man keine Konfigurationsrechte besitzt.

Also: Wie kann man diese backtrack-Zeugs resetten, löschen oder deaktivieren, damit ich weiterhin große Mengen von RegEx-Befehlen aufrufen kann. Die Performance beinträchtigt es im Übrigen nicht – Ein durchschnittliches Template ist in 0.2 Sekunden fertig gerendert.

Nachtrag am 12.2.2007: Abgesehen davon, dass ich am Wochenende die komplette Parsing-Engine einmal mit DOM und einmal mit SimpleXML nachgebaut haben, um das Problem zu umgehen, ist die eigentliche Lösung des Problems schnell erzählt und hat nichts mit Qualität, sondern mit Quantität zu tun. Es handelt sich um exakt eine Zeile Code, die ich einfügen musste:

ini_set('pcre.backtrack_limit', 1000000);

Das Standard-Limit für Backtracks liegt bei PHP auf 100.000, ich erhöhe es einfach auf 1 Million. Fertig. Naja, das ist natürlich keine Entschuldigung für den miesen Code, aber pragmatisch und schnell. Danke für die Inspiration an Sebastian Steins.

14 Kommentare

  1. Jo am 9. Februar 2007 #

    Du nutzt schon arrays für dein preg_replace()?

  2. Gerrit am 9. Februar 2007 #

    Ja, denn preg_match_all() liefert ja ein Array zurück mit den ganzen Treffern. Allerdings ist es mit dieser einen Abfrage nicht getan, weil ich mich ja wie bei einer Zwiebel durch alle Schichten hindurcharbeiten muss …

  3. Kommandozeilen-Junkie am 9. Februar 2007 #

    Wenn das auf 5.2 Standardinstallationen laufen soll, ohne die php.ini anzurühren, wirst Du wohl nicht drumherum kommen, Deinen Template-Code zu überarbeiten. Deiner Funktionsbeschreibung nach würde ich sowieso darüber nachdenken.

    Die Einstellung bezieht sich übrigens auf ein internes Limit der benutzen PCRE-Bibliothek. Wenn Du da etwas resetten wölltest, müsstest Du das während eines laufenden preg_replace-Aufrufes machen. Und das will ich sehen :o)

  4. Gerrit am 9. Februar 2007 #

    Hmm, ja, ich habe mir schon gedacht, dass es irgendwann knallen wird, aber erstaunlicherweise gab es bisher bei niemandem Performance-Probleme – und das bei mehreren hundert Installationen und knapp 2 Jahren mit unveränderter Parsing-Engine  …

  5. Jo am 9. Februar 2007 #

    Ich weiß ja leider auch nicht genau wie dein Template-Parser aufgebaut ist, aber ich hätte jetzt auch spontan »Umbauen« gesagt.

    Mit Arrays meinte ich jetzt eigentlich was anderes, aber ich denke du hast bei dir durch deine rekursive Schleife schon ganz andere Grundvoraussetzungen  …

    Zur Performance: hast du denn einen Vergleich zu deinen 0.2 Sekunden? ;-)

  6. Lars am 10. Februar 2007 #

    Wo liegt das Problem, die Option zur Laufzeit zu ändern?
    ini_set(‘backtrack_limit’, ‘0’);

    Zur Templateperformance: Meine Engine arbeitet nach dem selben Prinzip und braucht 0.08s :>

  7. Søren am 10. Februar 2007 #

    Falls dich Reguläre Ausdrücke und das Backtracking-Problem interessieren -> http://swtch.com/~rsc/regexp/regexp1.html

    Etwas länglich und theoretisch, aber auf alle Fälle »überfliegenswert«.

  8. Kommandozeilen-Junkie am 10. Februar 2007 #

    Bei dem Limit geht es darum ein mögliches Abschmieren des PHP-Interpreters zu verhindern. Je komplizierter ein regulärer Ausdruck, um so komplizierter ist es vorauszusagen, wie hoch der Aufwand für bestimmte Eingabedaten ist.

    Wenn Du einen Zugriff pro Sekunde hast, und eine Anfrage dauert 0,2 Sekunden, pfff, juckt keinen Menschen, weil der Server genug Luft hat sich zu erholen. Mal angenommen Dein Server skaliert linear – was er wahrscheinlich nicht tut -, dann hast Du bei fünf Zugriffen ein großes Problem, weil er nämlich dann im schlimmsten Fall abschmieren kann. Du kuckst dann in die Protokolldateien und sagst Dir: OK, war viel los, Server hat schlapp gemacht. Aber. Das will kein Mensch. Bei Überlastung soll der Server Anfragen ablehnen, ignorieren oder was auch immer. Aber nicht abschmieren. Und schon gar nicht nachts um eins ;o)

    Mit Performance hat das Ganze also eigentlich nichts zu tun. Es ist eher der Versuch Herr über schwer nachvollziehbare Abstürze zu werden. Versuche mal im nachhinein herauszufinden, ob irgendwo ein Stack übergelaufen ist.

    Zu den 0,2 Sekunden: Auf was bezieht sich das eigentlich und wo werden die verbracht? Wie hast Du die gemessen? Als Reaktionszeit für einen Klick im Browser sind die gut. Wenn das die Zeit auf dem Server ist, die er zum Rechnen braucht, dann eher schlecht.

  9. Jens am 10. Februar 2007 #

    Hast du evtl. schon mal nachgedacht aus den ganzen RegEx-Aufrufen einige zusammen zufassen? Das geht mit recht vielen Ausdrücken. Allerdings gehört da einiges an Erfahrung hinzu.

    Angaben in Absolutwerten sind etwas nutzlos wenn man nicht weiß, auf was sie sich beziehen. 0,2s auf einem 386er oder doch besser dem Numbercruncher aus der letzten Saison?

  10. Gerrit am 10. Februar 2007 #

    Ich bin schon am herumspielen mit DOM in PHP. Sollte besser funktionieren – muss ich aber umdenken an einigen Stellen.

  11. Lars am 10. Februar 2007 #

    DOM ist auch sehr langsam.
    SimpleXML und XMLReader/Writer sind um einiges schneller.

  12. marc am 11. Februar 2007 #

    Und wie wäre ein Umstieg auf Smarty? Dann wäre gleich schon das Caching mit eingebaut.

  13. Markus Tacker am 11. Februar 2007 #

    Wie ist denn die exakte Fehlermeldung?

    pcre.backtrack_limit und pcre.recursion_limit beeinflussen nämlich nur das Verhalten eines einzelnen Ausdruckes, und haben nichts mit einer Schleife danach zu tun.

    Und wie Du der Doku entnehmen kannst, steht das Limit standardmäßig auf 100.000 und nicht auf 128.

  14. Gerrit am 12. Februar 2007 #

    Danke für die Kommentare, das Problem nähert sich seiner Lösung. Ich habe die Parsing Engine umgeschrieben auf DOM-Basis. SimpleXML war einen Hauch zu simpel, obwohl es fast funktioniert hätte. Mit DOM erreiche ich zurzeit etwa die gleiche Geschwindigkeit wie mit RegEx, allerdings kann ich noch ein klein wenig optimieren, bevor ich release.

    Smarty wäre zwar wahrscheinlich die elegenteste Lösung, allerdings müsste ich dazu alles umschmeißen, was die Loudblog-Infrastruktur angeht, was die Anwender nicht besonders lustig fänden – ich auch nicht. Jetzt habe ich eine Lösung, die soweit gut funktioniert und bei der ich nichts ändern musste außer dem Parsing-Core.

Kommentar schreiben

Nutzt Textile zum Strukturieren eures Textes.
SEO-Beiträge werden sofort gelöscht, auch wenn ihr euch Mühe gebt, thematisch treffend zu spammen, ihr Deppen!

Kommentarfeed

Zusatzinfos

Mein Buch unterstützen!

Aktuelle Artikel