⇫Inhalt
- Ideen und Probleme zu mod_perl
- Threading Branch fertigstellen
- Coredumps mit Worker-MPM
$r->child_terminaterichtig implementieren- Verhalten beim Sterben eines Apache-Worker-Prozesses
- Handler Leak mit threaded Perl aber unabhängig vom MPM
- Perl Interpreter vor dem
forkerzeugen - Marc Lehmanns Coro-Ideen in modperl einbauen
- 64MByte Upload Limit in libapreq
- mod_perl closes apache's stdin and/or stdout
- modperl leaks memory while $r->rflush
Ideen und Probleme zu mod_perl
Ich möchte hier ein paar Ideen und Probleme mit dem aktuellen mod_perl aufzeigen. Falls jemand Interesse hat, einen Punkt davon zu implementieren oder daran mitzuarbeiten, melde er sich bitte.
Threading Branch fertigstellen
Der Threading Branch muß endlich fertig gemacht werden. Folgende Dinge fehlen noch:
- Anpassung an non-threaded Perl
- Anpassung und Tests unter Windows
Dafür könnte ich wirklich Mitarbeit gebrauchen von jemandem, der von Windows Ahnung hat.
Was ist der Threading-Branch eigentlich? Mod_perl verwaltet einen Perl-Interpreter-Pool, wenn es mit einem multithreaded MPM
benutzt wird. Wenn während der Bearbeitung einer Anfrage ein Perl-Interpreter benötigt wird, wird dieser allokiert und später wieder
freigegeben. Die aktuelle Logik kennt ein Flag MpInterp_f_PUTBACK, das besagt, daß der Interpreter an den Pool
zurück gegeben werden muß. Es enthält aber keine Aussage darüber, wann das zu passieren hat. Nun gibt es Stellen, in denen
Funktionen, die einen Perl-Interpreter brauchen rekursiv aufgerufen werden. Hier wird die Benutzung eines Flags problematisch,
denn man kann am Ende einer solchen Funktion nicht ohne Untersuchung des aktuellen Call-Stacks oder ähnliche Maßnahmen
nicht entscheiden, ob jetzt der richtige Zeitpunkt ist, den Interpreter zurück zu geben. Außerdem erfordert diese Logik ziemliche Hacks,
wenn man z.B. Pool-Cleanups benutzt. Dann muß nämlich sichergestellt werden, daß der Interpreter bis zum Cleanup der selbe
bleibt.
Im Threading Branch wird das PUTBACK Flag durch einen Referenz-Count abgelöst. So können viele Probleme
auf einfache Weise gelöst und sogar der eine oder andere Bug behoben werden, der schon länger im Code ist und auf den ersten
Blick nichts mit dem Interpreter-Pool Management zu tun hat.
Coredumps mit Worker-MPM
Mit Perl 5.10 wird die Test-Suite zwar absolviert; es entstehen aber öfters Coredumps. Folgende Dinge sind zu tun:
- Finden welcher Test dafür verantwortlich ist
- Problem fixen
$r->child_terminate richtig implementieren
Im Moment ruft die child_terminate einfach nur exit() auf C-Level auf. Sie sollte jedoch den Perl-Interpreter richtig
beenden und Destruktoren globaler Objekte, END-Blöcke und solchen Kram aufrufen
Verhalten beim Sterben eines Apache-Worker-Prozesses
Wenn ich in Perl folgenden Code ausführe, wird der Destruktor des Objekts sowohl im Vater als auch im Sohn aufgerufen.
{
package My::Object;
sub new {
return bless {};
}
sub DESTROY {
warn "$$: Destroying $_[0]";
}
}
my $obj=My::Object->new;
fork;
Ich erhalte:
5797: Destroying My::Object=HASH(0x796cd8) at - line 8.
5798: Destroying My::Object=HASH(0x796cd8) at - line 8.
Macht man etwas sehr ähnliches in mod_perl, nämlich ein Objekt vor der Erzeugung der Worker-Prozesse erzeugen, wird sein Destruktor nur im Vater-Apache aufgerufen.
Handler Leak mit threaded Perl aber unabhängig vom MPM
MP2 erzeugt jedes Mal, wenn man eine anonyme Funktion als Handler einträgt einen Eintrag in einem globalen Hash, der einen Zeiger auf die Funktion enthält. Der Hash wird erst geleert, wenn der Worker-Prozess sich beendet. Damit wird die Funktion niemals entsorgt. Ist es eine Closure, werden auch die referenzierten Objekte niemals entsorgt. Folgender harmlos aussehender Code ist also schädlich:
in .htaccess:
PerlResponseHandler sub {...}
in irgendeinem Handler:
$r->push_handlers( PerlResponseHandler => sub {...} );
Ich denke, das Verhalten von modperl ist OK, wenn die Handler während des Starts des Apaches erzeugt werden. Zur Laufzeit sollten dynamische Handler den Interpreter an den Request binden, so wie eine Pool-Cleanup Funktion das macht, und keine zusätzliche Referenz auf die Funktion erzeugen. Alternativ könnte man natürlich einen Cleanup Handler benutzen, um den dynamischen Handler wieder aus dem Hash herauszukratzen. Das gefällt mir aber wesentlich weniger, weil es mit einem threaded MPM und mehreren Perl-Interpretern in einem Prozess zu Problemen führt.
Perl Interpreter vor dem fork erzeugen
Die Interpreter-Pool Implementierung in MP2 mit einem threaded MPM ist sehr speicherhungrig. Dabei werden während
des Starts im Vater einer oder mehrere Vater-Interpreter erzeugt. Nach dem fork wird dann in jedem Worker-Prozess
die konfigurierte Anzahl Perl-Interpreter von diesen Vätern geklont. Außerdem kann modperl eine dynamische Anzahl Interpreter
pro Prozess verwalten, ähnlich wie der Apache im Prefork-Modus die Anzahl der Worker-Prozesse an die aktuelle Last anpassen kann.
Leider ist das mit geklonten Perl-Interpretern zur Laufzeit keine gute Idee, denn beim Klonen wird sehr viel Speicher benötigt. Außerdem fällt der Copy-On-Write Effekt weg, den das Betriebssystem liefert, was zu noch mehr Speicherbedarf führt. Das ist der Grund, weshalb ich jedem abrate, ein threaded MPM mit dem aktuellen modperl zu benutzen.
Folgende Dinge sind also zu tun
- die Direktiven
PerlInterpStart,PerlInterpMax,PerlInterpMinSpare,PerlInterpMaxSparesind alle durch eine einzige zu ersetzen, die angibt, wie viele Perl-Interpreter pro Prozess zur Verfügung stehen sollen. PerlInterpMaxRequestsmuss komplett entfernt werden.- die konfigurierte Anzahl muß vor dem
forkgestartet werden. So kann der Copy-On-Write Effekt genutzt werden.
Damit wäre es, denke ich, möglich modperl mit einem threaded MPM sinnvoll einzusetzen, besonders im Frontend in einem 2-Tier Setup. Man könnte so nämlich Konfigurationsaufgaben zur Laufzeit und gelegentliche Output-Filter oder Response-Handler in Perl erledigen und den Perl-Interpreter zwischenzeitlich immer wieder abgeben. Insbesondere wenn der Apache-Thread im Keepalive-Timeout gefangen ist, braucht man keinen Perl-Interpreter.
Marc Lehmanns Coro-Ideen in modperl einbauen
Das ist eine Idee zum selben multithread-Problem. Mark Lehmann hat in seinem Coro-Modul die Möglichkeit geschaffen, den aktuellen Aufruf-Zustand (die Call-Hierarchie) zu speichern und später wieder herzustellen. Benutzt man die selbe Idee für einen threaded Apache, kommt man mit einem Perl-Interpreter für eine größere Anzahl Threads aus, denke ich. Die Threads könnten sogar völlig schmerzfrei größere Datenmengen teilen. Natürlich werden so auch etwas größere Anforderungen an die Sorgfalt der Benutzer/Entwickler gestellt.
Die größte Arbeit hierbei besteht wahrscheinlich in der Identifizierung der blockierenden Aufrufe der Apache-Umgebung. Für all diese müsste ein Hook eingebaut werden, der den Perl- und C-Call-Stack sichert, den Interpreter freigibt, die Funktion aufruft, den Interpreter wieder allokiert, den Stack wieder herstellt und das Resultat der Funktion übergibt.
64MByte Upload Limit in libapreq
Libapreq enthält einen blöden Bug (meiner Meinung nach), der sich wahrscheinlich mit einem Patch von 2 Zeilen lösen
läßt. Man kann mit der Direktive APREQ2_ReadLimit das Upload-Limit zwar verkleinern, aber nicht vergrößern. Das
Problem selbst ist in 2 Zeilen Code gefixt. Aber es sind Tests zu schreiben und das Ganze muß in den offiziellen Code kommen. Das
gehört überwacht.
mod_perl closes apache's stdin and/or stdout
Dieser Thread aus der modperl Users Mailing Liste beschreibt das Problem.
Ich denke, ich habe das gelöst.
modperl leaks memory while $r->rflush
Dieser Thread aus der modperl Users
Mailing Liste beschreibt das Problem. modperl_filter.c:modperl_wbucket_pass() ist der
Einstiegspunkt für die Suche.
Letzte Aktualisierung: 02.04.2010

