Parallele Ausführung verhindern

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Parallele Ausführung verhindern

Beitrag von scientific » 18.07.2017 15:24:12

Hi Leute!

Ich widme mich gerade wieder meinem Backup-Skript. Dabei gehts um ein Locking-Problem.

Ich schildere es mal folgendermaßen:
Das Skript wird über systemd-timer stündlich, täglich, wöchentlich, vor jedem Upgrade und bei jedem Einstecken der externen Platte gestartet. Das funktioniert prinzipiell auch sehr gut.

Das Problem dabei ist, wenn ich länger die externe Platte nicht angesteckt habe, sammeln sich z.T. größere Datenmengen an, welche die Übertragung der Daten auf die externe Platte sehr verzögern. Dann kann es vorkommen, dass nach dem Einschalten des Rechners, der "Afterboot"-Run des Backup-Skriptes gestartet wird. Gleich drauf wird der tägliche und wöchentliche Run auch gestartet. Das heißt, das BTRFS-Dateisystem muss gleichzeitig 3 differentielle Snapshots erstellen und dann über USB an die Platte schicken. Bei einem Snapshot geht das in der Regel ratz-fatz. Aber bei zwei oder drei gleichzeitigen Läufen verlängert sich die Erstellung und auch die Übertragung um den Faktor 8-10. Außerdem wird durch das intensive I/O der Rechner trotz ionice und nice sehr schwerfällig zu bedienen.

Ich starte das ganze über sogenannte instanziierende systemd-Units. Also das Unit-File hat den Namen "mkbackup@.service". Gestartet werden dann die einzelnen Runs mit
systemctl mkbackup@hourly.service, mkbackup@daily.service... Dabei wird der Teil zwischen "@" und ".service" dem Python-Skript, welches die eigentliche Arbeit erledigt als Parameter übergeben, welches dann einen Snapshot anlegt, wo dieser Teil im Namen vorkommt.

Das Skript ist so ausgelegt, dass aus einer Konfiguration für bestimmte Intervall-Namen hinterlegte Optionen ausgelesen und verarbeitet werden. Für alle anderen, also nicht standardmäßig vorgesehenen Interval-Runs kommen Default-Optionen zum Einsatz. Das erlaubt es mir, mit

Code: Alles auswählen

systemctl start mkbackup@spezial.service
einen nicht vorgesehenes Backup mit dem Namen @debian-$DATUM.spezial zu erzeugen und zu übertragen. Das mache ich dann, wenn ich z.B. ein Backup benötige, bevor ich ganz bewusst eine Veränderung am Rechner vornehme, damit ich hinter diese Änderung wieder einfach zurückrollen kann, ohne dass mir das im üblichen Cleanup-Prozess gelöscht würde.
Ich kann auch

Code: Alles auswählen

systemctl start mkbackup@blablubb.service
oder

Code: Alles auswählen

systemctl start mkbackup@foo.service
starten und es werden die entsprechend benannten Backups/Snapshots erzeugt.

Diese flexibilität hat den Vorteil, dass ich wenig hardcoden muss, aber auch den Nachteil, dass ich in die systemd-Unit kein Before oder After einbauen kann, um die Units nacheinander zu starten.

Ich hab auch schon probiert, dass ich mit

Code: Alles auswählen

ExecStartPre=/bin/sh -c /bin/touch /tmp/mkbackup.lock
einfach oder ausgefeilter ein Locking herbeiführe, welches eine Unit failen lässt, sobald ein solches Lockfile vorhanden ist. Werden die Units durch systemd gleichzeitig gestartet, werden dennoch beide gleichzeitig ausgeführt, und das Lockfile zweimal berührt. Die Abfrage nach dem Lockfile

Code: Alles auswählen

ConditionPathExists=/tmp/mkbackup.lock
findet bei beiden Units zu einer Zeit statt, wo die andere dieses Lockfile noch nicht erzeugen konnte.

Also geht diese Methode auch nicht.

Anscheinend muss ich das im Python-Skript selber abfangen und die Ausführung solange verzögern, solange noch eine andere Instanz dieses Skripts läuft.

Aber da bin ich zuwenig in Python drin. Gibt es eine Möglichkeit des Threadings dass das Skript mit anderen Instanzen seiner selbst "sprechen" kann? Oder soll ich am beginn des Skrips eine Schleife einbauen, die im Sekundentakt abfrägt, ob noch ein Lockfile vorhanden ist, und erst wenn nicht, mit der Ausführung weitermachen? Was mache ich, wenn dann wiederum zufällig zur selben Zeit zwei weitere gestartete Instanzen feststellen, dass kein Lockfile vorhanden ist und weitermachen? Dann laufen wieder zwei Instanzen gleichzeitig... Genau das, was ich ja verhindern möchte...

Oder gibt es eine Möglichkeit über den Python-Interpreter selber abzufragen, ob noch weitere Instanzen ein und des selben Skripts laufen, und welchen Status die gerade haben?

ODER muss ich gar eine Daemon-Prozess bauen, der an einem Socket oder eine Pipe dauerhaft hört, und systemd schickt die Parameter an diesen Daemon, und der startet erst einen Intervallrun nach dem anderen? Das wäre wohl die sicherste Methode, aber geht wieder an der Prozessüberwachung von systemd vorbei...

