[Allegro] avanti unter xinetd [Lang]

Thomas Berger ThB at Gymel.com
Do Jul 7 12:16:12 CEST 2011


Lieber Herr Eversberg,

>> Bei modernen Kerneln faellt bereits auf, dass "top" zwar in der
>> Zusammenfassungszeile mehr als 99% "idle" anzeigt, der avanti-
>> Prozess aber 8-10% Verbrauch einer virtuellen CPU hat (und nicht 0.05%).
>> Evtl. sind das natuerlich 10% der extrem heruntergeregelten CPU,
>> alle anderen Dienste ausser Avanti bleiben aber unterhalb der
>> Wahrnehmungsschwelle.
> Womöglich besagen die 10% aber wirklich kaum was, denn das
> Messen von verbrauchten CPU-Leistungen eines Prozesses ist
> ja wohl keine simple Sache. Ich denke, auf die 99% "idle"
> ist dann schon eher Verlaß.

Fuer am belastbarsten halte ich eigentlich die Statistik des
Hypervisors: Der teilt einer VM im Leerlauf x Anteile einer
CPU pro Zeiteinheit zu, und ist Avanti drauf, etwa 10x : Daran
ist nicht zu deuteln.


>> ---
>>
>> Ich wollte daher die aelteren Avanti (die ich eigentlich nie benoetige,
>> nur installiert vorhalte) ueber xinetd aufstarten, d.h. sie sind
>> gar nicht aktiv, weil xinetd das listen() auf dem Socket leistet und
>> die Prozesse nur bei Bedarf startet.
>>
>> Leider ist es da so, dass sich avanti dann ueber die nicht auffindbare
>> Konfigurationsdatei beschwert: Arbeitsverzeichnis ist durch xinetd
>> auf "/" gestellt (avanti macht dann "//" draus, aber egal), vor
>> allem aber kann man sich nicht darauf verlassen, dass argv[0] den
>> Pfad zum Executable enthaelt, das ist eine Verabredung der Shell
>> mit dem Rest der Welt, xinetd startet avanti aber ohne Zuhilfenahme
>> einer Shell (gut). Anscheinend gibt es ueberhaupt keinen portablen
>> Weg, ~den~ Pfad des aktuell laufenden Executables zu bestimmen,
>> es scheint auch nicht unbedingt eine wohldefinierte Fragestellung...
> Das kapiere ich nun gar nicht. Wer denkt sich denn sowas aus,
> ein Prozeß, bei dem man gar nicht weiß, wo er denn läuft.

Naja, bereits Avanti beruecksichtigt ja die Faelle (anhand Betrachtung
von ARGV[0]) "Aufruf mit absolutem Pfad" und "Aufruf mit relativem
Pfad", in letzterem Fall wird das aktuelle Arbeitsverzeichnis ermittelt
und vorangestellt. Und das Arbeitsverzeichnis ist ja die Angabe
"wo er denn laeuft". M.W. hat auch Win32 in StartProc zwei Parameter:
Der erste sagt, von welchem Pfad das Image zu laden ist, der zweite,
was der gestartete Process in argv[0] ueber sich mitgeteilt bekommt.
Unter Linux ist das nicht viel anders, nur ist dort wegen der Traditionen
von Forks und von Soft- und Hardlinks die Vorstellung wenig verbreitet,
dass ein Binary in einem bestimmten Verzeichnis "wohnt" und dass
man das aus der Art des Aufrufs (was ist das eigentlich?) erschliessen
koennte.


> Und ein environment kann man ihm auch nicht mitgeben, mit
> einer geeigneten PATH-Variablen, damit er Programme und
> Dateien findet?

Natuerlich geht das. Und Kommandozeilenschalter auch.



>> Vermutlich ist das auch der Grund fuer gewisse Schwierigkeiten,
>> avanti ueber init-Skripte, die den jeweiligen Unix-Distributionen
>> nachempfunden sind, glatter in Systeme einzupassen: Je elaborierter
>> die Mechanismen,
> Das ist doch das Problem, daß man sich auf "elaborierte
> Mechanismen" überhaupt erst einläßt. Das ufert doch
> zuverlässig aus ("Falle der Komplexität" hat das mal einer
> genannt). Lassen Sie da lieber die Finger von.

