Scriptoptimierung aber wie?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
lunar

sma hat geschrieben:In wie fern ist da der Test, ob ein Worker noch lebt, eine Racing-Condition? Habe ich da einen Denkfehler?

Ich bestimme die Liste der (ID, PID) aus der Datenbank wo PID != NULL. Dies sind die aktiven bzw. hängen gebliebenden Jobs. Nun schaue ich, gruppiert nach PID, ob der Prozess noch lebt. Falls nein, ist das ein hängender Job und ich wiederbelebe alle diese Jobs, indem ich PID auf NULL setze. Was ich bei diesem Durchlauf nicht erwische - etwa weil ein Prozess gerade abnibbelt - das kommt beim nächsten Durchlauf in wenigen Sekunden oder Minuten dran. Was mir nicht passieren darf ist, dass eine PID vom OS wiederverwendet wird, sodass ich einen eigentlich toten Job für lebendig halte.
Das kann aber passieren, da die gesamte Operation nicht atomar ist. Der Kernel kann den Prozess durchaus zwischen der Abfrage der PIDs und der Abfrage der Prozesse schlafen legen. Wenn ein anderer Prozess während der Sleepphase nun einen langlebigen der Prozess erzeugt, für den der Kernel eine PID wiederverwendet, dann ist der mit dieser PID assozierte Job effektiv gesperrt.

Es reicht imho nicht, nur zu prüfen, ob ein Prozess mit dieser PID noch lebt, man muss diesen Prozess auch als Worker identifizieren, z.B. in dem man ihn über ein Socket kontaktiert.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

lunar hat geschrieben:
sma hat geschrieben:Was mir nicht passieren darf ist, dass eine PID vom OS wiederverwendet wird, sodass ich einen eigentlich toten Job für lebendig halte.
Das kann aber passieren, da die gesamte Operation nicht atomar ist.
Diesen Punkt habe ich doch aber die ganze Zeit erwähnt. Ich dachte, du meinst eine weitere prinzipielle Race Condition.

Die Idee, zu prüfen, ob die PID zu einem Worker gehört, ist gut. Wenn man das /proc-Dateisystem hat, ist das auch recht einfach. Dort kann man nachschauen, wie der den Prozess gestarted habende Befehl heißt und einiges mehr.

Am einfachsten wäre aber eine MySQL-Datenbank mit ImmoDB und Transaktionen und ein "SELECT ... FOR UPDATE"-Befehl, der die gefundene Zeile in der DB lockt. Stürzt der Worker dann ab, wird die Transaktion niemals beendet und der Server macht ein Rollback, was die Zeile wieder freigibt.

Stefan
liberavia
User
Beiträge: 19
Registriert: Donnerstag 28. August 2008, 12:27

Wow,

bin echt beeindruckt, wie ihr euch ins Zeug legt. Danke für den "gesäuberten" Code.

Zeigt auf jeden Fall, dass noch viel für mich zu lernen ist :)

Wie muss die Konfigurationsdatei aufgebaut sein? Der Python Interpreter beschwert sich gerade, dass keine Section Header vorhanden sind:

Code: Alles auswählen

Traceback (most recent call last):
  File "/usr/bin/cbjobobserver.py", line 75, in <module>
    main()
  File "/usr/bin/cbjobobserver.py", line 44, in main
    dbhost, dbuser, dbpasswd, dbname = read_config(CONFIG_FILENAME)
  File "/usr/bin/cbjobobserver.py", line 14, in read_config
    config_parser.read(filename)
  File "/usr/lib/python2.5/ConfigParser.py", line 267, in read
    self._read(fp, filename)
  File "/usr/lib/python2.5/ConfigParser.py", line 462, in _read
    raise MissingSectionHeaderError(fpname, lineno, line)
ConfigParser.MissingSectionHeaderError: File contains no section headers.
file: /etc/cbwebgui/cbconnect.conf, line: 1
'dbhost=localhost\n'
P.S. Das ist die Version von jens
Die Entscheidung zwischen OpenSource- und proprietärer Software ist die Entscheidung zwischen Evolution und Marketing.
Welches dieser Prinzipien ist Deiner Meinung nach bewährter und nachhaltiger?
lunar

sma hat geschrieben:
lunar hat geschrieben:
sma hat geschrieben:Was mir nicht passieren darf ist, dass eine PID vom OS wiederverwendet wird, sodass ich einen eigentlich toten Job für lebendig halte.
Das kann aber passieren, da die gesamte Operation nicht atomar ist.
Diesen Punkt habe ich doch aber die ganze Zeit erwähnt. Ich dachte, du meinst eine weitere prinzipielle Race Condition.
Naja, in deinem ersten Posting mit diesem Vorschlag hast du noch gesagt, dass stat() auf /proc/%d reichen sollte, da die PIDs deines Wissens nach nicht so schnell neu vergeben werden würden. Das ist halt imho nicht unbedingt der Fall.
Die Idee, zu prüfen, ob die PID zu einem Worker gehört, ist gut. Wenn man das /proc-Dateisystem hat, ist das auch recht einfach. Dort kann man nachschauen, wie der den Prozess gestarted habende Befehl heißt und einiges mehr.
Alternativ könnte man auch eine Art von IPC umsetzen (z.B. über DBus oder Sockets), bei der sich verschiedene Worker die Jobs, die sie bearbeiten, gegenseitig mitteilen. Das ist zwar komplexer, kommt dafür ohne Änderungen an der Datenbank aus. Dafür dürfte eine transaktionsgestütze Datenbanklösung wohl am einfachsten zu implementieren sein.
BlackJack

