@@ -67,13 +67,14 @@ public function schedule(ScheduledJob $job, ScheduledJob ...$jobs): void
6767 public function isScheduled (string $ groupName , string $ identifier ): bool
6868 {
6969 $ this ->validateGroupName ($ groupName );
70+ $ tableName = ScheduledJob::TABLE_NAME ;
7071
71- $ statement = '
72- SELECT 1 FROM ' . ScheduledJob:: TABLE_NAME . '
72+ $ statement = <<<"MySQL"
73+ SELECT 1 FROM { $ tableName }
7374 WHERE identifier = :identifier
74- AND groupname = :groupname
75- AND claimed = ""
76- ' ;
75+ AND groupname = :groupname
76+ AND claimed = ""
77+ MySQL ;
7778
7879 return $ this ->dbal ->fetchOne ($ statement , ['identifier ' => $ identifier , 'groupname ' => $ groupName ]) !== false ;
7980 }
@@ -82,6 +83,7 @@ public function next(string $groupName): ?ScheduledJob
8283 {
8384 $ this ->validateGroupName ($ groupName );
8485 $ claim = Algorithms::generateUUID ();
86+ $ tableName = ScheduledJob::TABLE_NAME ;
8587
8688 $ update =
8789 /**
@@ -97,19 +99,21 @@ public function next(string $groupName): ?ScheduledJob
9799 *
98100 * @lang MySQL
99101 */
100- '
102+ <<<"MySQL"
101103 UPDATE (SELECT identifier
102- FROM ' . ScheduledJob:: TABLE_NAME . '
104+ FROM { $ tableName }
103105 WHERE duedate <= :now
104106 AND groupname = :groupname
105107 AND claimed = ""
108+ AND running = 0
106109 ORDER BY duedate ASC
107110 LIMIT 1) AS delinquents
108- INNER JOIN ' . ScheduledJob:: TABLE_NAME . '
111+ INNER JOIN { $ tableName }
109112 USING (identifier)
110- SET claimed = :claimed
113+ SET claimed = :claimed,
114+ running = 1
111115 WHERE claimed = ""
112- ' ;
116+ MySQL ;
113117 (new Retry ())
114118 /**
115119 * @see http://backoffcalculator.com/?attempts=5&rate=1&interval=0.5
@@ -131,12 +135,12 @@ public function next(string $groupName): ?ScheduledJob
131135 ]
132136 ));
133137
134- $ select = /** @lang MySQL */ '
135- SELECT identifier, duedate, queue, job, incarnation, claimed
136- FROM ' . ScheduledJob:: TABLE_NAME . '
138+ $ select = /** @lang MySQL */ <<<MySQL
139+ SELECT identifier, duedate, queue, job, incarnation, claimed, running
140+ FROM { $ tableName }
137141 WHERE claimed = :claimed
138- AND groupname = :groupname
139- ' ;
142+ AND groupname = :groupname
143+ MySQL ;
140144 $ row = $ this ->dbal
141145 ->executeQuery (
142146 $ select ,
@@ -162,7 +166,8 @@ public function next(string $groupName): ?ScheduledJob
162166 $ groupName ,
163167 (string )$ row ['identifier ' ],
164168 (int )$ row ['incarnation ' ],
165- (string )$ row ['claimed ' ]
169+ (string )$ row ['claimed ' ],
170+ (bool )$ row ['running ' ]
166171 );
167172 }
168173
@@ -171,13 +176,15 @@ public function release(ScheduledJob $job): void
171176 if ($ job ->getClaimed () === '' ) {
172177 throw new InvalidArgumentException ('Cannot release unclaimed jobs ' , 1657027508 );
173178 }
174- $ delete = /** @lang MySQL */ '
175- DELETE FROM ' . ScheduledJob::TABLE_NAME . '
179+ $ tableName = ScheduledJob::TABLE_NAME ;
180+
181+ $ delete = /** @lang MySQL */ <<<"MySQL"
182+ DELETE FROM {$ tableName }
176183 WHERE groupname = :groupname
177- AND identifier = :identifier
178- AND claimed = :claimed
179- ' ;
180- $ this ->dbal
184+ AND identifier = :identifier
185+ AND claimed = :claimed
186+ MySQL ;
187+ $ deleteResult = $ this ->dbal
181188 ->executeQuery (
182189 $ delete ,
183190 [
@@ -186,21 +193,40 @@ public function release(ScheduledJob $job): void
186193 'claimed ' => $ job ->getClaimed (),
187194 ]
188195 );
196+ if ($ deleteResult ->rowCount () === 0 ) {
197+ $ free = /** @lang MySQL */ <<<"MySQL"
198+ UPDATE {$ tableName }
199+ SET running = 0
200+ WHERE groupname = :groupname
201+ AND identifier = :identifier
202+ AND claimed = ""
203+ MySQL ;
204+ $ this ->dbal
205+ ->executeQuery (
206+ $ free ,
207+ [
208+ 'groupname ' => $ job ->getGroupName (),
209+ 'identifier ' => $ job ->getIdentifier (),
210+ ]
211+ );
212+ }
189213 }
190214
191215 public function fail (ScheduledJob $ job , string $ reason ): void
192216 {
193217 if ($ job ->getClaimed () === '' ) {
194218 throw new InvalidArgumentException ('Cannot fail unclaimed jobs ' , 1718808398 );
195219 }
220+ $ tableName = ScheduledJob::TABLE_NAME ;
196221
197- $ update = /** @lang MySQL */ '
198- UPDATE ' . ScheduledJob::TABLE_NAME . '
199- SET claimed = :failed
222+ $ update = /** @lang MySQL */ <<<"MySQL"
223+ UPDATE {$ tableName }
224+ SET claimed = :failed,
225+ running = 0
200226 WHERE identifier = :identifier
201- AND claimed = :claimed
227+ AND claimed = :claimed
202228 LIMIT 1
203- ' ;
229+ MySQL ;
204230 $ this ->dbal
205231 ->executeQuery (
206232 $ update ,
@@ -219,18 +245,30 @@ public function fail(ScheduledJob $job, string $reason): void
219245 protected function scheduleJob (ScheduledJob $ job ): void
220246 {
221247 $ this ->validateGroupName ($ job ->getGroupName ());
248+ $ tableName = ScheduledJob::TABLE_NAME ;
222249
223- $ statement = /** @lang MySQL */ '
224- INSERT INTO ' . ScheduledJob:: TABLE_NAME . '
225- (groupname, identifier, duedate, queue, job, incarnation, claimed)
226- VALUES (:groupname, :identifier, :duedate, :queue, :job, :incarnation, :claimed)
250+ $ statement = /** @lang MySQL */ <<<MySQL
251+ INSERT INTO { $ tableName }
252+ (groupname, identifier, duedate, queue, job, incarnation, claimed, running )
253+ VALUES (:groupname, :identifier, :duedate, :queue, :job, :incarnation, :claimed, :running )
227254 ON DUPLICATE KEY
228- UPDATE duedate = IF(:duedate < duedate, :duedate, duedate),
255+ UPDATE
256+ duedate = CASE
257+ WHEN running = 1
258+ -- If the job is already running, this is "another one",
259+ -- so schedule the next one according to its own date.
260+ THEN :duedate
261+ WHEN duedate < :duedate
262+ -- If this reschedules a waiting job, use the lower value
263+ THEN duedate
264+ ELSE
265+ :duedate
266+ END,
229267 incarnation = :incarnation,
230268 queue = :queue,
231269 job = :job,
232270 claimed = :claimed
233- ' ;
271+ MySQL ;
234272 $ this ->dbal
235273 ->executeQuery (
236274 $ statement ,
@@ -242,6 +280,7 @@ protected function scheduleJob(ScheduledJob $job): void
242280 'job ' => serialize ($ job ->getJob ()),
243281 'incarnation ' => $ job ->getIncarnation (),
244282 'claimed ' => $ job ->getClaimed (),
283+ 'running ' => $ job ->isRunning () ? 1 : 0 ,
245284 ],
246285 [
247286 'groupname ' => Types::STRING ,
@@ -251,6 +290,7 @@ protected function scheduleJob(ScheduledJob $job): void
251290 'job ' => Types::BLOB ,
252291 'incarnation ' => Types::INTEGER ,
253292 'claimed ' => Types::STRING ,
293+ 'running ' => Types::BOOLEAN ,
254294 ]
255295 );
256296 // TODO: Find a way to "trigger queueing" without cronjobs. Maybe "dynamic cronjobs" like "at".
0 commit comments