[Commits] fbbc63bd33d: Range Locking, SeekForUpdate: use LockingIter with locking full table scan

revision-id: fbbc63bd33d1bd1ff75bec0f7abf093e2e2de070 (fb-prod201903-271-gfbbc63bd33d) parent(s): 1f738064ee9d3e3d04491ab01b6a6afcd6085136 author: Sergei Petrunia committer: Sergei Petrunia timestamp: 2019-12-15 23:39:36 +0300 message: Range Locking, SeekForUpdate: use LockingIter with locking full table scan - Make ha_rocksdb::setup_iterator_for_rnd_scan() use LockingIterator (that is, use SeekForUpdate) when doing a locking full table scan. - Adjust testcases that are affected by the change - Correctly handle transaction isolation errors (kLockTimeout, kDeadlock, etc). (Without SeekForUpdate, it was not possible to get such errors from an iterator. Now it's possible, and before this commit, we didn't handle e.g. kDeadlock) values ). --- .../rocksdb/r/range_locking_seek_for_update.result | 32 ++++++++++++++++++++-- mysql-test/suite/rocksdb/r/unique_sec.result | 4 +++ .../suite/rocksdb/r/unique_sec_rev_cf.result | 4 +++ .../rocksdb/t/range_locking_seek_for_update.test | 32 ++++++++++++++++++++++ mysql-test/suite/rocksdb/t/unique_sec.inc | 10 ++++++- storage/rocksdb/ha_rocksdb.cc | 25 ++++++++++------- storage/rocksdb/ha_rocksdb.h | 4 ++- 7 files changed, 96 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/rocksdb/r/range_locking_seek_for_update.result b/mysql-test/suite/rocksdb/r/range_locking_seek_for_update.result index 2614e8ce66e..cddf039bc4f 100644 --- a/mysql-test/suite/rocksdb/r/range_locking_seek_for_update.result +++ b/mysql-test/suite/rocksdb/r/range_locking_seek_for_update.result @@ -173,7 +173,7 @@ begin; delete from t1 where pk=7; set debug_sync='now SIGNAL spoiler_inserted'; connection default; -ERROR HY000: Lock wait timeout exceeded; try restarting transaction: +ERROR HY000: Lock wait timeout exceeded; try restarting transaction: Timeout on index: test.t1.PRIMARY rollback; connection con1; rollback; @@ -225,7 +225,7 @@ pk a connection default; begin; select * from t1 order by pk desc limit 2 for update; -ERROR HY000: Lock wait timeout exceeded; try restarting transaction: +ERROR HY000: Lock wait timeout exceeded; try restarting transaction: Timeout on index: test.t1.PRIMARY rollback; connection con1; rollback; @@ -236,10 +236,36 @@ pk a connection default; begin; select * from t1 order by pk desc limit 2 for update; -ERROR HY000: Lock wait timeout exceeded; try restarting transaction: +ERROR HY000: Lock wait timeout exceeded; try restarting transaction: Timeout on index: test.t1.PRIMARY rollback; connection con1; rollback; disconnect con1; connection default; drop table t0,t1; +# +# A test: full table scan doesn't lock gaps +# +create table t1 ( +pk int primary key, +a int +) engine=rocksdb; +insert into t1 values (10,10),(20,20),(30,30); +connect con1,localhost,root,,; +connect con2,localhost,root,,; +connection con1; +begin; +select * from t1 for update; +pk a +10 10 +20 20 +30 30 +connection con2; +insert into t1 values (5,5); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction: Timeout on index: test.t1.PRIMARY +connection con1; +rollback; +disconnect con1; +disconnect con2; +connection default; +drop table t1; diff --git a/mysql-test/suite/rocksdb/r/unique_sec.result b/mysql-test/suite/rocksdb/r/unique_sec.result index 64db56ca78e..a9550164e30 100644 --- a/mysql-test/suite/rocksdb/r/unique_sec.result +++ b/mysql-test/suite/rocksdb/r/unique_sec.result @@ -115,6 +115,10 @@ ERROR 23000: Duplicate entry '37' for key 'id5' UPDATE t1 SET id5=34 WHERE id1=38; ERROR HY000: Lock wait timeout exceeded; try restarting transaction: Timeout on index: test.t1.id5 # NULL values are unique +# (Note: the following UPDATE reads through the whole table without +# finding anything to update. With point locking, this is fine, +# but with range locking it will time out while waiting on a row lock +# that the other transaction is holding) UPDATE t1 SET id5=NULL WHERE value1 > 37; COMMIT; COMMIT; diff --git a/mysql-test/suite/rocksdb/r/unique_sec_rev_cf.result b/mysql-test/suite/rocksdb/r/unique_sec_rev_cf.result index 0ff55ac8d10..e3f592f6284 100644 --- a/mysql-test/suite/rocksdb/r/unique_sec_rev_cf.result +++ b/mysql-test/suite/rocksdb/r/unique_sec_rev_cf.result @@ -115,6 +115,10 @@ ERROR 23000: Duplicate entry '37' for key 'id5' UPDATE t1 SET id5=34 WHERE id1=38; ERROR HY000: Lock wait timeout exceeded; try restarting transaction: Timeout on index: test.t1.id5 # NULL values are unique +# (Note: the following UPDATE reads through the whole table without +# finding anything to update. With point locking, this is fine, +# but with range locking it will time out while waiting on a row lock +# that the other transaction is holding) UPDATE t1 SET id5=NULL WHERE value1 > 37; COMMIT; COMMIT; diff --git a/mysql-test/suite/rocksdb/t/range_locking_seek_for_update.test b/mysql-test/suite/rocksdb/t/range_locking_seek_for_update.test index 0230113eeb1..32590af1799 100644 --- a/mysql-test/suite/rocksdb/t/range_locking_seek_for_update.test +++ b/mysql-test/suite/rocksdb/t/range_locking_seek_for_update.test @@ -254,3 +254,35 @@ rollback; disconnect con1; connection default; drop table t0,t1; + +--echo # +--echo # A test: full table scan doesn't lock gaps +--echo # + +create table t1 ( + pk int primary key, + a int +) engine=rocksdb; + +insert into t1 values (10,10),(20,20),(30,30); + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +connection con1; +begin; + +select * from t1 for update; + +connection con2; + +--error ER_LOCK_WAIT_TIMEOUT +insert into t1 values (5,5); + +connection con1; +rollback; + +disconnect con1; +disconnect con2; +connection default; +drop table t1; diff --git a/mysql-test/suite/rocksdb/t/unique_sec.inc b/mysql-test/suite/rocksdb/t/unique_sec.inc index 2f11cd3b65a..a78a1e1218d 100644 --- a/mysql-test/suite/rocksdb/t/unique_sec.inc +++ b/mysql-test/suite/rocksdb/t/unique_sec.inc @@ -148,8 +148,16 @@ UPDATE t1 SET id5=37 WHERE id1=38; UPDATE t1 SET id5=34 WHERE id1=38; --echo # NULL values are unique +--echo # (Note: the following UPDATE reads through the whole table without +--echo # finding anything to update. With point locking, this is fine, +--echo # but with range locking it will time out while waiting on a row lock +--echo # that the other transaction is holding) +if (`select @@rocksdb_use_range_locking=0`) { UPDATE t1 SET id5=NULL WHERE value1 > 37; - +} +if (`select @@rocksdb_use_range_locking=1`) { +-- echo UPDATE t1 SET id5=NULL WHERE value1 > 37; +} connection con1; COMMIT; diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 2e161dbfb4b..cca9122f9d0 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -8012,14 +8012,18 @@ int ha_rocksdb::read_before_key(const Rdb_key_def &kd, } -inline int iter_status_to_retval(rocksdb::Iterator *it, int not_found_code) { +int ha_rocksdb::iter_status_to_retval(rocksdb::Iterator *it, + const Rdb_key_def &kd, + int not_found_code) { if (it->Valid()) return HA_EXIT_SUCCESS; + rocksdb::Status s= it->status(); - if (s.IsTimedOut()) - return HA_ERR_LOCK_WAIT_TIMEOUT; - //TODO: should we handle other kinds of errors? - return not_found_code; + if (s.ok() || s.IsNotFound()) + return not_found_code; + + Rdb_transaction *&tx = get_tx_from_thd(table->in_use); + return tx->set_status_error(table->in_use, s, kd, m_tbl_def, m_table_handler); } int ha_rocksdb::read_after_key(const Rdb_key_def &kd, @@ -8051,7 +8055,7 @@ int ha_rocksdb::read_after_key(const Rdb_key_def &kd, return is_valid_iterator(m_scan_it) ? HA_EXIT_SUCCESS : - iter_status_to_retval(m_scan_it, HA_ERR_KEY_NOT_FOUND); + iter_status_to_retval(m_scan_it, kd, HA_ERR_KEY_NOT_FOUND); } int ha_rocksdb::position_to_correct_key( @@ -10774,7 +10778,8 @@ void ha_rocksdb::setup_iterator_for_rnd_scan() { rocksdb::Slice table_key((const char *)m_pk_packed_tuple, key_size); - setup_scan_iterator(*m_pk_descr, &table_key, false, key_start_matching_bytes); + setup_scan_iterator(*m_pk_descr, &table_key, false, key_start_matching_bytes, + (m_lock_rows != RDB_LOCK_NONE) && rocksdb_use_range_locking); m_scan_it->Seek(table_key); m_skip_scan_it_next_call = true; } @@ -10864,7 +10869,7 @@ int ha_rocksdb::rnd_next_with_direction(uchar *const buf, bool move_forward) { In this case, we should return EOF. */ rc = HA_ERR_END_OF_FILE; - DBUG_RETURN(m_scan_it ? iter_status_to_retval(m_scan_it, rc) : rc); + DBUG_RETURN(m_scan_it ? iter_status_to_retval(m_scan_it, *m_pk_descr, rc) : rc); } for (;;) { @@ -10885,7 +10890,7 @@ int ha_rocksdb::rnd_next_with_direction(uchar *const buf, bool move_forward) { } if (!is_valid_iterator(m_scan_it)) { - rc = iter_status_to_retval(m_scan_it, HA_ERR_END_OF_FILE); + rc = iter_status_to_retval(m_scan_it, *m_pk_descr, HA_ERR_END_OF_FILE); break; } @@ -14051,7 +14056,7 @@ static void show_rocksdb_stall_vars(THD *thd, SHOW_VAR *var, char *buff) { } // -// psergey: lock tree escalation count status variable. +// Lock Tree Status variables // static longlong rocksdb_locktree_escalation_count=1234; static longlong rocksdb_locktree_current_lock_memory=0; diff --git a/storage/rocksdb/ha_rocksdb.h b/storage/rocksdb/ha_rocksdb.h index a285e74e728..272dc9973d4 100644 --- a/storage/rocksdb/ha_rocksdb.h +++ b/storage/rocksdb/ha_rocksdb.h @@ -321,7 +321,7 @@ class ha_rocksdb : public my_core::handler { rocksdb::Slice *upper_bound_slice); void setup_scan_iterator(const Rdb_key_def &kd, rocksdb::Slice *slice, const bool use_all_keys, const uint eq_cond_len, - bool use_locking_iterator=false) + bool use_locking_iterator) MY_ATTRIBUTE((__nonnull__)); int set_range_lock(Rdb_transaction *tx, @@ -1031,6 +1031,8 @@ class ha_rocksdb : public my_core::handler { bool m_in_rpl_update_rows; bool m_force_skip_unique_check; + + int iter_status_to_retval(rocksdb::Iterator *it, const Rdb_key_def &kd, int not_found_code); }; /*
participants (1)
-
psergey