@liberavia: Der ConfigParser besteht auf "Sections" in der Ini-Datei:

Code: Alles auswählen

[db]
dbhost=localhost
dbname=foobar
usw.
liberavia
User
Beiträge: 19
Registriert: Donnerstag 28. August 2008, 12:27

Habs grad entdeckt:
The configuration file consists of sections, led by a "[section]" header and followed by "name: value" entries
Die Entscheidung zwischen OpenSource- und proprietärer Software ist die Entscheidung zwischen Evolution und Marketing.
Welches dieser Prinzipien ist Deiner Meinung nach bewährter und nachhaltiger?
liberavia
User
Beiträge: 19
Registriert: Donnerstag 28. August 2008, 12:27

Nachdem ich eine kleinigkeit abgeändert habe, schnurrt das Script jetzt. In top taucht es nur gelegentlich mit 1,7% CPU Auslastung auf.

Ändern musste ich folgendes in main:

Code: Alles auswählen

    dispatch = {'restart': job_restart,
                'reload': job_reload,
                'write': partial(job_write, (dbuser, dbpasswd, dbname))}
Habt vielen Dank! Tolles Forum!

André
Die Entscheidung zwischen OpenSource- und proprietärer Software ist die Entscheidung zwischen Evolution und Marketing.
Welches dieser Prinzipien ist Deiner Meinung nach bewährter und nachhaltiger?
liberavia
User
Beiträge: 19
Registriert: Donnerstag 28. August 2008, 12:27

Hallo

leider ich nochmal :roll:

Wie gesagt: Das Programm funktioniert wunderbar und wie es soll, d. h. solange ich es im vordergrund laufen lasse.

rufe ich es so auf, wie das später auch über init geschehen soll, also

Code: Alles auswählen

cbjobobserver.py &
läuft zwar ein Prozess, aber der macht irgendwie nix :?:
Hier nochmal zum nachvollziehen:

Code: Alles auswählen

root@ubuntu-server:~# cbjobobserver.py &
[1] 8482
root@ubuntu-server:~# [Hier habe ich Enter gedrückt]

[1]+  Stopped                 cbjobobserver.py
root@ubuntu-server:~# cbjobobserver.py &
[2] 8504
root@ubuntu-server:~# ps aux | grep cbjobobserver.py
root      8482  0.0  1.7  15960  4384 pts/2    T    10:32   0:00 python /usr/bin/cbjobobserver.py
root      8504  0.4  1.7  15960  4372 pts/2    T    10:42   0:00 python /usr/bin/cbjobobserver.py
root      8510  0.0  0.2   3004   760 pts/2    R+   10:42   0:00 grep cbjobobserver.py

[2]+  Stopped                 cbjobobserver.py
root@ubuntu-server:~# kill 8504
root@ubuntu-server:~# 
root@ubuntu-server:~# kill 8482
root@ubuntu-server:~# ps aux | grep cbjobobserver.py
root      8482  0.0  1.7  15960  4384 pts/2    T    10:32   0:00 python /usr/bin/cbjobobserver.py
root      8504  0.0  1.7  15960  4372 pts/2    T    10:42   0:00 python /usr/bin/cbjobobserver.py
root      8517  0.0  0.2   3004   760 pts/2    R+   10:45   0:00 grep cbjobobserver.py
root@ubuntu-server:~# kill -9 8504
[2]+  Killed                  cbjobobserver.py
root@ubuntu-server:~# kill -9 8482
[1]+  Killed                  cbjobobserver.py
root@ubuntu-server:~# cbjobobserver.py &
[1] 8519
root@ubuntu-server:~# 
Es ist sowieso seltsam, dass das Programm Ausgaben erzeugt. Lasse ich es im Vordergrund laufen, läuft das Programm zuverlässig aber jede Aktion (Aufruf eines Bash-Scriptes) führt zu einer unsichtbaren Ausgabe (zu verfolgen an der Cursorbewegung). Habe auch schon versucht mittels

Code: Alles auswählen

stty -echo
die Ausgabe in meinen Scripten abzuschalten, leider ohne Erfolg.

Weiß jemand, wie ich das so in den Griff bekommen kann, dass das Programm auch im Hintergrund läuft?[/code]

Vielen Dank

André
Die Entscheidung zwischen OpenSource- und proprietärer Software ist die Entscheidung zwischen Evolution und Marketing.
Welches dieser Prinzipien ist Deiner Meinung nach bewährter und nachhaltiger?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Lenke die Ausgabe (stdout und stderr) deiner Skript nach /dev/null um, oder, was natürlich sinnvoller ist, in eine Datei, die du als Log verwenden kannst.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten