// Copyright (c) Meta Platforms, Inc. and affiliates. // // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #pragma once #include "file/file_util.h" #include "rocksdb/utilities/transaction_db.h" #include "test_util/testharness.h" #include "utilities/transactions/lock/point/point_lock_manager.h" #include "utilities/transactions/lock/point/point_lock_manager_test_common.h" #include "utilities/transactions/pessimistic_transaction_db.h" namespace ROCKSDB_NAMESPACE { class PointLockManagerTest : public testing::Test { public: void init() { env_ = Env::Default(); db_dir_ = test::PerThreadDBPath("point_lock_manager_test"); ASSERT_OK(env_->CreateDir(db_dir_)); Options opt; opt.create_if_missing = true; // Reduce the number of stripes to 4 to increase contention in test txndb_opt_.num_stripes = 4; txndb_opt_.transaction_lock_timeout = 0; ASSERT_OK(TransactionDB::Open(opt, txndb_opt_, db_dir_, &db_)); wait_sync_point_name_ = "PointLockManager::AcquireWithTimeout:WaitingTxn"; } void SetUp() override { init(); // CAUTION: This test creates a separate lock manager object (right, NOT // the one that the TransactionDB is using!), and runs tests on it. locker_.reset(new PointLockManager( static_cast(db_), txndb_opt_)); } void TearDown() override { std::string errmsg; auto no_lock_held = verifyNoLocksHeld(locker_, errmsg); ASSERT_TRUE(no_lock_held) << errmsg; delete db_; EXPECT_OK(DestroyDir(env_, db_dir_)); } PessimisticTransaction* NewTxn( TransactionOptions txn_opt = TransactionOptions()) { // override deadlock_timeout_us; txn_opt.deadlock_timeout_us = deadlock_timeout_us; Transaction* txn = db_->BeginTransaction(WriteOptions(), txn_opt); return static_cast(txn); } int64_t deadlock_timeout_us = 0; void UsePerKeyPointLockManager() { locker_.reset(new PerKeyPointLockManager( static_cast(db_), txndb_opt_)); } protected: Env* env_; TransactionDBOptions txndb_opt_; std::shared_ptr locker_; const char* wait_sync_point_name_; friend void PointLockManagerTestExternalSetup(PointLockManagerTest*); std::string db_dir_; TransactionDB* db_; }; void BlockUntilWaitingTxn(const char* sync_point_name, port::Thread& t, std::function f) { std::atomic reached(false); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( sync_point_name, [&](void* /*arg*/) { reached.store(true); }); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); t = port::Thread(f); // timeout after 30 seconds, so test does not hang forever // 30 seconds should be enough for the test to reach the expected state // without causing too much flakiness for (int i = 0; i < 3000; i++) { if (reached.load()) { break; } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } ASSERT_TRUE(reached.load()); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); } } // namespace ROCKSDB_NAMESPACE