]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
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 | ||
1e59de90 | 8 | #include "utilities/transactions/lock/point/point_lock_manager_test.h" |
20effc67 TL |
9 | |
10 | namespace ROCKSDB_NAMESPACE { | |
11 | ||
1e59de90 TL |
12 | // This test is not applicable for Range Lock manager as Range Lock Manager |
13 | // operates on Column Families, not their ids. | |
20effc67 TL |
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 | ||
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 | ||
1e59de90 TL |
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 | ||
20effc67 TL |
62 | delete txn1; |
63 | delete txn2; | |
64 | } | |
65 | ||
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 | ||
1e59de90 TL |
77 | // Cleanup |
78 | locker_->UnLock(txn2, 1, "k", env_); | |
79 | ||
20effc67 TL |
80 | delete txn1; |
81 | delete txn2; | |
82 | } | |
83 | ||
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 | ||
1e59de90 TL |
95 | // Cleanup |
96 | locker_->UnLock(txn2, 1, "k", env_); | |
20effc67 | 97 | |
20effc67 TL |
98 | delete txn1; |
99 | delete txn2; | |
100 | } | |
101 | ||
1e59de90 TL |
102 | // This test doesn't work with Range Lock Manager, because Range Lock Manager |
103 | // doesn't support deadlock_detect_depth. | |
20effc67 TL |
104 | |
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 | ||
1e59de90 | 129 | port::Thread t1 = BlockUntilWaitingTxn(wait_sync_point_name_, [&]() { |
20effc67 TL |
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 | ||
1e59de90 | 137 | port::Thread t2 = BlockUntilWaitingTxn(wait_sync_point_name_, [&]() { |
20effc67 TL |
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 | ||
1e59de90 TL |
161 | INSTANTIATE_TEST_CASE_P(PointLockManager, AnyLockManagerTest, |
162 | ::testing::Values(nullptr)); | |
163 | ||
20effc67 TL |
164 | } // namespace ROCKSDB_NAMESPACE |
165 | ||
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 | ||
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 |