2005c88a75
Summary: This is an implementation of non-exclusive locks for pessimistic transactions. It is relatively simple and does not prevent starvation (ie. it's possible that request for exclusive access will never be granted if there are always threads holding shared access). It is done by changing `KeyLockInfo` to hold an set a transaction ids, instead of just one, and adding a flag specifying whether this lock is currently held with exclusive access or not. Some implementation notes: - Some lock diagnostic functions had to be updated to return a set of transaction ids for a given lock, eg. `GetWaitingTxn` and `GetLockStatusData`. - Deadlock detection is a bit more complicated since a transaction can now wait on multiple other transactions. A BFS is done in this case, and deadlock detection depth is now just a limit on the number of transactions we visit. - Expirable transactions do not work efficiently with shared locks at the moment, but that's okay for now. Closes https://github.com/facebook/rocksdb/pull/1573 Differential Revision: D4239097 Pulled By: lth fbshipit-source-id: da7c074
132 lines
4.7 KiB
C++
132 lines
4.7 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under the BSD-style license found in the
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
#pragma once
|
|
#ifndef ROCKSDB_LITE
|
|
|
|
#include <chrono>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "rocksdb/utilities/transaction.h"
|
|
#include "util/autovector.h"
|
|
#include "util/hash_map.h"
|
|
#include "util/instrumented_mutex.h"
|
|
#include "util/thread_local.h"
|
|
#include "utilities/transactions/transaction_impl.h"
|
|
|
|
namespace rocksdb {
|
|
|
|
class ColumnFamilyHandle;
|
|
struct LockInfo;
|
|
struct LockMap;
|
|
struct LockMapStripe;
|
|
|
|
class Slice;
|
|
class TransactionDBImpl;
|
|
|
|
class TransactionLockMgr {
|
|
public:
|
|
TransactionLockMgr(TransactionDB* txn_db, size_t default_num_stripes,
|
|
int64_t max_num_locks,
|
|
std::shared_ptr<TransactionDBMutexFactory> factory);
|
|
|
|
~TransactionLockMgr();
|
|
|
|
// Creates a new LockMap for this column family. Caller should guarantee
|
|
// that this column family does not already exist.
|
|
void AddColumnFamily(uint32_t column_family_id);
|
|
|
|
// Deletes the LockMap for this column family. Caller should guarantee that
|
|
// this column family is no longer in use.
|
|
void RemoveColumnFamily(uint32_t column_family_id);
|
|
|
|
// Attempt to lock key. If OK status is returned, the caller is responsible
|
|
// for calling UnLock() on this key.
|
|
Status TryLock(TransactionImpl* txn, uint32_t column_family_id,
|
|
const std::string& key, Env* env, bool exclusive);
|
|
|
|
// Unlock a key locked by TryLock(). txn must be the same Transaction that
|
|
// locked this key.
|
|
void UnLock(const TransactionImpl* txn, const TransactionKeyMap* keys,
|
|
Env* env);
|
|
void UnLock(TransactionImpl* txn, uint32_t column_family_id,
|
|
const std::string& key, Env* env);
|
|
|
|
using LockStatusData = std::unordered_multimap<uint32_t, KeyLockInfo>;
|
|
LockStatusData GetLockStatusData();
|
|
|
|
private:
|
|
TransactionDBImpl* txn_db_impl_;
|
|
|
|
// Default number of lock map stripes per column family
|
|
const size_t default_num_stripes_;
|
|
|
|
// Limit on number of keys locked per column family
|
|
const int64_t max_num_locks_;
|
|
|
|
// The following lock order must be satisfied in order to avoid deadlocking
|
|
// ourselves.
|
|
// - lock_map_mutex_
|
|
// - stripe mutexes in ascending cf id, ascending stripe order
|
|
// - wait_txn_map_mutex_
|
|
//
|
|
// Must be held when accessing/modifying lock_maps_.
|
|
InstrumentedMutex lock_map_mutex_;
|
|
|
|
// Map of ColumnFamilyId to locked key info
|
|
using LockMaps = std::unordered_map<uint32_t, std::shared_ptr<LockMap>>;
|
|
LockMaps lock_maps_;
|
|
|
|
// Thread-local cache of entries in lock_maps_. This is an optimization
|
|
// to avoid acquiring a mutex in order to look up a LockMap
|
|
std::unique_ptr<ThreadLocalPtr> lock_maps_cache_;
|
|
|
|
// Must be held when modifying wait_txn_map_ and rev_wait_txn_map_.
|
|
std::mutex wait_txn_map_mutex_;
|
|
|
|
// Maps from waitee -> number of waiters.
|
|
HashMap<TransactionID, int> rev_wait_txn_map_;
|
|
// Maps from waiter -> waitee.
|
|
HashMap<TransactionID, autovector<TransactionID>> wait_txn_map_;
|
|
|
|
// Used to allocate mutexes/condvars to use when locking keys
|
|
std::shared_ptr<TransactionDBMutexFactory> mutex_factory_;
|
|
|
|
bool IsLockExpired(TransactionID txn_id, const LockInfo& lock_info, Env* env,
|
|
uint64_t* wait_time);
|
|
|
|
std::shared_ptr<LockMap> GetLockMap(uint32_t column_family_id);
|
|
|
|
Status AcquireWithTimeout(TransactionImpl* txn, LockMap* lock_map,
|
|
LockMapStripe* stripe, uint32_t column_family_id,
|
|
const std::string& key, Env* env, int64_t timeout,
|
|
const LockInfo& lock_info);
|
|
|
|
Status AcquireLocked(LockMap* lock_map, LockMapStripe* stripe,
|
|
const std::string& key, Env* env,
|
|
const LockInfo& lock_info, uint64_t* wait_time,
|
|
autovector<TransactionID>* txn_ids);
|
|
|
|
void UnLockKey(const TransactionImpl* txn, const std::string& key,
|
|
LockMapStripe* stripe, LockMap* lock_map, Env* env);
|
|
|
|
bool IncrementWaiters(const TransactionImpl* txn,
|
|
const autovector<TransactionID>& wait_ids);
|
|
void DecrementWaiters(const TransactionImpl* txn,
|
|
const autovector<TransactionID>& wait_ids);
|
|
void DecrementWaitersImpl(const TransactionImpl* txn,
|
|
const autovector<TransactionID>& wait_ids);
|
|
|
|
// No copying allowed
|
|
TransactionLockMgr(const TransactionLockMgr&);
|
|
void operator=(const TransactionLockMgr&);
|
|
};
|
|
|
|
} // namespace rocksdb
|
|
#endif // ROCKSDB_LITE
|