Skip to content

Commit

Permalink
Bug Fix: MyRocks locking scan with RC may not return full result sets (
Browse files Browse the repository at this point in the history
…percona#916)

Upstream commit ID : fb-mysql-5.6.35/b939510e2d1ecff961d1c1aee50a2fdd2bcb223a
PS-5577 : Merge fb-prod201901

Summary:
With Read Committed isolation level, MyRocks locking full/range scan
(updates/deletes with Seek) may end up GetForUpdate returning kNotFound,
if other transactions delete the same key after Seek and before
GetForUpdate. MyRocks was expected to skip to next row in that case.
However, under certain conditions (locking range scan with equal predicates,
and locking range/full scan with secondary index),
MyRocks stops scanning records further and end up not updating after
that, even if further rows may match.
From caller's perspective, the update succeeds with updating
zero/fewer rows.
This diff catches the cases and preventing from stopping scanning.
Pull Request resolved: facebook/mysql-5.6#916

Differential Revision: D13545942

Pulled By: yoshinorim

fbshipit-source-id: 76285a06a05
  • Loading branch information
yoshinorim authored and oleksandr-kachan committed Jan 29, 2024
1 parent 46a6abc commit 220f60b
Show file tree
Hide file tree
Showing 14 changed files with 875 additions and 96 deletions.
64 changes: 61 additions & 3 deletions mysql-test/suite/rocksdb/include/rocksdb_concurrent_delete.inc
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
--source include/have_rocksdb.inc
--source include/have_debug_sync.inc

--source include/count_sessions.inc

# Usage:
#
# let $order = ASC; # or DESC
Expand All @@ -8,21 +13,30 @@ let $first_row = -1; # Error this should never happen
if ($order == 'ASC')
{
let $first_row = 1;
let $middle_row = 3;
let $end_row = 5;
}
if ($order == 'DESC')
{
let $first_row = 3;
let $first_row = 5;
let $middle_row = 3;
let $end_row = 1;
}

connect (con, localhost, root,,);
connection default;
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;

eval CREATE TABLE t1 (pk INT PRIMARY KEY COMMENT '$comment', a INT) ENGINE=ROCKSDB;
INSERT INTO t1 VALUES(1,1), (2,2), (3,3);
SET debug_sync='RESET';

eval CREATE TABLE t1 (pk INT PRIMARY KEY COMMENT '$comment', a INT) ENGINE=RocksDB;
INSERT INTO t1 VALUES(1,1), (2,2), (3,3), (4,4), (5,5);

# This will cause the SELECT to block after finding the first row, but
# before locking and reading it.
--echo --PK first row delete
connection con;
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;
SET debug_sync='rocksdb_concurrent_delete SIGNAL parked WAIT_FOR go';
send_eval SELECT * FROM t1 order by t1.pk $order FOR UPDATE;

Expand All @@ -41,8 +55,52 @@ SET debug_sync='now SIGNAL go';
connection con;
reap;

# Deleting a middle row
--echo --PK middle row delete
SET debug_sync='rocksdb_concurrent_delete SIGNAL parked WAIT_FOR go';
send_eval SELECT * FROM t1 order by t1.pk $order FOR UPDATE;

connection default;
SET debug_sync='now WAIT_FOR parked';
eval DELETE FROM t1 WHERE pk = $middle_row;
SET debug_sync='now SIGNAL go';

connection con;
if ($isolation_level == "REPEATABLE READ")
{
--error ER_LOCK_DEADLOCK
reap;
}
if ($isolation_level == "READ COMMITTED")
{
reap;
}

# Deleting the end row
--echo --PK end row delete
SET debug_sync='rocksdb_concurrent_delete SIGNAL parked WAIT_FOR go';
send_eval SELECT * FROM t1 order by t1.pk $order FOR UPDATE;

connection default;
SET debug_sync='now WAIT_FOR parked';
eval DELETE FROM t1 WHERE pk = $end_row;
SET debug_sync='now SIGNAL go';

connection con;
if ($isolation_level == "REPEATABLE READ")
{
--error ER_LOCK_DEADLOCK
reap;
}
if ($isolation_level == "READ COMMITTED")
{
reap;
}


# Cleanup
connection default;
disconnect con;
set debug_sync='RESET';
drop table t1;
--source include/wait_until_count_sessions.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--source include/have_rocksdb.inc
--source include/have_debug_sync.inc

# This validates the fix for Issue #144. The problem was that with more
# than one client accessing/deleting the same row there was a possibility
# of client A finding a row (through Next() or Prev()) but the row being
# deleted before the GetForUpdate() call could occur. When this happened
# a nearly useless error was being returned.

let $order=ASC;
let $comment="";
--source rocksdb_concurrent_delete.inc
--source rocksdb_concurrent_delete_sk.inc

let $order=DESC;
let $comment="";
--source rocksdb_concurrent_delete.inc

let $order=ASC;
let $comment="rev:cf2";
--source rocksdb_concurrent_delete.inc

let $order=DESC;
let $comment="rev:cf2";
--source rocksdb_concurrent_delete.inc

let $index=PRIMARY;
--source rocksdb_concurrent_delete_range.inc
let $index=sk;
--source rocksdb_concurrent_delete_range.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
--source include/have_rocksdb.inc
--source include/have_debug_sync.inc

--source include/count_sessions.inc

# This is a test case to reproduce https://github.com/facebook/mysql-5.6/issues/162
# Expected output of the last select for update was (1,2,100) and (1,3,100), but
# currently it returns (1,2,1) and (1,3,1), which must be fixed.

connect (con, localhost, root,,);
connection default;

set debug_sync='RESET';
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;
create table t1 (id1 int, id2 int, value int, primary key (id1, id2), index sk (id1, value)) ENGINE=RocksDB;
insert into t1 values (1, 1, 1),(1, 2, 1),(1, 3, 1),(1, 4, 1),(1, 5, 1),(2, 2, 2);

# deleting a first row
--echo --First row delete with $index
connection con;
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;
set debug_sync='rocksdb.get_row_by_rowid SIGNAL parked WAIT_FOR go';
send_eval update t1 force index ($index) set value=100 where id1=1;

connection default;
set debug_sync='now WAIT_FOR parked';
delete from t1 where id1=1 and id2=1;
set debug_sync='now SIGNAL go';

connection con;
reap;
select * from t1 where id1=1;

# deleting a middle row
--echo --Middle row delete with $index
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;
set debug_sync='rocksdb.get_row_by_rowid SIGNAL parked WAIT_FOR go';
send_eval update t1 force index ($index) set value=200 where id1=1;

connection default;
set debug_sync='now WAIT_FOR parked';
delete from t1 where id1=1 and id2=3;
set debug_sync='now SIGNAL go';

connection con;
if ($isolation_level == "REPEATABLE READ")
{
--error ER_LOCK_DEADLOCK
reap;
}
if ($isolation_level == "READ COMMITTED")
{
reap;
}
select * from t1 where id1=1;

# deleting the end row
--echo --End row delete with $index
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;
set debug_sync='rocksdb.get_row_by_rowid SIGNAL parked WAIT_FOR go';
send_eval update t1 force index ($index) set value=300 where id1=1;

connection default;
set debug_sync='now WAIT_FOR parked';
delete from t1 where id1=1 and id2=5;
set debug_sync='now SIGNAL go';

connection con;
if ($isolation_level == "REPEATABLE READ")
{
--error ER_LOCK_DEADLOCK
reap;
}
if ($isolation_level == "READ COMMITTED")
{
reap;
}
select * from t1 where id1=1;

# Cleanup
connection default;
disconnect con;
set debug_sync='RESET';
drop table t1;
--source include/wait_until_count_sessions.inc
82 changes: 82 additions & 0 deletions mysql-test/suite/rocksdb/include/rocksdb_concurrent_delete_sk.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
--source include/have_rocksdb.inc
--source include/have_debug_sync.inc

--source include/count_sessions.inc

connect (con, localhost, root,,);
connection default;
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;

SET debug_sync='RESET';

eval CREATE TABLE t1 (pk INT PRIMARY KEY, a INT, index a(a)) ENGINE=RocksDB;
INSERT INTO t1 VALUES(1,1), (2,2), (3,3), (4,4), (5,5);

# This will cause the SELECT to block after finding the first row, but
# before locking and reading it.
--echo --SK first row delete
connection con;
eval SET SESSION TRANSACTION ISOLATION LEVEL $isolation_level;
SET debug_sync='rocksdb_concurrent_delete_sk SIGNAL parked WAIT_FOR go';
send_eval SELECT a FROM t1 FORCE INDEX(a) FOR UPDATE;

# While that connection is waiting, delete the first row (the one con
# is about to lock and read
connection default;
SET debug_sync='now WAIT_FOR parked';
eval DELETE FROM t1 WHERE pk = 1;

# Signal the waiting select to continue
SET debug_sync='now SIGNAL go';

connection con;
reap;

# Deleting a middle row
--echo --SK middle row delete
SET debug_sync='rocksdb_concurrent_delete_sk SIGNAL parked WAIT_FOR go';
send_eval SELECT a FROM t1 FORCE INDEX(a) FOR UPDATE;

connection default;
SET debug_sync='now WAIT_FOR parked';
eval DELETE FROM t1 WHERE pk = 3;
SET debug_sync='now SIGNAL go';

connection con;
if ($isolation_level == "REPEATABLE READ")
{
--error ER_LOCK_DEADLOCK
reap;
}
if ($isolation_level == "READ COMMITTED")
{
reap;
}

# Deleting the end row
--echo --SK end row delete
SET debug_sync='rocksdb_concurrent_delete_sk SIGNAL parked WAIT_FOR go';
send_eval SELECT a FROM t1 FORCE INDEX(a) FOR UPDATE;

connection default;
SET debug_sync='now WAIT_FOR parked';
eval DELETE FROM t1 WHERE pk = 5;
SET debug_sync='now SIGNAL go';

connection con;
if ($isolation_level == "REPEATABLE READ")
{
--error ER_LOCK_DEADLOCK
reap;
}
if ($isolation_level == "READ COMMITTED")
{
reap;
}

# Cleanup
connection default;
disconnect con;
set debug_sync='RESET';
drop table t1;
--source include/wait_until_count_sessions.inc
13 changes: 0 additions & 13 deletions mysql-test/suite/rocksdb/r/delete_before_lock.result

This file was deleted.

0 comments on commit 220f60b

Please sign in to comment.