Sie meinen, jede Linux-Distribution von juenger als 1995 ist des
Teufels und sollte vermieden werden? Jede Linux-Distribution
hat ihre eigenen Mechanismen fuer die Administration der
"Daemonen", also der in /etc/rc.d hinterlegten, automatisch
zu startenden und zu stoppenden Dienste. Dazu gehoert die Erkennung
von Abhaengigkeiten, aber auch der Test darauf, ob der Dienst
konfiguriert ist, und ob er unter gewissen Benutzer- und
Gruppenkennungen zu laufen hat, ob er noch lebt, dass er moeglichst
nie doppelt laeuft etc. Bei manchen Distributionen gibt es als
Bausteine fuer solche Init-Skripte einfache Shell-Makros oder
-Skripte, unter OpenSuse z.B. ein Binary (start-stop-daemon)
und die zugehoerige Spezifikation (LSB) scheint nicht vorzugeben,
dass einem Daemon vorzugaukeln ist, er sei von einer Shell gestartet
worden...


>> Fuer den Produktivbetrieb der Datenbanken ist die Ansteuerung ueber
>> xinetd gewiss nicht optimal
> Zumal inetd und xinetd nur für relativ selten benötigte
> Prozesse wegen des Overheads wirklich empfohlen werden.

Es geht traditionell ums Abwaegen zwischen Ressourcenverbrauch eines
staendig laufenden Server-Prozesses einerseits und der Leistungs-
einbusse beim fallweisen Aufstarten durch einen Superserver anderer-
seits. ("Selten" ist dabei vermutlich alles mit weniger als 10
Anfragen pro Sekunde, und so wie avanti sich historisch entwickelt
hat gibt es auch im Daemon-Modus die Performance-Verluste, derentwegen
man den Start ueber einen Superserver vermeiden koennen will)

Jedenfalls ist ein Zweit-(Dritt-, ...) Avanti zur vorigen oder
kuenftigen Version eigentlich ein klassischer Kandidat fuer
inetd/xinetd: Man braucht ihn eigentlich nie, aber wenn man ihn
braucht, soll er da sein und nicht erst installiert / umkonfiguiert
/ gestartet werden mit allen Fallstricken, die daran haengen.


> Ich verstehe daher gar nicht, warum Sie diesen Weg eigentlich
> einschlagen wollen, statt avanti in den fraglichen Punkten zu
> modifizieren oder uns Modifikationen vorzuschlagen.

Weil ich *jetzt* einen Weg suche, damit meine leerlaufenden Avantis
nicht in der Summe 30% einer CPU auffressen. Und weil ich alte
Avantis nicht retrospektiv umprogrammieren kann. Und weil Avanti
sich seit 1996 zu genau einem solchen Superserver wie inetd entwickelt
hat, d.h. die Arbeit wird von pro Job separat aufgestarteten acon-
Prozessen uebernommen. Damit ein "allegro-Datenbankserver" wieder
Sinn macht, sind die ausgelagerten Teile wieder einzulagern, das
Verhaeltnis von "acon" (als Funktionale Einheit, nicht als Executable)
zur "avanti.con" ist zu klaeren, das Logging-Konzept von avanti
ist voellig umzukrempeln (etwa so wie beim ztarget), die internen
Datenstroeme sind (wohl nicht zuletzt wegen des Loggings)  und man muss
ueber persistente Verbindungen nachdenken (erfordert wohl starke
Eingriffe in die Klassenbibliothek).

D.h. was benoetigt wird ist ein Server, der mit dem aktuellen
Avanti nur sehr wenig zu tun hat, auch wenn er hinterher (u.a.)
auch so angesprochen werden kann. Modifikation der bestehenden
Codebasis von avanti scheint mir kein Weg zu sein, der dort hin
fuehrt.

