Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1c9701c

Browse files
committed
Fix FOR UPDATE NOWAIT on updated tuple chains
If SELECT FOR UPDATE NOWAIT tries to lock a tuple that is concurrentlybeing updated, it might fail to honor its NOWAIT specification and blockinstead of raising an error.Fix by adding a no-wait flag to EvalPlanQualFetch which it can pass downto heap_lock_tuple; also use it in EvalPlanQualFetch itself to avoidblocking while waiting for a concurrent transaction.Authors: Craig Ringer and Thomas Munro, tweaked by Álvarohttp://www.postgresql.org/message-id/51FB6703.9090801@2ndquadrant.comPer Thomas Munro in the course of his SKIP LOCKED feature submission,who also provided one of the isolation test specs.Backpatch to 9.4, because that's as far back as it applies withoutconflicts (although the bug goes all the way back). To that branch alsobackpatch Thomas Munro's new NOWAIT test cases, committed in master byHeikki as commit9ee16b4 .
1 parent9a2d948 commit1c9701c

File tree

9 files changed

+186
-12
lines changed

9 files changed

+186
-12
lines changed

‎src/backend/executor/execMain.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
18631863
/*
18641864
* Get and lock the updated version of the row; if fail, return NULL.
18651865
*/
1866-
copyTuple=EvalPlanQualFetch(estate,relation,lockmode,
1866+
copyTuple=EvalPlanQualFetch(estate,relation,lockmode, false/* wait */,
18671867
tid,priorXmax);
18681868

18691869
if (copyTuple==NULL)
@@ -1922,6 +1922,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
19221922
*estate - executor state data
19231923
*relation - table containing tuple
19241924
*lockmode - requested tuple lock mode
1925+
*noWait - wait mode to pass to heap_lock_tuple
19251926
**tid - t_ctid from the outdated tuple (ie, next updated version)
19261927
*priorXmax - t_xmax from the outdated tuple
19271928
*
@@ -1934,7 +1935,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
19341935
* but we use "int" to avoid having to include heapam.h in executor.h.
19351936
*/
19361937
HeapTuple
1937-
EvalPlanQualFetch(EState*estate,Relationrelation,intlockmode,
1938+
EvalPlanQualFetch(EState*estate,Relationrelation,intlockmode,boolnoWait,
19381939
ItemPointertid,TransactionIdpriorXmax)
19391940
{
19401941
HeapTuplecopyTuple=NULL;
@@ -1978,14 +1979,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
19781979

19791980
/*
19801981
* If tuple is being updated by other transaction then we have to
1981-
* wait for its commit/abort.
1982+
* wait for its commit/abort, or die trying.
19821983
*/
19831984
if (TransactionIdIsValid(SnapshotDirty.xmax))
19841985
{
19851986
ReleaseBuffer(buffer);
1986-
XactLockTableWait(SnapshotDirty.xmax,
1987-
relation,&tuple.t_data->t_ctid,
1988-
XLTW_FetchUpdated);
1987+
if (noWait)
1988+
{
1989+
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
1990+
ereport(ERROR,
1991+
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
1992+
errmsg("could not obtain lock on row in relation \"%s\"",
1993+
RelationGetRelationName(relation))));
1994+
}
1995+
else
1996+
XactLockTableWait(SnapshotDirty.xmax,
1997+
relation,&tuple.t_data->t_ctid,
1998+
XLTW_FetchUpdated);
19891999
continue;/* loop back to repeat heap_fetch */
19902000
}
19912001

@@ -2012,7 +2022,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
20122022
*/
20132023
test=heap_lock_tuple(relation,&tuple,
20142024
estate->es_output_cid,
2015-
lockmode,false/* wait */,
2025+
lockmode,noWait,
20162026
false,&buffer,&hufd);
20172027
/* We now have two pins on the buffer, get rid of one */
20182028
ReleaseBuffer(buffer);

‎src/backend/executor/nodeLockRows.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ ExecLockRows(LockRowsState *node)
170170
}
171171

172172
/* updated, so fetch and lock the updated version */
173-
copyTuple=EvalPlanQualFetch(estate,erm->relation,lockmode,
173+
copyTuple=EvalPlanQualFetch(estate,erm->relation,lockmode,erm->noWait,
174174
&hufd.ctid,hufd.xmax);
175175

176176
if (copyTuple==NULL)

‎src/include/executor/executor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
199199
Relationrelation,Indexrti,intlockmode,
200200
ItemPointertid,TransactionIdpriorXmax);
201201
externHeapTupleEvalPlanQualFetch(EState*estate,Relationrelation,
202-
intlockmode,ItemPointertid,TransactionIdpriorXmax);
202+
intlockmode,boolnoWait,ItemPointertid,TransactionIdpriorXmax);
203203
externvoidEvalPlanQualInit(EPQState*epqstate,EState*estate,
204204
Plan*subplan,List*auxrowmarks,intepqParam);
205205
externvoidEvalPlanQualSetPlan(EPQState*epqstate,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s2a s1a s2b s2c s2d s2e s1b s2f
4+
step s2a: SELECT pg_advisory_lock(0);
5+
pg_advisory_lock
6+
7+
8+
step s1a: SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL FOR UPDATE NOWAIT; <waiting ...>
9+
step s2b: UPDATE foo SET data = data;
10+
step s2c: BEGIN;
11+
step s2d: UPDATE foo SET data = data;
12+
step s2e: SELECT pg_advisory_unlock(0);
13+
pg_advisory_unlock
14+
15+
t
16+
step s1a: <... completed>
17+
error in steps s2e s1a: ERROR: could not obtain lock on row in relation "foo"
18+
step s1b: COMMIT;
19+
step s2f: COMMIT;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s2a s1a s2b s2c s2d s2e s1b s2f
4+
step s2a: SELECT pg_advisory_lock(0);
5+
pg_advisory_lock
6+
7+
8+
step s1a: SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL FOR UPDATE NOWAIT; <waiting ...>
9+
step s2b: UPDATE foo SET data = data;
10+
step s2c: BEGIN;
11+
step s2d: UPDATE foo SET data = data;
12+
step s2e: SELECT pg_advisory_unlock(0);
13+
pg_advisory_unlock
14+
15+
t
16+
step s1a: <... completed>
17+
error in steps s2e s1a: ERROR: could not serialize access due to concurrent update
18+
step s1b: COMMIT;
19+
step s2f: COMMIT;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
Parsed test spec with 3 sessions
2+
3+
starting permutation: sl1_prep upd_getlock sl1_exec upd_doupdate lk1_doforshare upd_releaselock
4+
step sl1_prep:
5+
PREPARE sl1_run AS SELECT id FROM test_nowait WHERE pg_advisory_lock(0) is not null FOR UPDATE NOWAIT;
6+
7+
step upd_getlock:
8+
SELECT pg_advisory_lock(0);
9+
10+
pg_advisory_lock
11+
12+
13+
step sl1_exec:
14+
BEGIN ISOLATION LEVEL READ COMMITTED;
15+
EXECUTE sl1_run;
16+
SELECT xmin, xmax, ctid, * FROM test_nowait;
17+
<waiting ...>
18+
step upd_doupdate:
19+
BEGIN ISOLATION LEVEL READ COMMITTED;
20+
UPDATE test_nowait SET value = value WHERE id % 2 = 0;
21+
COMMIT;
22+
23+
step lk1_doforshare:
24+
BEGIN ISOLATION LEVEL READ COMMITTED;
25+
SELECT id FROM test_nowait WHERE id % 2 = 0 FOR SHARE;
26+
27+
id
28+
29+
2
30+
step upd_releaselock:
31+
SELECT pg_advisory_unlock(0);
32+
33+
pg_advisory_unlock
34+
35+
t
36+
step sl1_exec: <... completed>
37+
error in steps upd_releaselock sl1_exec: ERROR: could not obtain lock on row in relation "test_nowait"

‎src/test/isolation/isolation_schedule

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ test: aborted-keyrevoke
2222
test: multixact-no-deadlock
2323
test: multixact-no-forget
2424
test: propagate-lock-delete
25-
test: drop-index-concurrently-1
26-
test: alter-table-1
27-
test: timeouts
2825
test: nowait
2926
test: nowait-2
3027
test: nowait-3
28+
test: nowait-4
29+
test: nowait-5
30+
test: drop-index-concurrently-1
31+
test: alter-table-1
32+
test: timeouts
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Test NOWAIT with an updated tuple chain.
2+
3+
setup
4+
{
5+
CREATETABLEfoo (
6+
idintPRIMARYKEY,
7+
datatextNOTNULL
8+
);
9+
INSERTINTOfooVALUES (1,'x');
10+
}
11+
12+
teardown
13+
{
14+
DROPTABLEfoo;
15+
}
16+
17+
session"s1"
18+
setup{BEGIN; }
19+
step"s1a"{SELECT*FROMfooWHEREpg_advisory_lock(0)ISNOTNULLFORUPDATENOWAIT; }
20+
step"s1b"{COMMIT; }
21+
22+
session"s2"
23+
step"s2a"{SELECTpg_advisory_lock(0); }
24+
step"s2b"{UPDATEfooSETdata=data; }
25+
step"s2c"{BEGIN; }
26+
step"s2d"{UPDATEfooSETdata=data; }
27+
step"s2e"{SELECTpg_advisory_unlock(0); }
28+
step"s2f"{COMMIT; }
29+
30+
permutation"s2a""s1a""s2b""s2c""s2d""s2e""s1b""s2f"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Test NOWAIT on an updated tuple chain
2+
3+
setup
4+
{
5+
6+
DROPTABLEIFEXISTStest_nowait;
7+
CREATETABLEtest_nowait (
8+
idintegerPRIMARYKEY,
9+
valueintegernotnull
10+
);
11+
12+
INSERTINTOtest_nowait
13+
SELECTx,xFROMgenerate_series(1,2)x;
14+
}
15+
16+
teardown
17+
{
18+
DROPTABLEtest_nowait;
19+
}
20+
21+
session"sl1"
22+
step"sl1_prep" {
23+
PREPAREsl1_runASSELECTidFROMtest_nowaitWHEREpg_advisory_lock(0)isnotnullFORUPDATENOWAIT;
24+
}
25+
step"sl1_exec" {
26+
BEGINISOLATIONLEVELREADCOMMITTED;
27+
EXECUTEsl1_run;
28+
SELECTxmin,xmax,ctid,*FROMtest_nowait;
29+
}
30+
teardown {COMMIT; }
31+
32+
# A session that's used for an UPDATE of the rows to be locked, for when we're testing ctid
33+
# chain following.
34+
session"upd"
35+
step"upd_getlock" {
36+
SELECTpg_advisory_lock(0);
37+
}
38+
step"upd_doupdate" {
39+
BEGINISOLATIONLEVELREADCOMMITTED;
40+
UPDATEtest_nowaitSETvalue=valueWHEREid%2=0;
41+
COMMIT;
42+
}
43+
step"upd_releaselock" {
44+
SELECTpg_advisory_unlock(0);
45+
}
46+
47+
# A session that acquires locks that sl1 is supposed to avoid blocking on
48+
session"lk1"
49+
step"lk1_doforshare" {
50+
BEGINISOLATIONLEVELREADCOMMITTED;
51+
SELECTidFROMtest_nowaitWHEREid%2=0FORSHARE;
52+
}
53+
teardown {
54+
COMMIT;
55+
}
56+
57+
permutation"sl1_prep""upd_getlock""sl1_exec""upd_doupdate""lk1_doforshare""upd_releaselock"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp