Suppose I have multiple workers that can read and write to a MySQL table at the same time (e.g. jobs
). The task of each worker is:
Queued
Job RUNNING
Please note that when the worker runs step #1, there may not be any eligible jobs (i.e. QUEUED
).
I have the following pseudocode so far. I believe if step #1 does not return the job, I need to cancel the (ROLLBACK
) transaction. How would I do this in the code below?
BEGIN TRANSACTION; # Update the status of jobs fetched by this query: SELECT id from jobs WHERE status = "QUEUED" ORDER BY created_at ASC LIMIT 1; # Do the actual update, otherwise abort (i.e. ROLLBACK?) UPDATE jobs SET status="RUNNING" # HERE: Not sure how to make this conditional on the previous ID # WHERE id = <ID from the previous SELECT> COMMIT;
It's not quite clear what you want. But let's say your task is: find the next
QUEUED
job. Set its status toRUNNING
and select the appropriate ID.In a single-threaded environment, you can just use your code. Extract the selected ID into a variable in the application code and pass it to the UPDATE query in the WHERE clause. You don't even need a transaction as there is only one write statement. You can imitate this in SQLscript.
Assume this is your current status:
You want to start the next queued job (id=2).
You will get
Start from last selection. The table will have the following status:
View on DB Fiddle
If you have multiple processes launching jobs, you will need to lock the row using
FOR UPDATE
. But you can useLAST_INSERT_ID()
to avoid this situation:Starting from the above status, job 2 is already running:
You will get:
The new status is:
View on DB Fiddle
If the UPDATE statement did not affect any rows (no queued rows),
ROW_COUNT()
will be0
.There may be some risks that I'm not aware of - but that's not really how I approach it either. I would rather store more information in the
jobs
table. Simple example:and
The job that is running now belongs to a specific process, you can select it simply using
You might even want to know more - eg.
queued_at
,started_at
,finished_at
.This week I'm implementing something very similar to your case. Multiple workers, each grabbing the "next row" in a set of rows to work on.
The pseudo code is like this:
Using
FOR UPDATE
is important to avoid race conditions (i.e. multiple workers trying to get the same row).See https://dev.mysql.com/ doc/refman/8.0/en/select-into.html for information about
SELECT ... INTO
.