Cronjob nur 1x gleichzeitig

YetiF

Feind der Lasagne
ID: 29725
L
9 Mai 2006
270
13
Hallo Leutz,

auf meinem Server läuft ein Cronjob, der alle 30 Minuten Paidmails verschickt (wenn denn welche da sind). Normalerweise dauert der Versand von 5.000 Mails 15-18 Minuten. Dann darf sich der Server ein bißchen erholen und startet dann zur nächsten halben Stunde den nächsten Versand.

Nun habe ich aber neuerdings das Problem, dass die Ausführung des Cronjobs über 30 Minuten dauert. Nicht immer, nur wenn der SQL-Server schwerst am ackern ist, aber leider passiert es doch. Und dann gibt es da die häßliche Situation, dass der erste Cronjob noch aktiv ist, der zweite bereits gestartet wurde und beide zusammen auf dieselbe Datenbank zugreifen. In der Folge werden Mails doppelt verschickt, was nicht sein soll.

Meine Frage ist nun, wie ich verhindern kann, dass zwei Cronjobs (zweimal derselbe Cronjob) gleichzeitig laufen.

Meine erste Idee war, dass ein Cronjob sich beim Start "anmeldet", indem er in der Datenbank einen Wert setzt. Und wenn er fertig ist, dann wird dieser Wert wieder gelöscht. Der nächste Cronjob fragt zunächst ab, ob ein Wert gesetzt ist, wenn ja weiß er "da läuft noch mein Vorgänger, starte ich mal erst gar nicht".

Das System hat nur den Nachteil, dass so ein Cronjob (oder der Server) ja auch mal abstürzen kann. Und wenn das passiert, wenn der Wert gerade gesetzt war, gibt es keine Funktion, die ihn wieder löschen wird. Also müsste man da die nächste Abfrage programmieren "wenn das Setzen des Wertes mehr als x Stunden her ist, dann..."

So könnte man das sicher programmieren und zum Laufen bringen. Aber ich bin mir echt nicht sicher, ob ich das nicht viel zu kompliziert angehe. Ich kann mich noch dunkel an ein paar Semester C++ unter Windows erinnern, wo uns irgendein Dozent was beigebracht hat, dass man definieren kann, dass ein Programm (Prozess) vom System nur einmal ausgeführt werden kann/darf. Läuft der Prozess/Programm (noch), kann es kein weiteres Mal gestartet werden.

Gibt es sowas unter Linux oder in php auch??? - Oder hat jemand noch eine richtig coole Idee für mich???
 
Du könntest den Cronjob doch auch alle x Mails anweisen, einen Timestamp in die DB zu setzen. Der nächste Aufruf des Cronjobs wird nur dann durchgeführt, wenn der Timestamp in der DB y Minuten in der Vergangenheit liegt. Somit kannst Du das Abstürzen des Cronjobs abfangen...
 
gibts nicht die Möglichkeit, sich mittels
ps / pstree / top oder so was
die gerade laufenden Prozesse aufzulisten ?
Dann könnte man doch einfach prüfen, ob der Prozess schon/noch läuft und ihn entsprechend starten oder auch nicht.
 
Du könntest den Cronjob doch auch alle x Mails anweisen, einen Timestamp in die DB zu setzen. Der nächste Aufruf des Cronjobs wird nur dann durchgeführt, wenn der Timestamp in der DB y Minuten in der Vergangenheit liegt. Somit kannst Du das Abstürzen des Cronjobs abfangen...
Ja, ich hatte auch an einen Timestamp gedacht, der in die Datenbank geschrieben wird, und mit der der Cronjob seine noch vorhandene Aktivität anzeigt.

Aber irgendwie kommt mir diese Lösung so notdürftig vor... ich weiß auch nicht.

neija eine möglichkeit wäre nen symbolisches locking gelöst mit flock()
flock geht in die Richtung die ich mir vorstelle. Hat aber letztlich dasselbe Problem, wenn der Cron in der Ausführung abstürzt. Dann wird die Datei nämlich nicht mehr entlockt und startet gar nicht mehr.

Am saubersten wäre es doch wirklich, wenn man dem Betriebssystem sagen könnte: Laß nur zu, dass diese Datei 1x gleichzeitig geöffnet wird; bzw. ein Prozess/eine Instanz läuft, die aus diesem Script erzeugt wurde...

