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

Commit312bde3

Browse files
committed
Fix improper abort during update chain locking
In247c76a, I added some code to do fine-grained checking ofMultiXact status of locking/updating transactions when traversing anupdate chain. There was a thinko in that patch which would have thetraversing abort, that is return HeapTupleUpdated, when the othertransaction is a committed lock-only. In this case we should ignore itand return success instead. Of course, in the case where there is acommitted update, HeapTupleUpdated is the correct return value.A user-visible symptom of this bug is that in REPEATABLE READ andSERIALIZABLE transaction isolation modes spurious serializability errorscan occur: ERROR: could not serialize access due to concurrent updateIn order for this to happen, there needs to be a tuple that's key-share-locked and also updated, and the update must abort; a subsequenttransaction trying to acquire a new lock on that tuple would abort withthe above error. The reason is that the initial FOR KEY SHARE is seenas committed by the new locking transaction, which triggers this bug.(If the UPDATE commits, then the serialization error is correctlyreported.)When running a query in READ COMMITTED mode, what happens is that thelocking is aborted by the HeapTupleUpdated return value, thenEvalPlanQual fetches the newest version of the tuple, which is then theonly version that gets locked. (The second time the tuple is checkedthere is no misbehavior on the committed lock-only, because it's notchecked by the code that traverses update chains; so no bug.) Only thenewest version of the tuple is locked, not older ones, but this isharmless.The isolation test added by this commit illustrates the desiredbehavior, including the proper serialization errors that get thrown.Backpatch to 9.3.
1 parent74242c2 commit312bde3

File tree

5 files changed

+317
-3
lines changed

5 files changed

+317
-3
lines changed

‎src/backend/access/heap/heapam.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4859,10 +4859,23 @@ test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
48594859
elseif (TransactionIdDidCommit(xid))
48604860
{
48614861
/*
4862-
* If the updating transaction committed, what we do depends on whether
4863-
* the lock modes conflict: if they do, then we must report error to
4864-
* caller. But if they don't, we can fall through to lock it.
4862+
* The other transaction committed. If it was only a locker, then the
4863+
* lock is completely gone now and we can return success; but if it
4864+
* was an update, then what we do depends on whether the two lock
4865+
* modes conflict.If they conflict, then we must report error to
4866+
* caller. But if they don't, we can fall through to allow the current
4867+
* transaction to lock the tuple.
4868+
*
4869+
* Note: the reason we worry about ISUPDATE here is because as soon as
4870+
* a transaction ends, all its locks are gone and meaningless, and
4871+
* thus we can ignore them; whereas its updates persist. In the
4872+
* TransactionIdIsInProgress case, above, we don't need to check
4873+
* because we know the lock is still "alive" and thus a conflict needs
4874+
* always be checked.
48654875
*/
4876+
if (!ISUPDATE_from_mxstatus(status))
4877+
returnHeapTupleMayBeUpdated;
4878+
48664879
if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
48674880
LOCKMODE_from_mxstatus(wantedstatus)))
48684881
/* bummer */
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
Parsed test spec with 3 sessions
2+
3+
starting permutation: s1_show s1_commit s2_commit
4+
step s1_show: SELECT current_setting('default_transaction_isolation') <> 'read committed';
5+
?column?
6+
7+
f
8+
step s1_commit: COMMIT;
9+
step s2_commit: COMMIT;
10+
11+
starting permutation: s1_lock s2_update s2_abort s3_forkeyshr s1_commit
12+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
13+
value
14+
15+
1
16+
step s2_update: UPDATE dont_forget SET value = 2;
17+
step s2_abort: ROLLBACK;
18+
step s3_forkeyshr: SELECT * FROM dont_forget FOR KEY SHARE;
19+
value
20+
21+
1
22+
step s1_commit: COMMIT;
23+
24+
starting permutation: s1_lock s2_update s2_commit s3_forkeyshr s1_commit
25+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
26+
value
27+
28+
1
29+
step s2_update: UPDATE dont_forget SET value = 2;
30+
step s2_commit: COMMIT;
31+
step s3_forkeyshr: SELECT * FROM dont_forget FOR KEY SHARE;
32+
value
33+
34+
2
35+
step s1_commit: COMMIT;
36+
37+
starting permutation: s1_lock s2_update s1_commit s3_forkeyshr s2_commit
38+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
39+
value
40+
41+
1
42+
step s2_update: UPDATE dont_forget SET value = 2;
43+
step s1_commit: COMMIT;
44+
step s3_forkeyshr: SELECT * FROM dont_forget FOR KEY SHARE;
45+
value
46+
47+
1
48+
step s2_commit: COMMIT;
49+
50+
starting permutation: s1_lock s2_update s2_abort s3_fornokeyupd s1_commit
51+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
52+
value
53+
54+
1
55+
step s2_update: UPDATE dont_forget SET value = 2;
56+
step s2_abort: ROLLBACK;
57+
step s3_fornokeyupd: SELECT * FROM dont_forget FOR NO KEY UPDATE;
58+
value
59+
60+
1
61+
step s1_commit: COMMIT;
62+
63+
starting permutation: s1_lock s2_update s2_commit s3_fornokeyupd s1_commit
64+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
65+
value
66+
67+
1
68+
step s2_update: UPDATE dont_forget SET value = 2;
69+
step s2_commit: COMMIT;
70+
step s3_fornokeyupd: SELECT * FROM dont_forget FOR NO KEY UPDATE;
71+
value
72+
73+
2
74+
step s1_commit: COMMIT;
75+
76+
starting permutation: s1_lock s2_update s1_commit s3_fornokeyupd s2_commit
77+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
78+
value
79+
80+
1
81+
step s2_update: UPDATE dont_forget SET value = 2;
82+
step s1_commit: COMMIT;
83+
step s3_fornokeyupd: SELECT * FROM dont_forget FOR NO KEY UPDATE; <waiting ...>
84+
step s2_commit: COMMIT;
85+
step s3_fornokeyupd: <... completed>
86+
value
87+
88+
2
89+
90+
starting permutation: s1_lock s2_update s2_abort s3_forupd s1_commit
91+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
92+
value
93+
94+
1
95+
step s2_update: UPDATE dont_forget SET value = 2;
96+
step s2_abort: ROLLBACK;
97+
step s3_forupd: SELECT * FROM dont_forget FOR UPDATE; <waiting ...>
98+
step s1_commit: COMMIT;
99+
step s3_forupd: <... completed>
100+
value
101+
102+
1
103+
104+
starting permutation: s1_lock s2_update s2_commit s3_forupd s1_commit
105+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
106+
value
107+
108+
1
109+
step s2_update: UPDATE dont_forget SET value = 2;
110+
step s2_commit: COMMIT;
111+
step s3_forupd: SELECT * FROM dont_forget FOR UPDATE; <waiting ...>
112+
step s1_commit: COMMIT;
113+
step s3_forupd: <... completed>
114+
value
115+
116+
2
117+
118+
starting permutation: s1_lock s2_update s1_commit s3_forupd s2_commit
119+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
120+
value
121+
122+
1
123+
step s2_update: UPDATE dont_forget SET value = 2;
124+
step s1_commit: COMMIT;
125+
step s3_forupd: SELECT * FROM dont_forget FOR UPDATE; <waiting ...>
126+
step s2_commit: COMMIT;
127+
step s3_forupd: <... completed>
128+
value
129+
130+
2
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
Parsed test spec with 3 sessions
2+
3+
starting permutation: s1_show s1_commit s2_commit
4+
step s1_show: SELECT current_setting('default_transaction_isolation') <> 'read committed';
5+
?column?
6+
7+
t
8+
step s1_commit: COMMIT;
9+
step s2_commit: COMMIT;
10+
11+
starting permutation: s1_lock s2_update s2_abort s3_forkeyshr s1_commit
12+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
13+
value
14+
15+
1
16+
step s2_update: UPDATE dont_forget SET value = 2;
17+
step s2_abort: ROLLBACK;
18+
step s3_forkeyshr: SELECT * FROM dont_forget FOR KEY SHARE;
19+
value
20+
21+
1
22+
step s1_commit: COMMIT;
23+
24+
starting permutation: s1_lock s2_update s2_commit s3_forkeyshr s1_commit
25+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
26+
value
27+
28+
1
29+
step s2_update: UPDATE dont_forget SET value = 2;
30+
step s2_commit: COMMIT;
31+
step s3_forkeyshr: SELECT * FROM dont_forget FOR KEY SHARE;
32+
value
33+
34+
2
35+
step s1_commit: COMMIT;
36+
37+
starting permutation: s1_lock s2_update s1_commit s3_forkeyshr s2_commit
38+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
39+
value
40+
41+
1
42+
step s2_update: UPDATE dont_forget SET value = 2;
43+
step s1_commit: COMMIT;
44+
step s3_forkeyshr: SELECT * FROM dont_forget FOR KEY SHARE;
45+
value
46+
47+
1
48+
step s2_commit: COMMIT;
49+
50+
starting permutation: s1_lock s2_update s2_abort s3_fornokeyupd s1_commit
51+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
52+
value
53+
54+
1
55+
step s2_update: UPDATE dont_forget SET value = 2;
56+
step s2_abort: ROLLBACK;
57+
step s3_fornokeyupd: SELECT * FROM dont_forget FOR NO KEY UPDATE;
58+
value
59+
60+
1
61+
step s1_commit: COMMIT;
62+
63+
starting permutation: s1_lock s2_update s2_commit s3_fornokeyupd s1_commit
64+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
65+
value
66+
67+
1
68+
step s2_update: UPDATE dont_forget SET value = 2;
69+
step s2_commit: COMMIT;
70+
step s3_fornokeyupd: SELECT * FROM dont_forget FOR NO KEY UPDATE;
71+
value
72+
73+
2
74+
step s1_commit: COMMIT;
75+
76+
starting permutation: s1_lock s2_update s1_commit s3_fornokeyupd s2_commit
77+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
78+
value
79+
80+
1
81+
step s2_update: UPDATE dont_forget SET value = 2;
82+
step s1_commit: COMMIT;
83+
step s3_fornokeyupd: SELECT * FROM dont_forget FOR NO KEY UPDATE; <waiting ...>
84+
step s2_commit: COMMIT;
85+
step s3_fornokeyupd: <... completed>
86+
error in steps s2_commit s3_fornokeyupd: ERROR: could not serialize access due to concurrent update
87+
88+
starting permutation: s1_lock s2_update s2_abort s3_forupd s1_commit
89+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
90+
value
91+
92+
1
93+
step s2_update: UPDATE dont_forget SET value = 2;
94+
step s2_abort: ROLLBACK;
95+
step s3_forupd: SELECT * FROM dont_forget FOR UPDATE; <waiting ...>
96+
step s1_commit: COMMIT;
97+
step s3_forupd: <... completed>
98+
value
99+
100+
1
101+
102+
starting permutation: s1_lock s2_update s2_commit s3_forupd s1_commit
103+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
104+
value
105+
106+
1
107+
step s2_update: UPDATE dont_forget SET value = 2;
108+
step s2_commit: COMMIT;
109+
step s3_forupd: SELECT * FROM dont_forget FOR UPDATE; <waiting ...>
110+
step s1_commit: COMMIT;
111+
step s3_forupd: <... completed>
112+
value
113+
114+
2
115+
116+
starting permutation: s1_lock s2_update s1_commit s3_forupd s2_commit
117+
step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE;
118+
value
119+
120+
1
121+
step s2_update: UPDATE dont_forget SET value = 2;
122+
step s1_commit: COMMIT;
123+
step s3_forupd: SELECT * FROM dont_forget FOR UPDATE; <waiting ...>
124+
step s2_commit: COMMIT;
125+
step s3_forupd: <... completed>
126+
error in steps s2_commit s3_forupd: ERROR: could not serialize access due to concurrent update

