/Users/deen/code/yugabyte-db/src/yb/docdb/shared_lock_manager-test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <atomic> |
15 | | #include <mutex> |
16 | | #include <stack> |
17 | | #include <thread> |
18 | | |
19 | | #include "yb/docdb/lock_batch.h" |
20 | | #include "yb/docdb/shared_lock_manager.h" |
21 | | |
22 | | #include "yb/rpc/thread_pool.h" |
23 | | |
24 | | #include "yb/util/ref_cnt_buffer.h" |
25 | | #include "yb/util/result.h" |
26 | | #include "yb/util/test_macros.h" |
27 | | #include "yb/util/test_util.h" |
28 | | |
29 | | using namespace std::literals; |
30 | | |
31 | | using std::string; |
32 | | using std::vector; |
33 | | using std::stack; |
34 | | using std::thread; |
35 | | |
36 | | DECLARE_bool(dump_lock_keys); |
37 | | |
38 | | namespace yb { |
39 | | namespace docdb { |
40 | | |
41 | | const RefCntPrefix kKey1("foo"s); |
42 | | const RefCntPrefix kKey2("bar"s); |
43 | | |
44 | | class SharedLockManagerTest : public YBTest { |
45 | | protected: |
46 | | SharedLockManagerTest(); |
47 | | |
48 | | protected: |
49 | | SharedLockManager lm_; |
50 | | |
51 | 0 | LockBatch TestLockBatch(CoarseTimePoint deadline = CoarseTimePoint::max()) { |
52 | 0 | return LockBatch(&lm_, { |
53 | 0 | {kKey1, IntentTypeSet({IntentType::kStrongWrite, IntentType::kStrongRead})}, |
54 | 0 | {kKey2, IntentTypeSet({IntentType::kStrongWrite, IntentType::kStrongRead})}}, |
55 | 0 | deadline); |
56 | 0 | } |
57 | | }; |
58 | | |
59 | 0 | SharedLockManagerTest::SharedLockManagerTest() { |
60 | 0 | } |
61 | | |
62 | 0 | TEST_F(SharedLockManagerTest, LockBatchAutoUnlockTest) { |
63 | 0 | for (int i = 0; i < 2; ++i) { |
64 | 0 | auto lb = TestLockBatch(); |
65 | 0 | EXPECT_EQ(2, lb.size()); |
66 | 0 | EXPECT_FALSE(lb.empty()); |
67 | | // The locks get unlocked on scope exit. |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | 0 | TEST_F(SharedLockManagerTest, LockBatchMoveConstructor) { |
72 | 0 | LockBatch lb = TestLockBatch(); |
73 | 0 | EXPECT_EQ(2, lb.size()); |
74 | 0 | EXPECT_FALSE(lb.empty()); |
75 | 0 | ASSERT_OK(lb.status()); |
76 | |
|
77 | 0 | LockBatch lb_fail = TestLockBatch(CoarseMonoClock::now() + 10ms); |
78 | 0 | ASSERT_FALSE(lb_fail.status().ok()); |
79 | 0 | ASSERT_TRUE(lb_fail.empty()); |
80 | |
|
81 | 0 | LockBatch lb2(std::move(lb)); |
82 | 0 | EXPECT_EQ(2, lb2.size()); |
83 | 0 | EXPECT_FALSE(lb2.empty()); |
84 | 0 | ASSERT_OK(lb2.status()); |
85 | | |
86 | | // lb has been moved from and is now empty |
87 | 0 | EXPECT_EQ(0, lb.size()); |
88 | 0 | EXPECT_TRUE(lb.empty()); |
89 | 0 | ASSERT_OK(lb.status()); |
90 | |
|
91 | 0 | LockBatch lb_fail2(std::move(lb_fail)); |
92 | 0 | ASSERT_FALSE(lb_fail2.status().ok()); |
93 | 0 | ASSERT_TRUE(lb_fail2.empty()); |
94 | 0 | } |
95 | | |
96 | | TEST_F(SharedLockManagerTest, LockBatchMoveAssignment) { |
97 | | LockBatch lb = TestLockBatch(); |
98 | | |
99 | | LockBatch lb_fail = TestLockBatch(CoarseMonoClock::now() + 10ms); |
100 | | ASSERT_FALSE(lb_fail.status().ok()); |
101 | | ASSERT_TRUE(lb_fail.empty()); |
102 | | |
103 | | LockBatch lb2 = std::move(lb); |
104 | | EXPECT_EQ(2, lb2.size()); |
105 | | EXPECT_FALSE(lb2.empty()); |
106 | | ASSERT_OK(lb2.status()); |
107 | | |
108 | | // lb has been moved from and is now empty |
109 | | EXPECT_EQ(0, lb.size()); |
110 | | EXPECT_TRUE(lb.empty()); |
111 | | |
112 | | LockBatch lb_fail2 = std::move(lb_fail); |
113 | | ASSERT_FALSE(lb_fail2.status().ok()); |
114 | | ASSERT_TRUE(lb_fail2.empty()); |
115 | | } |
116 | | |
117 | 0 | TEST_F(SharedLockManagerTest, LockBatchReset) { |
118 | 0 | LockBatch lb = TestLockBatch(); |
119 | 0 | lb.Reset(); |
120 | |
|
121 | 0 | EXPECT_EQ(0, lb.size()); |
122 | 0 | EXPECT_TRUE(lb.empty()); |
123 | 0 | } |
124 | | |
125 | | // Launch pairs of threads. Each pair tries to lock/unlock on the same key sequence. |
126 | | // This catches bug in SharedLockManager when condition is waited incorrectly. |
127 | 0 | TEST_F(SharedLockManagerTest, QuickLockUnlock) { |
128 | 0 | const auto kThreads = 2 * 32; // Should be even |
129 | |
|
130 | 0 | std::atomic<bool> stop_requested{false}; |
131 | 0 | std::vector<std::thread> threads; |
132 | 0 | std::atomic<size_t> finished_threads{0}; |
133 | 0 | while (threads.size() != kThreads) { |
134 | 0 | size_t pair_idx = threads.size() / 2; |
135 | 0 | threads.emplace_back([this, &stop_requested, &finished_threads, pair_idx] { |
136 | 0 | int i = 0; |
137 | 0 | while (!stop_requested.load(std::memory_order_acquire)) { |
138 | 0 | RefCntPrefix key(Format("key_$0_$1", pair_idx, i)); |
139 | 0 | LockBatch lb(&lm_, |
140 | 0 | {{key, IntentTypeSet({IntentType::kStrongWrite, IntentType::kStrongRead})}}, |
141 | 0 | CoarseTimePoint::max()); |
142 | 0 | ++i; |
143 | 0 | } |
144 | 0 | finished_threads.fetch_add(1, std::memory_order_acq_rel); |
145 | 0 | }); |
146 | 0 | } |
147 | |
|
148 | 0 | std::this_thread::sleep_for(30s); |
149 | 0 | LOG(INFO) << "Requesting stop"; |
150 | 0 | stop_requested.store(true, std::memory_order_release); |
151 | |
|
152 | 0 | ASSERT_OK(WaitFor( |
153 | 0 | [&finished_threads] { |
154 | 0 | return finished_threads.load(std::memory_order_acquire) == kThreads; |
155 | 0 | }, |
156 | 0 | 3s, |
157 | 0 | "All threads finished")); |
158 | |
|
159 | 0 | for (auto& thread : threads) { |
160 | 0 | thread.join(); |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | 0 | TEST_F(SharedLockManagerTest, LockConflicts) { |
165 | 0 | rpc::ThreadPool tp(rpc::ThreadPoolOptions{"test_pool"s, 10, 1}); |
166 | |
|
167 | 0 | for (size_t idx1 = 0; idx1 != kIntentTypeSetMapSize; ++idx1) { |
168 | 0 | IntentTypeSet set1(idx1); |
169 | 0 | SCOPED_TRACE(Format("Set1: $0", set1)); |
170 | 0 | for (size_t idx2 = 0; idx2 != kIntentTypeSetMapSize; ++idx2) { |
171 | 0 | IntentTypeSet set2(idx2); |
172 | 0 | SCOPED_TRACE(Format("Set2: $0", set2)); |
173 | 0 | LockBatch lb1(&lm_, {{kKey1, set1}}, CoarseTimePoint::max()); |
174 | 0 | ASSERT_OK(lb1.status()); |
175 | 0 | LockBatch lb2(&lm_, {{kKey1, set2}}, CoarseMonoClock::now()); |
176 | 0 | if (lb2.status().ok()) { |
177 | | // Lock on set2 was taken fast enough, it means that sets should NOT conflict. |
178 | 0 | ASSERT_FALSE(IntentTypeSetsConflict(set1, set2)); |
179 | 0 | } else { |
180 | | // Lock on set2 was taken not taken for too long, it means that sets should conflict. |
181 | 0 | ASSERT_TRUE(IntentTypeSetsConflict(set1, set2)); |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } |
185 | |
|
186 | 0 | tp.Shutdown(); |
187 | 0 | } |
188 | | |
189 | 0 | TEST_F(SharedLockManagerTest, DumpKeys) { |
190 | 0 | FLAGS_dump_lock_keys = true; |
191 | |
|
192 | 0 | auto lb1 = TestLockBatch(); |
193 | 0 | ASSERT_OK(lb1.status()); |
194 | 0 | auto lb2 = TestLockBatch(CoarseMonoClock::now() + 10ms); |
195 | 0 | ASSERT_NOK(lb2.status()); |
196 | 0 | ASSERT_STR_CONTAINS( |
197 | 0 | lb2.status().ToString(), |
198 | 0 | "[{ key: 666F6F intent_types: [kStrongRead, kStrongWrite] }, " |
199 | 0 | "{ key: 626172 intent_types: [kStrongRead, kStrongWrite] }]"); |
200 | 0 | } |
201 | | |
202 | | } // namespace docdb |
203 | | } // namespace yb |