gibts nicht die Möglichkeit, sich mittels
ps / pstree / top oder so was
die gerade laufenden Prozesse aufzulisten ?
Dann könnte man doch einfach prüfen, ob der Prozess schon/noch läuft und ihn entsprechend starten oder auch nicht.
Ja, aber wie frage ich genau diesen Sache in php ab???
 
Zuletzt bearbeitet von einem Moderator:
Sorg doch einfach dafür das das Script nach x Minuten sich beendet. (das ist mit nem $end = time()+60*25; am Anfang und nem if($end < time()) break; in der Hauptschleife getan) Alternativ kann man das ganze auch so gestalten das mehrere Prozesse nebeneinander arbeiten können. Dass ist aber nicht ganz so trivial.
 
Sorg doch einfach dafür das das Script nach x Minuten sich beendet.
Die Lösung gefällt mir, die ist schön simpel. Wenn die Laufzeitdauer des Scriptes 29 Minuten übersteigt, dann beendet sich das Script via die(); knallhart selber. :D
Alternativ kann man das ganze auch so gestalten das mehrere Prozesse nebeneinander arbeiten können. Dass ist aber nicht ganz so trivial.
Mehrere Prozesse nebeneinander arbeiten zu lassen bringt nichts, weil Mails versandt werden, und die Geschwindigkeit des Scriptes einzig dadurch bestimmt wird, wie schnell sendmail auf dem Server die Emails dem Script abnimmt.

Das dennoch so zu ändern, dass mehrere Prozesse gleichzeitig laufen könnten, ohne sich gegenseitig zu behindern... ach... weißt Du... ich bin nicht mehr der Jüngste. In meinem Alter nimmt man besser den Weg des geringeren Widerstandes... (Warum durch die Mauer brechen, wenn die Tür offen steht?) :ugly:

Das Script holt sich eben zum Start 5.000 unversandte Mails und arbeitet die dann in einer Schleife ab. Das würde bedeuten, dann man vor der Ausführung des wirklichen Versandes abfragen müsste, ob nicht irgendein anderer Prozess die Mail nicht zwischenzeitlich schon versandt hat.
 
flock geht in die Richtung die ich mir vorstelle. Hat aber letztlich dasselbe Problem, wenn der Cron in der Ausführung abstürzt. Dann wird die Datei nämlich nicht mehr entlockt und startet gar nicht mehr.
Der lock wird dann auch aufgehoben.

Wenn du nichts an der DB-Struktur ändern willst würde ich dann mit einem pid-File arbeiten.
 
Also, ich glaub, ich kombiniere das:

A) In der DB wird ein timestamp gespeichert, der dafür sorgt, dass immer nur ein Prozess gleichzeititg arbeiten darf.

und

B) Im Script selber sorgt eine Funktion dafür, dass die Ausführung des Scriptes nach 29 Minuten beendet wird.


Danke für Eure Ideen! :D
 
Die Lösung gefällt mir, die ist schön simpel. Wenn die Laufzeitdauer des Scriptes 29 Minuten übersteigt, dann beendet sich das Script via die(); knallhart selber. :D

Ja die Lösung ist schön simple, aber ist nicht unbedingt sicher. Wenn das auf keine Fall passieren darf würd ich das so nicht machen. Sowas nennt man auch pfusch am Bau ;)


Mehrere Prozesse nebeneinander arbeiten zu lassen bringt nichts, weil Mails versandt werden, und die Geschwindigkeit des Scriptes einzig dadurch bestimmt wird, wie schnell sendmail auf dem Server die Emails dem Script abnimmt.

Da wäre ich mir nicht so sicher... aber sendmail ist nicht wirklich mein Gebiet. Prinzipiell gehts aber auch nicht darum das es "schneller" geht, sondern dass es sicher (oder zumindesten sicherer) ist. Ob nun diese Sicherheit bei nem Paidmailer von nöten ist sei mal dahingestellt, aber das wäre eigentlich der richtige Weg.

PS: ich würde aber auch zu Variante 1 greifen, ob da nun mal nen paar Mails doppelt rausgehen ist doch wurscht.
 
