Skip to content

Commit

Permalink
PS-9297 Lost a record while scanning in reverse order on cursor
Browse files Browse the repository at this point in the history
https://perconadev.atlassian.net/browse/PS-9297

Problem
========
If the last user record on the prev leaf is pessimistically updated during move_backward_from_page, the record may be deleted from the prev leaf and reinserted into the current page. Scanning in reverse order will miss this record.

Cause
========
Same as PS-9092, move_backward_from_page cannot sense that records are moved from the prev leaf to the current leaf.

Solution
========
If btr_insert_into_right_sibling causes records to move between pages, prevent the next page from being optimistically accessed
  • Loading branch information
zhangruyi.vedb committed Jul 5, 2024
1 parent a195525 commit 8e03ddc
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 0 deletions.
42 changes: 42 additions & 0 deletions mysql-test/suite/innodb/r/percona_bug_ps9297.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
create table c(id int primary key, val varchar(8000));
insert into c values (1,repeat('a',3000));
insert into c values (2,repeat('a',3000));
insert into c values (3,repeat('a',3000));
insert into c values (4,repeat('a',3000));
insert into c values (5,repeat('a',3000));
insert into c values (-1,repeat('a',2000));
insert into c values (-2,repeat('a',2000));
insert into c values (-3,repeat('a',2000));
insert into c values (-4,repeat('a',2000));
insert into c values (-5,repeat('a',2000));
select id,length(val) from c order by id desc;
id length(val)
5 3000
4 3000
3 3000
2 3000
1 3000
-1 2000
-2 2000
-3 2000
-4 2000
-5 2000
set DEBUG="+d,desc_scan_debug";
set DEBUG_SYNC="desc_scan_before_restore_position SIGNAL desc_scan_before_restore_position_done WAIT_FOR continue";
select id,length(val) from c order by id desc;
set DEBUG_SYNC="now WAIT_FOR desc_scan_before_restore_position_done";
update c set val=repeat('b',6000) where id=2;
set DEBUG_SYNC="now SIGNAL continue";
id length(val)
5 3000
4 3000
3 3000
2 3000
1 3000
-1 2000
-2 2000
-3 2000
-4 2000
-5 2000
set DEBUG="-d,desc_scan_debug";
drop table c;
31 changes: 31 additions & 0 deletions mysql-test/suite/innodb/t/percona_bug_ps9297.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--source include/have_debug.inc

connect (con1, localhost, root,,);
--connection default
create table c(id int primary key, val varchar(8000));
insert into c values (1,repeat('a',3000));
insert into c values (2,repeat('a',3000));
insert into c values (3,repeat('a',3000));
insert into c values (4,repeat('a',3000));
insert into c values (5,repeat('a',3000));

insert into c values (-1,repeat('a',2000));
insert into c values (-2,repeat('a',2000));
insert into c values (-3,repeat('a',2000));
insert into c values (-4,repeat('a',2000));
insert into c values (-5,repeat('a',2000));

--connection con1
select id,length(val) from c order by id desc;
set DEBUG="+d,desc_scan_debug";
set DEBUG_SYNC="desc_scan_before_restore_position SIGNAL desc_scan_before_restore_position_done WAIT_FOR continue";
send select id,length(val) from c order by id desc;
--connection default
set DEBUG_SYNC="now WAIT_FOR desc_scan_before_restore_position_done";
update c set val=repeat('b',6000) where id=2;
set DEBUG_SYNC="now SIGNAL continue";
--connection con1
reap;
set DEBUG="-d,desc_scan_debug";
--connection default
drop table c;
14 changes: 14 additions & 0 deletions storage/innobase/btr/btr0cur.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3849,6 +3849,7 @@ dberr_t btr_cur_pessimistic_update(ulint flags, btr_cur_t *cursor,
dberr_t optim_err;
roll_ptr_t roll_ptr;
bool was_first;
bool was_before_last = false;
ulint n_reserved = 0;
ulint max_ins_size = 0;
trx_t *const trx = (thr == nullptr) ? nullptr : thr_get_trx(thr);
Expand Down Expand Up @@ -4155,6 +4156,10 @@ dberr_t btr_cur_pessimistic_update(ulint flags, btr_cur_t *cursor,
record on its page? */
was_first = page_cur_is_before_first(page_cursor);

was_before_last =
!page_rec_is_supremum(btr_cur_get_rec(cursor)) &&
page_rec_is_supremum(page_rec_get_next(btr_cur_get_rec(cursor)));

/* Lock checks and undo logging were already performed by
btr_cur_upd_lock_and_undo(). We do not try
btr_cur_optimistic_insert() because
Expand All @@ -4169,6 +4174,15 @@ dberr_t btr_cur_pessimistic_update(ulint flags, btr_cur_t *cursor,
ut_ad(rec_offs_validate(rec, cursor->index, *offsets));
page_cursor->rec = rec;

if (was_before_last) {
/** We may moved the record from the current page to the next page.
* Therefore, we need to disable optimistic access to the next page to
* ensure that the record will not be mistakenly skipped during reverse
* scanning on cursor.
*/
buf_block_modify_clock_inc(btr_cur_get_block(cursor));
}

/* Multiple transactions cannot simultaneously operate on the
same temp-table in parallel.
max_trx_id is ignored for temp tables because it not required
Expand Down
6 changes: 6 additions & 0 deletions storage/innobase/btr/btr0pcur.cc
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,12 @@ void btr_pcur_t::move_backward_from_page(mtr_t *mtr) {

mtr_commit(mtr);

DBUG_EXECUTE_IF("desc_scan_debug", {
if (strcmp(index()->table_name, "test/c") == 0) {
DEBUG_SYNC_C("desc_scan_before_restore_position");
}
});

mtr_start(mtr);

restore_position(latch_mode2, mtr, UT_LOCATION_HERE);
Expand Down

0 comments on commit 8e03ddc

Please sign in to comment.