Mein aktuelles Problem haengt ebenfalls stark am Design von
avanti: Es werden getrennt select() auf Sockets als auch auf
Pipes durchgefuehrt, damit beides gleichwertig ist, kann dabei
nicht via Betriebssystem gewartet werden. Unter Unix ist
das im Prinzip dasselbe, Unter Windows hingegen muessen die
(anonymen) Pipes einzeln durchgehaspelt werden ("Pipes" in beiden
Welten haben bis auf den Namen wenig gemeinsam?). Der Code
liesse sich hier durch massive Umprogrammierung guenstiger zwischen
den Plattformen auftrennen, aber eigentlich tut sich hier die
Frage auf, ob die gewaehlte Realisierung der Unix-Pipes als
Windows-anonyme-Pipes ein sinnvoller Weg ist, und bevor man da
dann anfaengt, es Windows-maessig "richtig" (mit event-basierter IO
auf Named Pipes) zu machen, kommt wieder die Frage des Redesigns
(weil das Ziel, das mit den Pipes erreicht wird, ein eher
fragwuerdiges zu sein scheint).

Mein bislang erreichtes Verstaendnis der Operationsweise von avanti ist
folgendes:

1. Der Daemon ueberwacht
  a) neue eingehende Verbindungen (klassische Serverfunktion)
     und startet zugehoerige Slaves
  b) die Eingangsdaten etablierter Verbindungen, die per pipe
     an den jeweiligen avanti-Slave umgeschaufelt werden
  c) die Ausgangsdaten etablierter Verbindungen, die von
     slave-Kindern und acon-Enkeln geliefert werden, dabei
     werden "Avanti"-Spezifika in die Logdatei umgebogen, die
     "eigentlichen" Ergebnisse an den externen Client weitergereicht

2. Die Slaves
  a) verwalten einen Pool dieser Verbindung zugeordneter acons
  b) entnehmen diesem Pool pro "Job" einen frischen acon-Prozess
  c) uebernehmen zeichenweise blockierend den Job vom Server
  d) senden den Job gesammelt gebuffert an acon
  e) teilen sich ihr stdout mit den acons, senden selbst aber
     nur "AVANTI:EOR" und "JOB DONE"
     (insbesondere wird die acon-Ausgabe nicht ueberwacht, es
     kann also nicht erkannt werden, ob wg. unvollstaendiger Zeilen
     noch ein CRLF vor das "AVANTI:EOR" zu setzen waere)

3. Die acons
  a) lesen den kompletten Job ueber stdin (zeilenweise blockierend?)
  b) sind aufgrund des Aufrufs mit "@" in der letzten Jobzeile in
     einer Art avanti-Betriebsmodus, der zusaetzlichen Output generiert
  c) lesen daher avanti.conf
  d) Reichern die "normale" Job-Ausgabe um Informationen zur Datenbank,
     gefolgt von einer Leerzeile an

Zwischen 1. und 2. sowie 2. und 3. sind dazu u.U. auch noch Shells involviert,
ob die sich aktiv als weitere Teilnehmer der grossen Datenumschaufelei
beteiligen, weiss ich nicht.

Entstanden ist das im Laufe der Aeonen aus einem klassischen Design
eines forkenden Servers: Der Daemon akzeptiert die Verbindung,
und spaltet einen Slave ab, der die Socket-Deskriptoren, also die
volle Kommunikation zum Client uebernimmt und die Arbeit tut. Dann
gab es wohl Zugriffskonflikte auf die Logdatei, Probleme beim Portieren
auf Windows und beim Zurueckportieren des nicht portierbaren nach
Unix, Memory-Leaks irgendwo, allgemeine Initialisierungsprobleme mit
der Klassenbibliothek, ambitionierte prefork-Ideen, die irgendwie
dann doch nicht klappten und die Loesung war jeweils, unabhaengige
Prozesse zu starten. Weil es da mit der bidirektionalen Kommunikation
bzw. dem Weiterreichen von Verbindungen nicht so einfach ist, wurde
dabei auch auf Shell-Funktionalitaeten wie popen() zurueckgegriffen.
Und so entstand dann langsam der byzantinische Schaufelradbagger,
als der sich avanti heute darstellt: Ein Kontroll-Monster avanti -daemon,
das eingehende und ausgehende Kommunikation mit allen Clients in
einer poll/sleep-Schleife abwickelt, Kind-Prozesse avanti -slave, die
eigentlich nichts tun, ausser Enkel zu starten und mit Input zu
versorgen, und via Shells gestartete acons, die neben der Job-
Verarbeitung noch undokumentierte "avanti"-Funktionsvarianten ausspielen.


viele Gruesse
Thomas Berger




Mehr Informationen über die Mailingliste Allegro