...ob da nun mal nen paar Mails doppelt rausgehen ist doch wurscht.
So spricht aber auch nur jemand, bei dem sich täglich keine Dutzende User beschweren, die ein Mail doppelt bekommen haben! Zwei Mails geklickt => aber nur eine ließ sich bestätigen, die andere meldet, dass sie schon bestätigt wurde (weil der Link in beiden Mails identisch ist). Da werden manche Leute schon rabiat wegen...

Ja die Lösung ist schön simple, aber ist nicht unbedingt sicher. Wenn das auf keine Fall passieren darf würd ich das so nicht machen. Sowas nennt man auch pfusch am Bau ;)
Aber wie heißt so schön: Nichts funktioniert so gut und hält so lange wie ein PROVISORIUM! :mrgreen:

Da wäre ich mir nicht so sicher... aber sendmail ist nicht wirklich mein Gebiet.
Das Auslesen der Empfänger und der Mailtexte aus der Datenbank; also das eigentliche Zusammenstellen der Mails dauert Sekundenbruchteile. Gibt man alles per echo auf dem Bildschirm aus ist man in wenigen Sekunden fertig.

Verschickt man hingegen die Sachen per (Einzel-) Mail... dann dauert das. Unser Server (und das ist kein langsamer) braucht 3 Minuten für den Versand von 1.000 Mails. Lässt man zwei Scripte parallel nebeneinander Mails verschicken wird verdoppelt sich die Zeit nahezu (1.000 Mails dann in 5'50" über den Daumen). Startet man den dritten Prozeß zerlegt's alle drei mit Timeouts. - DAS war genau der Grund, warum ich die Cron Geschichte programmiert habe: Alle Versandaufträge in der DB speichern und nach und nach wird das dann fein säuberlich abgearbeitet.
 
So spricht aber auch nur jemand, bei dem sich täglich keine Dutzende User beschweren, die ein Mail doppelt bekommen haben! Zwei Mails geklickt => aber nur eine ließ sich bestätigen, die andere meldet, dass sie schon bestätigt wurde (weil der Link in beiden Mails identisch ist). Da werden manche Leute schon rabiat wegen...

Da hast du recht ;) Ich hab aber auch nix mit Paidmailern am Hut... und erst recht nicht mit Usern :roll:

Aber wie heißt so schön: Nichts funktioniert so gut und hält so lange wie ein PROVISORIUM! :mrgreen:

Ja nix hält solange, ganz einfach weil man zufaul ist es richtig zu machen. Aber spätestens wenn das provisorium kollabiert schaut man dumm in die Röhre. :roll:

Das Auslesen der Empfänger und der Mailtexte aus der Datenbank; also das eigentliche Zusammenstellen der Mails dauert Sekundenbruchteile. Gibt man alles per echo auf dem Bildschirm aus ist man in wenigen Sekunden fertig.Verschickt man hingegen die Sachen per (Einzel-) Mail... dann dauert das. Unser Server (und das ist kein langsamer) braucht 3 Minuten für den Versand von 1.000 Mails. Lässt man zwei Scripte parallel nebeneinander Mails verschicken wird verdoppelt sich die Zeit nahezu (1.000 Mails dann in 5'50" über den Daumen). [...]

Wie gesagt ich kenn mich mit Sendmail nicht wirklich aus... ich hätte aber schon gedacht dass das ding mehrere Prozesse parallel ausfrühen kann. Ich kenn Systeme die pulvern in einer Minute locker 20.000 Mails raus. Ich hab zwar keine ahnung wie das funktioniert, es funktioniert aber.
 
Ich kenn Systeme die pulvern in einer Minute locker 20.000 Mails raus. Ich hab zwar keine ahnung wie das funktioniert, es funktioniert aber.

Du kannst in einem mail-Befehl ja "mehrere" Empfänger angeben. Wenn das mehrere z.B. = 1.000 ist, dann schafft man einen nahezu beliebigen Versand.

Nachteil ist dann ab, dass solche Mails zu 99% als SPAM deklariert werden und alle Mails absolut identisch sind. Bedeutet: Keine individuellen Bestätigungslinks in den Mails und keine variablen Felder, keine Anrede "Hallo [Vorname]..."