Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0131291

Browse files
nkeyCommitfest Bot
nkey
authored and
Commitfest Bot
committed
Add an isolation test to reproduce a dirty snapshot scan issue
This commit introduces an isolation test to reliably reproduce the issue where non-MVCC index scans using SnapshotDirty can miss tuples due to concurrent modifications. When using non-MVCC snapshot types, a scan could miss tuples if concurrent transactions delete existing tuples and insert new one with different TIDs on the same page.
1 parent 3c4d755 commit 0131291

File tree

6 files changed

+77
-1
lines changed

6 files changed

+77
-1
lines changed

src/backend/access/index/indexam.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "utils/ruleutils.h"
5858
#include "utils/snapmgr.h"
5959
#include "utils/syscache.h"
60+
#include "utils/injection_point.h"
6061

6162

6263
/* ----------------------------------------------------------------
@@ -741,6 +742,13 @@ index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *
741742
* the index.
742743
*/
743744
Assert(ItemPointerIsValid(&scan->xs_heaptid));
745+
#ifdef USE_INJECTION_POINTS
746+
if (!IsCatalogRelationOid(scan->indexRelation->rd_id))
747+
{
748+
INJECTION_POINT("index_getnext_slot_before_fetch", NULL);
749+
}
750+
#endif
751+
744752
if (index_fetch_heap(scan, slot))
745753
return true;
746754
}

src/backend/executor/execIndexing.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
#include "utils/multirangetypes.h"
118118
#include "utils/rangetypes.h"
119119
#include "utils/snapmgr.h"
120+
#include "utils/injection_point.h"
120121

121122
/* waitMode argument to check_exclusion_or_unique_constraint() */
122123
typedef enum
@@ -943,6 +944,8 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
943944

944945
ExecDropSingleTupleTableSlot(existing_slot);
945946

947+
if (!conflict)
948+
INJECTION_POINT("check_exclusion_or_unique_constraint_no_conflict", NULL);
946949
return !conflict;
947950
}
948951

src/test/modules/injection_points/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ PGFILEDESC = "injection_points - facility for injection points"
1414
REGRESS = injection_points hashagg reindex_conc
1515
REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
1616

17-
ISOLATION = basic inplace syscache-update-pruned
17+
ISOLATION = basic inplace syscache-update-pruned dirty_index_scan
1818

1919
TAP_TESTS = 1
2020

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Parsed test spec with 3 sessions
2+
3+
starting permutation: s1_s1 s2_s1 s3_s1
4+
injection_points_attach
5+
-----------------------
6+
7+
(1 row)
8+
9+
step s1_s1: INSERT INTO test.tbl VALUES(42, 1) on conflict(i) do update set n = EXCLUDED.n + 1; <waiting ...>
10+
step s2_s1: UPDATE test.tbl SET n = n + 1 WHERE i = 42; <waiting ...>
11+
step s3_s1:
12+
SELECT injection_points_detach('index_getnext_slot_before_fetch');
13+
SELECT injection_points_wakeup('index_getnext_slot_before_fetch');
14+
<waiting ...>
15+
step s1_s1: <... completed>
16+
step s2_s1: <... completed>
17+
step s3_s1: <... completed>
18+
injection_points_detach
19+
-----------------------
20+
21+
(1 row)
22+
23+
injection_points_wakeup
24+
-----------------------
25+
26+
(1 row)
27+

src/test/modules/injection_points/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ tests += {
4747
'basic',
4848
'inplace',
4949
'syscache-update-pruned',
50+
'dirty_index_scan',
5051
],
5152
'runningcheck': false, # see syscache-update-pruned
5253
},
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
setup
2+
{
3+
CREATE EXTENSION injection_points;
4+
CREATE SCHEMA test;
5+
CREATE UNLOGGED TABLE test.tbl(i int primary key, n int);
6+
CREATE INDEX tbl_n_idx ON test.tbl(n);
7+
INSERT INTO test.tbl VALUES(42,1);
8+
}
9+
10+
teardown
11+
{
12+
DROP SCHEMA test CASCADE;
13+
DROP EXTENSION injection_points;
14+
}
15+
16+
session s1
17+
setup {
18+
SELECT injection_points_set_local();
19+
SELECT injection_points_attach('check_exclusion_or_unique_constraint_no_conflict', 'error');
20+
SELECT injection_points_attach('index_getnext_slot_before_fetch', 'wait');
21+
}
22+
23+
step s1_s1 { INSERT INTO test.tbl VALUES(42, 1) on conflict(i) do update set n = EXCLUDED.n + 1; }
24+
25+
session s2
26+
step s2_s1 { UPDATE test.tbl SET n = n + 1 WHERE i = 42; }
27+
28+
session s3
29+
step s3_s1 {
30+
SELECT injection_points_detach('index_getnext_slot_before_fetch');
31+
SELECT injection_points_wakeup('index_getnext_slot_before_fetch');
32+
}
33+
34+
permutation
35+
s1_s1
36+
s2_s1(*)
37+
s3_s1(s1_s1)

0 commit comments

Comments
 (0)