1 //  Copyright (c) 2020-present, Facebook, Inc.  All rights reserved.
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 
6 #ifndef ROCKSDB_LITE
7 
8 #include "utilities/transactions/lock/point/point_lock_manager_test.h"
9 
10 namespace ROCKSDB_NAMESPACE {
11 
12 // This test is not applicable for Range Lock manager as Range Lock Manager
13 // operates on Column Families, not their ids.
TEST_F(PointLockManagerTest,LockNonExistingColumnFamily)14 TEST_F(PointLockManagerTest, LockNonExistingColumnFamily) {
15   MockColumnFamilyHandle cf(1024);
16   locker_->RemoveColumnFamily(&cf);
17   auto txn = NewTxn();
18   auto s = locker_->TryLock(txn, 1024, "k", env_, true);
19   ASSERT_TRUE(s.IsInvalidArgument());
20   ASSERT_STREQ(s.getState(), "Column family id not found: 1024");
21   delete txn;
22 }
23 
TEST_F(PointLockManagerTest,LockStatus)24 TEST_F(PointLockManagerTest, LockStatus) {
25   MockColumnFamilyHandle cf1(1024), cf2(2048);
26   locker_->AddColumnFamily(&cf1);
27   locker_->AddColumnFamily(&cf2);
28 
29   auto txn1 = NewTxn();
30   ASSERT_OK(locker_->TryLock(txn1, 1024, "k1", env_, true));
31   ASSERT_OK(locker_->TryLock(txn1, 2048, "k1", env_, true));
32 
33   auto txn2 = NewTxn();
34   ASSERT_OK(locker_->TryLock(txn2, 1024, "k2", env_, false));
35   ASSERT_OK(locker_->TryLock(txn2, 2048, "k2", env_, false));
36 
37   auto s = locker_->GetPointLockStatus();
38   ASSERT_EQ(s.size(), 4u);
39   for (uint32_t cf_id : {1024, 2048}) {
40     ASSERT_EQ(s.count(cf_id), 2u);
41     auto range = s.equal_range(cf_id);
42     for (auto it = range.first; it != range.second; it++) {
43       ASSERT_TRUE(it->second.key == "k1" || it->second.key == "k2");
44       if (it->second.key == "k1") {
45         ASSERT_EQ(it->second.exclusive, true);
46         ASSERT_EQ(it->second.ids.size(), 1u);
47         ASSERT_EQ(it->second.ids[0], txn1->GetID());
48       } else if (it->second.key == "k2") {
49         ASSERT_EQ(it->second.exclusive, false);
50         ASSERT_EQ(it->second.ids.size(), 1u);
51         ASSERT_EQ(it->second.ids[0], txn2->GetID());
52       }
53     }
54   }
55 
56   // Cleanup
57   locker_->UnLock(txn1, 1024, "k1", env_);
58   locker_->UnLock(txn1, 2048, "k1", env_);
59   locker_->UnLock(txn2, 1024, "k2", env_);
60   locker_->UnLock(txn2, 2048, "k2", env_);
61 
62   delete txn1;
63   delete txn2;
64 }
65 
TEST_F(PointLockManagerTest,UnlockExclusive)66 TEST_F(PointLockManagerTest, UnlockExclusive) {
67   MockColumnFamilyHandle cf(1);
68   locker_->AddColumnFamily(&cf);
69 
70   auto txn1 = NewTxn();
71   ASSERT_OK(locker_->TryLock(txn1, 1, "k", env_, true));
72   locker_->UnLock(txn1, 1, "k", env_);
73 
74   auto txn2 = NewTxn();
75   ASSERT_OK(locker_->TryLock(txn2, 1, "k", env_, true));
76 
77   // Cleanup
78   locker_->UnLock(txn2, 1, "k", env_);
79 
80   delete txn1;
81   delete txn2;
82 }
83 
TEST_F(PointLockManagerTest,UnlockShared)84 TEST_F(PointLockManagerTest, UnlockShared) {
85   MockColumnFamilyHandle cf(1);
86   locker_->AddColumnFamily(&cf);
87 
88   auto txn1 = NewTxn();
89   ASSERT_OK(locker_->TryLock(txn1, 1, "k", env_, false));
90   locker_->UnLock(txn1, 1, "k", env_);
91 
92   auto txn2 = NewTxn();
93   ASSERT_OK(locker_->TryLock(txn2, 1, "k", env_, true));
94 
95   // Cleanup
96   locker_->UnLock(txn2, 1, "k", env_);
97 
98   delete txn1;
99   delete txn2;
100 }
101 
102 // This test doesn't work with Range Lock Manager, because Range Lock Manager
103 // doesn't support deadlock_detect_depth.
104 
TEST_F(PointLockManagerTest,DeadlockDepthExceeded)105 TEST_F(PointLockManagerTest, DeadlockDepthExceeded) {
106   // Tests that when detecting deadlock, if the detection depth is exceeded,
107   // it's also viewed as deadlock.
108   MockColumnFamilyHandle cf(1);
109   locker_->AddColumnFamily(&cf);
110   TransactionOptions txn_opt;
111   txn_opt.deadlock_detect = true;
112   txn_opt.deadlock_detect_depth = 1;
113   txn_opt.lock_timeout = 1000000;
114   auto txn1 = NewTxn(txn_opt);
115   auto txn2 = NewTxn(txn_opt);
116   auto txn3 = NewTxn(txn_opt);
117   auto txn4 = NewTxn(txn_opt);
118   // "a ->(k) b" means transaction a is waiting for transaction b to release
119   // the held lock on key k.
120   // txn4 ->(k3) -> txn3 ->(k2) txn2 ->(k1) txn1
121   // txn3's deadlock detection will exceed the detection depth 1,
122   // which will be viewed as a deadlock.
123   // NOTE:
124   // txn4 ->(k3) -> txn3 must be set up before
125   // txn3 ->(k2) -> txn2, because to trigger deadlock detection for txn3,
126   // it must have another txn waiting on it, which is txn4 in this case.
127   ASSERT_OK(locker_->TryLock(txn1, 1, "k1", env_, true));
128 
129   port::Thread t1 = BlockUntilWaitingTxn(wait_sync_point_name_, [&]() {
130     ASSERT_OK(locker_->TryLock(txn2, 1, "k2", env_, true));
131     // block because txn1 is holding a lock on k1.
132     locker_->TryLock(txn2, 1, "k1", env_, true);
133   });
134 
135   ASSERT_OK(locker_->TryLock(txn3, 1, "k3", env_, true));
136 
137   port::Thread t2 = BlockUntilWaitingTxn(wait_sync_point_name_, [&]() {
138     // block because txn3 is holding a lock on k1.
139     locker_->TryLock(txn4, 1, "k3", env_, true);
140   });
141 
142   auto s = locker_->TryLock(txn3, 1, "k2", env_, true);
143   ASSERT_TRUE(s.IsBusy());
144   ASSERT_EQ(s.subcode(), Status::SubCode::kDeadlock);
145 
146   std::vector<DeadlockPath> deadlock_paths = locker_->GetDeadlockInfoBuffer();
147   ASSERT_EQ(deadlock_paths.size(), 1u);
148   ASSERT_TRUE(deadlock_paths[0].limit_exceeded);
149 
150   locker_->UnLock(txn1, 1, "k1", env_);
151   locker_->UnLock(txn3, 1, "k3", env_);
152   t1.join();
153   t2.join();
154 
155   delete txn4;
156   delete txn3;
157   delete txn2;
158   delete txn1;
159 }
160 
161 INSTANTIATE_TEST_CASE_P(PointLockManager, AnyLockManagerTest,
162                         ::testing::Values(nullptr));
163 
164 }  // namespace ROCKSDB_NAMESPACE
165 
main(int argc,char ** argv)166 int main(int argc, char** argv) {
167   ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
168   ::testing::InitGoogleTest(&argc, argv);
169   return RUN_ALL_TESTS();
170 }
171 
172 #else
173 #include <stdio.h>
174 
main(int,char **)175 int main(int /*argc*/, char** /*argv*/) {
176   fprintf(stderr,
177           "SKIPPED because Transactions are not supported in ROCKSDB_LITE\n");
178   return 0;
179 }
180 
181 #endif  // ROCKSDB_LITE
182