SELECT FOR UPDATE necessary in CTE for UPDATE?
up vote
1
down vote
favorite
In PostgreSQL 9.6, is the below FOR UPDATE
clause in this CTE necessary?
CREATE OR REPLACE FUNCTION next_job()
RETURNS json
LANGUAGE 'sql'
AS $BODY$
WITH thejob AS (
SELECT jobs.*, company.*
FROM (
select * from jobs
WHERE NOT EXISTS (SELECT * from jobs AS j2 where jobs.platform = j2.platform and jobs.project = j2.project AND start > now() - interval '1 hour')
order by priority, account_priority, job_id
limit 1) jobs
LEFT OUTER JOIN company
ON jobs.company_id = company.id
, enabled
WHERE enabled.status IS TRUE
FOR UPDATE of jobs
)
UPDATE jobs
SET start = now()
FROM thejob
WHERE jobs.job_id = thejob.job_id
RETURNING json_build_object('job_id', jobs.job_id, 'platform', jobs.platform, 'project', jobs.project, 'firstSeen', thejob.first_seen);
$BODY$;
The intent of the locking is to ensure that a job is taken by only one worker at a time (which seems to work as expected), but I'm seeing occasional deadlocks when calling this function and wonder if my explicit locking is potentially causing problems.
The WHERE NOT EXISTS
is to ensure the same project doesn't get started twice, unless it has timed out after 1 hour.
postgresql
add a comment |
up vote
1
down vote
favorite
In PostgreSQL 9.6, is the below FOR UPDATE
clause in this CTE necessary?
CREATE OR REPLACE FUNCTION next_job()
RETURNS json
LANGUAGE 'sql'
AS $BODY$
WITH thejob AS (
SELECT jobs.*, company.*
FROM (
select * from jobs
WHERE NOT EXISTS (SELECT * from jobs AS j2 where jobs.platform = j2.platform and jobs.project = j2.project AND start > now() - interval '1 hour')
order by priority, account_priority, job_id
limit 1) jobs
LEFT OUTER JOIN company
ON jobs.company_id = company.id
, enabled
WHERE enabled.status IS TRUE
FOR UPDATE of jobs
)
UPDATE jobs
SET start = now()
FROM thejob
WHERE jobs.job_id = thejob.job_id
RETURNING json_build_object('job_id', jobs.job_id, 'platform', jobs.platform, 'project', jobs.project, 'firstSeen', thejob.first_seen);
$BODY$;
The intent of the locking is to ensure that a job is taken by only one worker at a time (which seems to work as expected), but I'm seeing occasional deadlocks when calling this function and wonder if my explicit locking is potentially causing problems.
The WHERE NOT EXISTS
is to ensure the same project doesn't get started twice, unless it has timed out after 1 hour.
postgresql
Is it necessary? Not if you remove the CTE and put all the conditions in the WHERE clause of the UPDATE statement.
– eurotrash
Nov 7 at 9:37
Update: I changed the CTE to be a subquery as suggested by @eurotrash and removed the FOR UPDATE as also suggested by Laurenz Albe and can now report that the result was not as expected. Multiple workers consumed/updated the same job (row) at a time.
– rgareth
Nov 8 at 6:44
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
In PostgreSQL 9.6, is the below FOR UPDATE
clause in this CTE necessary?
CREATE OR REPLACE FUNCTION next_job()
RETURNS json
LANGUAGE 'sql'
AS $BODY$
WITH thejob AS (
SELECT jobs.*, company.*
FROM (
select * from jobs
WHERE NOT EXISTS (SELECT * from jobs AS j2 where jobs.platform = j2.platform and jobs.project = j2.project AND start > now() - interval '1 hour')
order by priority, account_priority, job_id
limit 1) jobs
LEFT OUTER JOIN company
ON jobs.company_id = company.id
, enabled
WHERE enabled.status IS TRUE
FOR UPDATE of jobs
)
UPDATE jobs
SET start = now()
FROM thejob
WHERE jobs.job_id = thejob.job_id
RETURNING json_build_object('job_id', jobs.job_id, 'platform', jobs.platform, 'project', jobs.project, 'firstSeen', thejob.first_seen);
$BODY$;
The intent of the locking is to ensure that a job is taken by only one worker at a time (which seems to work as expected), but I'm seeing occasional deadlocks when calling this function and wonder if my explicit locking is potentially causing problems.
The WHERE NOT EXISTS
is to ensure the same project doesn't get started twice, unless it has timed out after 1 hour.
postgresql
In PostgreSQL 9.6, is the below FOR UPDATE
clause in this CTE necessary?
CREATE OR REPLACE FUNCTION next_job()
RETURNS json
LANGUAGE 'sql'
AS $BODY$
WITH thejob AS (
SELECT jobs.*, company.*
FROM (
select * from jobs
WHERE NOT EXISTS (SELECT * from jobs AS j2 where jobs.platform = j2.platform and jobs.project = j2.project AND start > now() - interval '1 hour')
order by priority, account_priority, job_id
limit 1) jobs
LEFT OUTER JOIN company
ON jobs.company_id = company.id
, enabled
WHERE enabled.status IS TRUE
FOR UPDATE of jobs
)
UPDATE jobs
SET start = now()
FROM thejob
WHERE jobs.job_id = thejob.job_id
RETURNING json_build_object('job_id', jobs.job_id, 'platform', jobs.platform, 'project', jobs.project, 'firstSeen', thejob.first_seen);
$BODY$;
The intent of the locking is to ensure that a job is taken by only one worker at a time (which seems to work as expected), but I'm seeing occasional deadlocks when calling this function and wonder if my explicit locking is potentially causing problems.
The WHERE NOT EXISTS
is to ensure the same project doesn't get started twice, unless it has timed out after 1 hour.
postgresql
postgresql
edited Nov 7 at 10:40
Laurenz Albe
41.2k92746
41.2k92746
asked Nov 7 at 9:07
rgareth
1,19121125
1,19121125
Is it necessary? Not if you remove the CTE and put all the conditions in the WHERE clause of the UPDATE statement.
– eurotrash
Nov 7 at 9:37
Update: I changed the CTE to be a subquery as suggested by @eurotrash and removed the FOR UPDATE as also suggested by Laurenz Albe and can now report that the result was not as expected. Multiple workers consumed/updated the same job (row) at a time.
– rgareth
Nov 8 at 6:44
add a comment |
Is it necessary? Not if you remove the CTE and put all the conditions in the WHERE clause of the UPDATE statement.
– eurotrash
Nov 7 at 9:37
Update: I changed the CTE to be a subquery as suggested by @eurotrash and removed the FOR UPDATE as also suggested by Laurenz Albe and can now report that the result was not as expected. Multiple workers consumed/updated the same job (row) at a time.
– rgareth
Nov 8 at 6:44
Is it necessary? Not if you remove the CTE and put all the conditions in the WHERE clause of the UPDATE statement.
– eurotrash
Nov 7 at 9:37
Is it necessary? Not if you remove the CTE and put all the conditions in the WHERE clause of the UPDATE statement.
– eurotrash
Nov 7 at 9:37
Update: I changed the CTE to be a subquery as suggested by @eurotrash and removed the FOR UPDATE as also suggested by Laurenz Albe and can now report that the result was not as expected. Multiple workers consumed/updated the same job (row) at a time.
– rgareth
Nov 8 at 6:44
Update: I changed the CTE to be a subquery as suggested by @eurotrash and removed the FOR UPDATE as also suggested by Laurenz Albe and can now report that the result was not as expected. Multiple workers consumed/updated the same job (row) at a time.
– rgareth
Nov 8 at 6:44
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
Because there is only a single row involved, the CTE seems unnecessary. This would only make sense if you want to update several rows and lock them in a certain order to avoid deadlocks.
Since the query will lock only a single jobs
row, it cannot deadlock with anything by itself.
There most be some other data modifying statements in the same transaction that together with the statement you show create a deadlock.
Remember that locks are held until the end of a transaction!
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
Because there is only a single row involved, the CTE seems unnecessary. This would only make sense if you want to update several rows and lock them in a certain order to avoid deadlocks.
Since the query will lock only a single jobs
row, it cannot deadlock with anything by itself.
There most be some other data modifying statements in the same transaction that together with the statement you show create a deadlock.
Remember that locks are held until the end of a transaction!
add a comment |
up vote
0
down vote
Because there is only a single row involved, the CTE seems unnecessary. This would only make sense if you want to update several rows and lock them in a certain order to avoid deadlocks.
Since the query will lock only a single jobs
row, it cannot deadlock with anything by itself.
There most be some other data modifying statements in the same transaction that together with the statement you show create a deadlock.
Remember that locks are held until the end of a transaction!
add a comment |
up vote
0
down vote
up vote
0
down vote
Because there is only a single row involved, the CTE seems unnecessary. This would only make sense if you want to update several rows and lock them in a certain order to avoid deadlocks.
Since the query will lock only a single jobs
row, it cannot deadlock with anything by itself.
There most be some other data modifying statements in the same transaction that together with the statement you show create a deadlock.
Remember that locks are held until the end of a transaction!
Because there is only a single row involved, the CTE seems unnecessary. This would only make sense if you want to update several rows and lock them in a certain order to avoid deadlocks.
Since the query will lock only a single jobs
row, it cannot deadlock with anything by itself.
There most be some other data modifying statements in the same transaction that together with the statement you show create a deadlock.
Remember that locks are held until the end of a transaction!
answered Nov 7 at 10:39
Laurenz Albe
41.2k92746
41.2k92746
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53186332%2fselect-for-update-necessary-in-cte-for-update%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Is it necessary? Not if you remove the CTE and put all the conditions in the WHERE clause of the UPDATE statement.
– eurotrash
Nov 7 at 9:37
Update: I changed the CTE to be a subquery as suggested by @eurotrash and removed the FOR UPDATE as also suggested by Laurenz Albe and can now report that the result was not as expected. Multiple workers consumed/updated the same job (row) at a time.
– rgareth
Nov 8 at 6:44