Ich hoffe, mir kann geholfen werden :)

lg scientific
dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

Benutzeravatar
Wolf2000
Beiträge: 52
Registriert: 08.07.2017 08:19:58
Lizenz eigener Beiträge: MIT Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von Wolf2000 » 18.07.2017 16:08:36

Kannst du nicht in seinem Skript slepp einbauen von einen zum anderen Befehl

Gesendet von meinem SM-G935F mit Tapatalk


scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von scientific » 18.07.2017 16:38:34

Wie meinst du das?

Das Skript wird ja mehrfach von systemd über die Instanzen mit unterschiedlichen Optionen aufgerufen. Und es muss sichergestellt werden, dasd immer nur eine Instanz zur selben Zeit läuft.

So eine Art multiprocessing ohne Master...

Lg scientific
dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

Benutzeravatar
MSfree
Beiträge: 11605
Registriert: 25.09.2007 19:59:30

Re: Parallele Ausführung verhindern

Beitrag von MSfree » 18.07.2017 17:16:58

Warum machst du das mit systemd statt cron?

Ansonsten könntest du hier mal ein wenig lesen, der Absatz über flock könnte das richtige für dich sein:
http://bencane.com/2015/09/22/preventin ... xecutions/

Der dort beschriebene Mechanismus sollte sich auch in einer systemd Unit realisieren lassen. Der Trick ist, daß sich flock einfach mit einem Fehlerzustand beendet statt den angegebenen Befehl auszuführen.

scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von scientific » 18.07.2017 19:56:22

Ich arbeite mit systemd-timern, da diese über kurz oder lang cron ablösen. (btw, bei mir ist systemd-cron installiert, den klassischen cron gibts nicht mehr auf meinem system...)

Prinzipiell wäre es ja sogar fein, wenn sowieso alle Snapshots erzeugt würden. Also parallel gestartete dann seriell abgearbeitet würden. Denn gerade die "plugin"-Snapshots stellen sicher, dass immer gleiche verfügbare auf externer und interner Platte vorhanden sind, was differentielle snapshots sicher ermöglicht, auch wenn ich die externe Platre länger nicht angesteckt hatte.

Mit durch locking gekillte Dnapshot-Läufe könnten im schlimmsten Fall dazu führen, dass keine gleichen Sanps mehr extern und intern vorhanden sind, und die gesamte Datenmenge erneut übertragen werden muss... Insbesondere bei kürzerer Vorhaltezeit.

dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von scientific » 18.07.2017 19:59:11

Aber es gibt doch bei python einen lock-mechanismus für multithreading-skripte. Kann man den nich dafür auch gebrauchen?
dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

Benutzeravatar
MSfree
Beiträge: 11605
Registriert: 25.09.2007 19:59:30

Re: Parallele Ausführung verhindern

Beitrag von MSfree » 18.07.2017 20:20:33

scientific hat geschrieben: ↑ zum Beitrag ↑
18.07.2017 19:56:22
Prinzipiell wäre es ja sogar fein, wenn sowieso alle Snapshots erzeugt würden. Also parallel gestartete dann seriell abgearbeitet würden.
Naja, das geht via cron mit dem Befehl batch. Solange du garantieren kannst, daß nur hin und wieder mal ein Snapshot etwas länger dauert, ist das sicher ein Möglichkeit. Ich warne aber, denn wenn regelmässig die Backupzeit überschritten wird, akkumuliert sich das serielle Abarbeiten über Wochen und Monate und die Kiste wird nie fertig, weil ständig frische Jobs aufgereiht werden.

Vielleicht findest du ja auch mit den Stichworten sysmtend und batch replacement etwas in einer Suchmaschine deiner Wahl.
Mit durch locking gekillte Dnapshot-Läufe könnten im schlimmsten Fall dazu führen, dass keine gleichen Sanps mehr extern und intern vorhanden sind
Wenn zwischendurch mal eine Stundenbackup ausfällt, übernimmt doch der nächste Stundenbackup. Ich sehe da keine Gefahr, kenne aber dein Setup auch nicht im Detail :wink:

scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von scientific » 18.07.2017 22:17:16

Wenn das Stundenbackup ausfällt ists egal. Wenn aber mehrmals das pluginbackup ausfällt, wirds blöd.
dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

r4pt0r
Beiträge: 1237
Registriert: 30.04.2007 13:32:44
Lizenz eigener Beiträge: MIT Lizenz

Re: Parallele Ausführung verhindern

Beitrag von r4pt0r » 19.07.2017 09:00:51

One-liner für crontab und die udev-rule für die platte:

Code: Alles auswählen

[ `ps ax | grep <scriptname> | wc -l` -gt 1 ] && /run/my/backup/script.sh !! echo "Backup already running" 
Wieso man dazu systemd braucht ist mir völlig fremd...

scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von scientific » 19.07.2017 10:07:04

Du kennst die Verquickung von udev und systemd?
Und die Flexibilität beim Skriptaufruf in Abhängigkeit vom Vorhandensein von der Platte (auch mit Entschlüsseln vorher, wenn gewünscht...)

Ich mag systemd. Es bietet mir viel Komfort.
dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Parallele Ausführung verhindern

Beitrag von heisenberg » 19.07.2017 10:14:04

r4pt0r hat geschrieben: ↑ zum Beitrag ↑
19.07.2017 09:00:51

Code: Alles auswählen

[ `ps ax | grep <scriptname> | wc -l` -gt 1 ] && /run/my/backup/script.sh || echo "Backup already running" 
Das ist ein möglicher Ansatz. Aber da ist noch ein Fehler drin(Der grep bringt sich selbst als Ergebnis, oder auch ein vi <scriptname>). Vielleicht stattdessen lieber pgrep verwenden. Ansonsten habe ich mal !! durch || ersetzt. Ich nehme an, das ist ein Schreibfehler.


flock - wie von MSfree vorgeschlagen - finde auch ich hier viel robuster. Hier nochmal ein Link zu einer anderen Doku:

http://www.kfirlavi.com/blog/2012/11/06 ... h-program/
Zuletzt geändert von heisenberg am 19.07.2017 10:21:34, insgesamt 1-mal geändert.

r4pt0r
Beiträge: 1237
Registriert: 30.04.2007 13:32:44
Lizenz eigener Beiträge: MIT Lizenz

Re: Parallele Ausführung verhindern

Beitrag von r4pt0r » 19.07.2017 10:20:34

heisenberg hat geschrieben: ↑ zum Beitrag ↑
19.07.2017 10:14:04


Aber da ist noch ein Fehler drin(Der grep bringt sich selbst als Ergebnis, oder auch ein vi <scriptname>).
deshalb "-gt 1", damit deckt man das eigene grep ab :wink:
idealerweise sucht man nach dem kompletten string "/absoluter/pfad/zum/script" und ruft es auch in cron so auf (was sowieso grundsätzlich eine gute idee ist), ggf noch mit argumenten - damit schließt man falsche matches wie z.b. durch vim ziemlich gut aus. statt grep kann man mit awk auch noch besser verfeinert suchen.
Auf jeden Fall ist das noch immer weitaus eleganter als der wust mit systemd bei dem man sich auf völlig intransparente mechanismen verlassen muss die unmöglich zu debuggen sind. Gerade bei Backups sollte man sich nicht auf sowas überkompliziertes und hoffnungslos unzuverlässiges wie systemd verlassen müssen :roll:

TomL

Re: Parallele Ausführung verhindern

Beitrag von TomL » 19.07.2017 11:39:18

r4pt0r hat geschrieben: ↑ zum Beitrag ↑
19.07.2017 10:20:34
Gerade bei Backups sollte man sich nicht auf sowas überkompliziertes und hoffnungslos unzuverlässiges wie systemd verlassen müssen
:THX:

scientific
Beiträge: 3022
Registriert: 03.11.2009 13:45:23
Lizenz eigener Beiträge: Artistic Lizenz
Kontaktdaten:

Re: Parallele Ausführung verhindern

Beitrag von scientific » 19.07.2017 12:03:53

Also ich kann systemd gut debuggen. Hat ein wenig Einarbeitung erfordert... Aber das klappt richtig gut.

Und für mich ist das nicht intransparent. Aber egal. Ich werd mir die Geschichte von flock näher anschauen, oder in python noch etwas bauen. Mir schwebt schon etwas vor.

Mit deinen Aussagen über systemd hast du dich leider in meinen Augen disqualifiziert, Raptor.

dann putze ich hier mal nur...

Eine Auswahl meiner Skripte und systemd-units.
https://github.com/xundeenergie

auch als Debian-Repo für Testing einbindbar:
deb http://debian.xundeenergie.at/xundeenergie testing main

breakthewall
Beiträge: 507
Registriert: 30.12.2016 23:48:51

Re: Parallele Ausführung verhindern

Beitrag von breakthewall » 19.07.2017 18:18:02

Klingt nach einem falscher Ansatz. Nicht Systemd muss hier verändert werden, noch ist das die Aufgabe von Systemd dies zu managen. Hier muss definitiv die Software die gestartet wird selbst kontrollieren ob sie bereits läuft, sprich dein Python-Script. Und wie bereits angemerkt wurde, ist ein Locking auch in Python möglich, aber wenn dann ohne Dateien, was bei unvorhergesehenen Ereignissen ungünstig wird.

In der regulären Shell könnte das bspw. so aussehen, mit File-Descriptor basierendem Lock:

Code: Alles auswählen

exec 200<"$0"
flock -n -x 200 || exit 2
In Python gibt es zumindest mehrere Arten das zutun, was aber auch wieder von deinem Python-Script abhängt was hier sinnvoll wäre. Die Frage die sich mir primär stellt ist, warum man Python nutzt für so eine sensible Aufgabe, wenn man Python nicht gänzlich beherrscht? Für ein Backup ist die Shell inkl. aller Programme dazu, wie geschaffen. Würde ein Backup nicht komplizierter gestalten als nötig, gerade wenn es für einen selbst beherrschbar sein muss.

Antworten