Skip to content

Commit 841a563

Browse files
committed
[#27288] YSQL: Avoid 'restart read required' error during yb_index_check()
Summary: yb_index_check() scans all the rows of the index and the base relation. Hence, it becomes prone to 'Restart read required' error if any row is involved in a write operation concurrently with the yb_index_check() read point, which is a high probability event. This hurts usability, but more importantly, yb_index_check() is not concerned with read-after-commit-visibility. It picks up a read time (and an associated snapshot) and uses it to scan both the index and base relation. Even if a write that got committed before the scan read point (as observed by an external clock) is missing from the snapshot due to clock skew, that's acceptable—its effects will be absent from both the index and the base table scans. So, it is safe to set GUC yb_read_after_commit_visibility to 'relaxed' while executing yb_index_check() to avoid these errors. Note that this GUC cannot be set from within a transaction block. Don't do it in such cases and throw a warning instead. Jira: DB-16780 Test Plan: ./yb_build.sh --gtest_filter PgYbIndexCheckTest.YbIndexCheckRestartReadRequired Reviewers: patnaik.balivada Reviewed By: patnaik.balivada Subscribers: svc_phabricator, yql Differential Revision: https://phorge.dev.yugabyte.com/D46002
1 parent d3bc257 commit 841a563

File tree

5 files changed

+70
-0
lines changed

5 files changed

+70
-0
lines changed

src/postgres/src/backend/utils/misc/yb_index_check.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,36 @@ yb_index_check(PG_FUNCTION_ARGS)
141141
{
142142
Oid indexoid = PG_GETARG_OID(0);
143143
bool multi_snapshot_mode = !PG_GETARG_BOOL(1);
144+
int savedGUCLevel = -1;
144145

145146
if (yb_test_index_check_num_batches_per_snapshot == 0)
146147
multi_snapshot_mode = false;
147148

148149
uint64 original_read_point PG_USED_FOR_ASSERTS_ONLY =
149150
YBCPgGetCurrentReadPoint();
150151

152+
bool is_txn_block = IsTransactionBlock();
153+
154+
if (is_txn_block)
155+
ereport(NOTICE,
156+
(errmsg("yb_index_check() is prone to 'Restart read required' "
157+
"erorrs when executed from within a transaction "
158+
"block.")));
159+
160+
if (!is_txn_block)
161+
{
162+
savedGUCLevel = NewGUCNestLevel();
163+
164+
(void) set_config_option("yb_read_after_commit_visibility", "relaxed",
165+
PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE,
166+
true, 0, false);
167+
}
168+
151169
do_index_check(indexoid, multi_snapshot_mode);
152170

171+
if (!is_txn_block)
172+
AtEOXact_GUC(false, savedGUCLevel);
173+
153174
/*
154175
* yb_index_check() uses multiple snapshots in multi_snapshot_mode. Verify
155176
* that it restored the original read point at the end.

src/postgres/src/test/regress/expected/yb.orig.yb_index_check.out

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,16 @@ SELECT yb_index_check('test_bytea_c_b_idx'::regclass::oid);
305305

306306
(1 row)
307307

308+
-- Inside a transaction block
309+
BEGIN;
310+
SELECT yb_index_check('abcd_b_c_d_idx'::regclass::oid);
311+
NOTICE: yb_index_check() is prone to 'Restart read required' erorrs when executed from within a transaction block.
312+
yb_index_check
313+
----------------
314+
315+
(1 row)
316+
317+
COMMIT;
308318
-- Test with more data
309319
INSERT INTO abcd SELECT i, i, i, i FROM generate_series(12, 2000) i;
310320
INSERT INTO abcd values (2002, 2002, 1, 2);

src/postgres/src/test/regress/expected/yb.orig.yb_index_check_1.out

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,16 @@ SELECT yb_index_check('test_bytea_c_b_idx'::regclass::oid);
304304

305305
(1 row)
306306

307+
-- Inside a transaction block
308+
BEGIN;
309+
SELECT yb_index_check('abcd_b_c_d_idx'::regclass::oid);
310+
NOTICE: yb_index_check() is prone to 'Restart read required' erorrs when executed from within a transaction block.
311+
yb_index_check
312+
----------------
313+
314+
(1 row)
315+
316+
COMMIT;
307317
-- Test with more data
308318
INSERT INTO abcd SELECT i, i, i, i FROM generate_series(12, 2000) i;
309319
INSERT INTO abcd values (2002, 2002, 1, 2);

src/postgres/src/test/regress/sql/yb.orig.yb_index_check.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ INSERT INTO test_bytea (a, b, c) VALUES (2, 42, 'test');
187187
SELECT yb_index_check('test_bytea_b_c_idx'::regclass::oid);
188188
SELECT yb_index_check('test_bytea_c_b_idx'::regclass::oid);
189189

190+
-- Inside a transaction block
191+
BEGIN;
192+
SELECT yb_index_check('abcd_b_c_d_idx'::regclass::oid);
193+
COMMIT;
194+
190195
-- Test with more data
191196
INSERT INTO abcd SELECT i, i, i, i FROM generate_series(12, 2000) i;
192197
INSERT INTO abcd values (2002, 2002, 1, 2);

src/yb/yql/pgwrapper/pg_yb_index_check-test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,29 @@ TEST_F(PgYbIndexCheckTest, YbIndexCheckSnapshotTooOld) {
7878
ASSERT_OK((conn.Fetch("SELECT yb_index_check('abcd_b_c_d_idx'::regclass)")));
7979
}
8080

81+
TEST_F(PgYbIndexCheckTest, YbIndexCheckRestartReadRequired) {
82+
auto conn = ASSERT_RESULT(Connect());
83+
int rowcount = 1000;
84+
ASSERT_OK(conn.Execute("SET yb_max_query_layer_retries=0"));
85+
ASSERT_OK(conn.Execute(
86+
"CREATE TABLE abcd(a int primary key, b int, c int, d int) SPLIT INTO 1 TABLETS;"));
87+
ASSERT_OK(conn.Execute("CREATE INDEX abcd_b_c_d_idx ON abcd (b ASC) INCLUDE (c, d)"));
88+
ASSERT_OK(conn.ExecuteFormat(
89+
"INSERT INTO abcd SELECT i, i, i, i FROM generate_series(1, $0) i", rowcount));
90+
91+
CountDownLatch latch(1);
92+
TestThreadHolder holder;
93+
holder.AddThreadFunctor([this, &stop = holder.stop_flag(), &latch, &rowcount] {
94+
auto conn2 = ASSERT_RESULT(Connect());
95+
latch.CountDown();
96+
while (!stop.load()) {
97+
CHECK_OK(conn2.ExecuteFormat("INSERT INTO abcd VALUES ($0, $0, $0, $0)", ++rowcount));
98+
}
99+
});
100+
101+
latch.Wait();
102+
ASSERT_OK((conn.Fetch("SELECT yb_index_check('abcd_b_c_d_idx'::regclass)")));
103+
}
104+
81105
} // namespace pgwrapper
82106
} // namespace yb

0 commit comments

Comments
 (0)