YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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