‎src/test/isolation/isolation_schedule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ test: delete-abort-savept
2020
test: delete-abort-savept-2
2121
test: aborted-keyrevoke
2222
test: multixact-no-deadlock
23+
test: multixact-no-forget
2324
test: drop-index-concurrently-1
2425
test: timeouts
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# If transaction A holds a lock, and transaction B does an update,
2+
# make sure we don't forget the lock if B aborts.
3+
setup
4+
{
5+
CREATETABLEdont_forget (
6+
valueint
7+
);
8+
9+
INSERTINTOdont_forgetVALUES (1);
10+
}
11+
12+
teardown
13+
{
14+
DROPTABLEdont_forget;
15+
}
16+
17+
session"s1"
18+
setup{BEGIN; }
19+
step"s1_show"{SELECTcurrent_setting('default_transaction_isolation')<>'read committed'; }
20+
step"s1_lock"{SELECT*FROMdont_forgetFORKEYSHARE; }
21+
step"s1_commit" {COMMIT; }
22+
23+
session"s2"
24+
setup{BEGIN; }
25+
step"s2_update"{UPDATEdont_forgetSETvalue=2; }
26+
step"s2_abort"{ROLLBACK; }
27+
step"s2_commit"{COMMIT; }
28+
29+
session"s3"
30+
# try cases with both a non-conflicting lock with s1's and a conflicting one
31+
step"s3_forkeyshr"{SELECT*FROMdont_forgetFORKEYSHARE; }
32+
step"s3_fornokeyupd"{SELECT*FROMdont_forgetFORNOKEYUPDATE; }
33+
step"s3_forupd"{SELECT*FROMdont_forgetFORUPDATE; }
34+
35+
permutation"s1_show""s1_commit""s2_commit"
36+
permutation"s1_lock""s2_update""s2_abort""s3_forkeyshr""s1_commit"
37+
permutation"s1_lock""s2_update""s2_commit""s3_forkeyshr""s1_commit"
38+
permutation"s1_lock""s2_update""s1_commit""s3_forkeyshr""s2_commit"
39+
permutation"s1_lock""s2_update""s2_abort""s3_fornokeyupd""s1_commit"
40+
permutation"s1_lock""s2_update""s2_commit""s3_fornokeyupd""s1_commit"
41+
permutation"s1_lock""s2_update""s1_commit""s3_fornokeyupd""s2_commit"
42+
permutation"s1_lock""s2_update""s2_abort""s3_forupd""s1_commit"
43+
permutation"s1_lock""s2_update""s2_commit""s3_forupd""s1_commit"
44+
permutation"s1_lock""s2_update""s1_commit""s3_forupd""s2_commit"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp