any_lock_manager_test.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Copyright (c) Meta Platforms, Inc. and affiliates.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #pragma once
  6. #include "utilities/transactions/lock/point/point_lock_manager_test.h"
  7. namespace ROCKSDB_NAMESPACE {
  8. using init_func_t = void (*)(PointLockManagerTest*);
  9. class AnyLockManagerTest : public PointLockManagerTest,
  10. public testing::WithParamInterface<init_func_t> {
  11. public:
  12. void SetUp() override {
  13. // If a custom setup function was provided, use it. Otherwise, use what we
  14. // have inherited.
  15. auto init_func = GetParam();
  16. if (init_func) {
  17. (*init_func)(this);
  18. } else {
  19. PointLockManagerTest::SetUp();
  20. }
  21. }
  22. };
  23. TEST_P(AnyLockManagerTest, ReentrantExclusiveLock) {
  24. // Tests that a txn can acquire exclusive lock on the same key repeatedly.
  25. MockColumnFamilyHandle cf(1);
  26. locker_->AddColumnFamily(&cf);
  27. auto txn = NewTxn();
  28. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, true));
  29. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, true));
  30. // Cleanup
  31. locker_->UnLock(txn, 1, "k", env_);
  32. delete txn;
  33. }
  34. TEST_P(AnyLockManagerTest, ReentrantSharedLock) {
  35. // Tests that a txn can acquire shared lock on the same key repeatedly.
  36. MockColumnFamilyHandle cf(1);
  37. locker_->AddColumnFamily(&cf);
  38. auto txn = NewTxn();
  39. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, false));
  40. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, false));
  41. // Cleanup
  42. if (dynamic_cast<PointLockManager*>(locker_.get()) != nullptr &&
  43. dynamic_cast<PerKeyPointLockManager*>(locker_.get()) == nullptr) {
  44. // PointLockManager would create 2 entries in the lock manager, so it needs
  45. // to unlock it twice.
  46. locker_->UnLock(txn, 1, "k", env_);
  47. }
  48. locker_->UnLock(txn, 1, "k", env_);
  49. delete txn;
  50. }
  51. TEST_P(AnyLockManagerTest, LockUpgrade) {
  52. // Tests that a txn can upgrade from a shared lock to an exclusive lock.
  53. MockColumnFamilyHandle cf(1);
  54. locker_->AddColumnFamily(&cf);
  55. auto txn = NewTxn();
  56. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, false));
  57. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, true));
  58. // Cleanup
  59. locker_->UnLock(txn, 1, "k", env_);
  60. delete txn;
  61. }
  62. TEST_P(AnyLockManagerTest, LockDowngrade) {
  63. // Tests that a txn can acquire a shared lock after acquiring an exclusive
  64. // lock on the same key.
  65. MockColumnFamilyHandle cf(1);
  66. locker_->AddColumnFamily(&cf);
  67. auto txn = NewTxn();
  68. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, true));
  69. ASSERT_OK(locker_->TryLock(txn, 1, "k", env_, false));
  70. // Cleanup
  71. locker_->UnLock(txn, 1, "k", env_);
  72. delete txn;
  73. }
  74. TEST_P(AnyLockManagerTest, LockConflict) {
  75. // Tests that lock conflicts lead to lock timeout.
  76. MockColumnFamilyHandle cf(1);
  77. locker_->AddColumnFamily(&cf);
  78. auto txn1 = NewTxn();
  79. auto txn2 = NewTxn();
  80. {
  81. // exclusive-exclusive conflict.
  82. ASSERT_OK(locker_->TryLock(txn1, 1, "k1", env_, true));
  83. auto s = locker_->TryLock(txn2, 1, "k1", env_, true);
  84. ASSERT_TRUE(s.IsTimedOut());
  85. }
  86. {
  87. // exclusive-shared conflict.
  88. ASSERT_OK(locker_->TryLock(txn1, 1, "k2", env_, true));
  89. auto s = locker_->TryLock(txn2, 1, "k2", env_, false);
  90. ASSERT_TRUE(s.IsTimedOut());
  91. }
  92. {
  93. // shared-exclusive conflict.
  94. ASSERT_OK(locker_->TryLock(txn1, 1, "k2", env_, false));
  95. auto s = locker_->TryLock(txn2, 1, "k2", env_, true);
  96. ASSERT_TRUE(s.IsTimedOut());
  97. }
  98. // Cleanup
  99. locker_->UnLock(txn1, 1, "k1", env_);
  100. locker_->UnLock(txn1, 1, "k2", env_);
  101. delete txn1;
  102. delete txn2;
  103. }
  104. TEST_P(AnyLockManagerTest, SharedLocks) {
  105. // Tests that shared locks can be concurrently held by multiple transactions.
  106. MockColumnFamilyHandle cf(1);
  107. locker_->AddColumnFamily(&cf);
  108. auto txn1 = NewTxn();
  109. auto txn2 = NewTxn();
  110. ASSERT_OK(locker_->TryLock(txn1, 1, "k", env_, false));
  111. ASSERT_OK(locker_->TryLock(txn2, 1, "k", env_, false));
  112. // Cleanup
  113. locker_->UnLock(txn1, 1, "k", env_);
  114. locker_->UnLock(txn2, 1, "k", env_);
  115. delete txn1;
  116. delete txn2;
  117. }
  118. TEST_P(AnyLockManagerTest, Deadlock) {
  119. // Tests that deadlock can be detected.
  120. // Deadlock scenario:
  121. // txn1 exclusively locks k1, and wants to lock k2;
  122. // txn2 exclusively locks k2, and wants to lock k1.
  123. MockColumnFamilyHandle cf(1);
  124. locker_->AddColumnFamily(&cf);
  125. TransactionOptions txn_opt;
  126. // disable dead lock timeout, so that the dead lock detection behavior is
  127. // consistent. This prevents the test to be flaky
  128. txn_opt.deadlock_timeout_us = 0;
  129. txn_opt.deadlock_detect = true;
  130. txn_opt.lock_timeout = 1000000;
  131. auto txn1 = NewTxn(txn_opt);
  132. auto txn2 = NewTxn(txn_opt);
  133. ASSERT_OK(locker_->TryLock(txn1, 1, "k1", env_, true));
  134. ASSERT_OK(locker_->TryLock(txn2, 1, "k2", env_, true));
  135. // txn1 tries to lock k2, will be blocked.
  136. port::Thread t;
  137. BlockUntilWaitingTxn(wait_sync_point_name_, t, [&]() {
  138. // block because txn2 is holding a lock on k2.
  139. ASSERT_OK(locker_->TryLock(txn1, 1, "k2", env_, true));
  140. });
  141. auto s = locker_->TryLock(txn2, 1, "k1", env_, true);
  142. ASSERT_TRUE(s.IsBusy());
  143. ASSERT_EQ(s.subcode(), Status::SubCode::kDeadlock);
  144. std::vector<DeadlockPath> deadlock_paths = locker_->GetDeadlockInfoBuffer();
  145. ASSERT_EQ(deadlock_paths.size(), 1u);
  146. ASSERT_FALSE(deadlock_paths[0].limit_exceeded);
  147. std::vector<DeadlockInfo> deadlocks = deadlock_paths[0].path;
  148. ASSERT_EQ(deadlocks.size(), 2u);
  149. ASSERT_EQ(deadlocks[0].m_txn_id, txn1->GetID());
  150. ASSERT_EQ(deadlocks[0].m_cf_id, 1u);
  151. ASSERT_TRUE(deadlocks[0].m_exclusive);
  152. ASSERT_EQ(deadlocks[0].m_waiting_key, "k2");
  153. ASSERT_EQ(deadlocks[1].m_txn_id, txn2->GetID());
  154. ASSERT_EQ(deadlocks[1].m_cf_id, 1u);
  155. ASSERT_TRUE(deadlocks[1].m_exclusive);
  156. ASSERT_EQ(deadlocks[1].m_waiting_key, "k1");
  157. locker_->UnLock(txn2, 1, "k2", env_);
  158. t.join();
  159. // Cleanup
  160. locker_->UnLock(txn1, 1, "k1", env_);
  161. locker_->UnLock(txn1, 1, "k2", env_);
  162. delete txn2;
  163. delete txn1;
  164. }
  165. TEST_P(AnyLockManagerTest, GetWaitingTxns_MultipleTxns) {
  166. MockColumnFamilyHandle cf(1);
  167. locker_->AddColumnFamily(&cf);
  168. auto txn1 = NewTxn();
  169. ASSERT_OK(locker_->TryLock(txn1, 1, "k", env_, false));
  170. auto txn2 = NewTxn();
  171. ASSERT_OK(locker_->TryLock(txn2, 1, "k", env_, false));
  172. auto txn3 = NewTxn();
  173. txn3->SetLockTimeout(10000);
  174. port::Thread t1;
  175. BlockUntilWaitingTxn(wait_sync_point_name_, t1, [&]() {
  176. ASSERT_OK(locker_->TryLock(txn3, 1, "k", env_, true));
  177. locker_->UnLock(txn3, 1, "k", env_);
  178. });
  179. // Ok, now txn3 is waiting for lock on "k", which is owned by two
  180. // transactions. Check that GetWaitingTxns reports this correctly
  181. uint32_t wait_cf_id;
  182. std::string wait_key;
  183. auto waiters = txn3->GetWaitingTxns(&wait_cf_id, &wait_key);
  184. ASSERT_EQ(wait_cf_id, 1u);
  185. ASSERT_EQ(wait_key, "k");
  186. ASSERT_EQ(waiters.size(), 2);
  187. bool waits_correct =
  188. (waiters[0] == txn1->GetID() && waiters[1] == txn2->GetID()) ||
  189. (waiters[1] == txn1->GetID() && waiters[0] == txn2->GetID());
  190. ASSERT_EQ(waits_correct, true);
  191. // Release locks so txn3 can proceed with execution
  192. locker_->UnLock(txn1, 1, "k", env_);
  193. locker_->UnLock(txn2, 1, "k", env_);
  194. // Wait until txn3 finishes
  195. t1.join();
  196. delete txn1;
  197. delete txn2;
  198. delete txn3;
  199. }
  200. } // namespace ROCKSDB_NAMESPACE