revision-id: d4579809cc08bb9de785f60ab4a9b6f8fdeb2a99 (fb-prod201903-271-gd4579809cc0) parent(s): 1f738064ee9d3e3d04491ab01b6a6afcd6085136 author: Sergei Petrunia committer: Sergei Petrunia timestamp: 2019-12-15 23:38:32 +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). --- .../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); }; /*