Add thread-safety documentation to MemTable and related classes
Summary: Other than making some class members private, this is a documentation-only change Test Plan: unit tests Reviewers: sdong, yhchiang, igor Reviewed By: igor Subscribers: dhruba, leveldb Differential Revision: https://reviews.facebook.net/D36567
This commit is contained in:
parent
ee9bdd38a1
commit
84c5bd7eb9
@ -85,24 +85,23 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
|||||||
|
|
||||||
// holds references to memtable, all immutable memtables and version
|
// holds references to memtable, all immutable memtables and version
|
||||||
struct SuperVersion {
|
struct SuperVersion {
|
||||||
|
// Accessing members of this class is not thread-safe and requires external
|
||||||
|
// synchronization (ie db mutex held or on write thread).
|
||||||
MemTable* mem;
|
MemTable* mem;
|
||||||
MemTableListVersion* imm;
|
MemTableListVersion* imm;
|
||||||
Version* current;
|
Version* current;
|
||||||
MutableCFOptions mutable_cf_options;
|
MutableCFOptions mutable_cf_options;
|
||||||
std::atomic<uint32_t> refs;
|
|
||||||
// We need to_delete because during Cleanup(), imm->Unref() returns
|
|
||||||
// all memtables that we need to free through this vector. We then
|
|
||||||
// delete all those memtables outside of mutex, during destruction
|
|
||||||
autovector<MemTable*> to_delete;
|
|
||||||
// Version number of the current SuperVersion
|
// Version number of the current SuperVersion
|
||||||
uint64_t version_number;
|
uint64_t version_number;
|
||||||
|
|
||||||
InstrumentedMutex* db_mutex;
|
InstrumentedMutex* db_mutex;
|
||||||
|
|
||||||
// should be called outside the mutex
|
// should be called outside the mutex
|
||||||
SuperVersion() = default;
|
SuperVersion() = default;
|
||||||
~SuperVersion();
|
~SuperVersion();
|
||||||
SuperVersion* Ref();
|
SuperVersion* Ref();
|
||||||
|
// If Unref() returns true, Cleanup() should be called with mutex held
|
||||||
|
// before deleting this SuperVersion.
|
||||||
bool Unref();
|
bool Unref();
|
||||||
|
|
||||||
// call these two methods with db mutex held
|
// call these two methods with db mutex held
|
||||||
@ -121,6 +120,13 @@ struct SuperVersion {
|
|||||||
static int dummy;
|
static int dummy;
|
||||||
static void* const kSVInUse;
|
static void* const kSVInUse;
|
||||||
static void* const kSVObsolete;
|
static void* const kSVObsolete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<uint32_t> refs;
|
||||||
|
// We need to_delete because during Cleanup(), imm->Unref() returns
|
||||||
|
// all memtables that we need to free through this vector. We then
|
||||||
|
// delete all those memtables outside of mutex, during destruction
|
||||||
|
autovector<MemTable*> to_delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
extern ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||||
|
@ -54,6 +54,19 @@ struct MemTableOptions {
|
|||||||
Logger* info_log;
|
Logger* info_log;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Note: Many of the methods in this class have comments indicating that
|
||||||
|
// external synchromization is required as these methods are not thread-safe.
|
||||||
|
// It is up to higher layers of code to decide how to prevent concurrent
|
||||||
|
// invokation of these methods. This is usually done by acquiring either
|
||||||
|
// the db mutex or the single writer thread.
|
||||||
|
//
|
||||||
|
// Some of these methods are documented to only require external
|
||||||
|
// synchronization if this memtable is immutable. Calling MarkImmutable() is
|
||||||
|
// not sufficient to guarantee immutability. It is up to higher layers of
|
||||||
|
// code to determine if this MemTable can still be modified by other threads.
|
||||||
|
// Eg: The Superversion stores a pointer to the current MemTable (that can
|
||||||
|
// be modified) and a separate list of the MemTables that can no longer be
|
||||||
|
// written to (aka the 'immutable memtables').
|
||||||
class MemTable {
|
class MemTable {
|
||||||
public:
|
public:
|
||||||
struct KeyComparator : public MemTableRep::KeyComparator {
|
struct KeyComparator : public MemTableRep::KeyComparator {
|
||||||
@ -72,13 +85,18 @@ class MemTable {
|
|||||||
const MutableCFOptions& mutable_cf_options,
|
const MutableCFOptions& mutable_cf_options,
|
||||||
WriteBuffer* write_buffer);
|
WriteBuffer* write_buffer);
|
||||||
|
|
||||||
|
// Do not delete this MemTable unless Unref() indicates it not in use.
|
||||||
~MemTable();
|
~MemTable();
|
||||||
|
|
||||||
// Increase reference count.
|
// Increase reference count.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
void Ref() { ++refs_; }
|
void Ref() { ++refs_; }
|
||||||
|
|
||||||
// Drop reference count.
|
// Drop reference count.
|
||||||
// If the refcount goes to zero return this memtable, otherwise return null
|
// If the refcount goes to zero return this memtable, otherwise return null.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
MemTable* Unref() {
|
MemTable* Unref() {
|
||||||
--refs_;
|
--refs_;
|
||||||
assert(refs_ >= 0);
|
assert(refs_ >= 0);
|
||||||
@ -92,7 +110,7 @@ class MemTable {
|
|||||||
// data structure.
|
// data structure.
|
||||||
//
|
//
|
||||||
// REQUIRES: external synchronization to prevent simultaneous
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
// operations on the same MemTable.
|
// operations on the same MemTable (unless this Memtable is immutable).
|
||||||
size_t ApproximateMemoryUsage();
|
size_t ApproximateMemoryUsage();
|
||||||
|
|
||||||
// This method heuristically determines if the memtable should continue to
|
// This method heuristically determines if the memtable should continue to
|
||||||
@ -120,6 +138,9 @@ class MemTable {
|
|||||||
// Add an entry into memtable that maps key to value at the
|
// Add an entry into memtable that maps key to value at the
|
||||||
// specified sequence number and with the specified type.
|
// specified sequence number and with the specified type.
|
||||||
// Typically value will be empty if type==kTypeDeletion.
|
// Typically value will be empty if type==kTypeDeletion.
|
||||||
|
//
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
void Add(SequenceNumber seq, ValueType type,
|
void Add(SequenceNumber seq, ValueType type,
|
||||||
const Slice& key,
|
const Slice& key,
|
||||||
const Slice& value);
|
const Slice& value);
|
||||||
@ -142,6 +163,9 @@ class MemTable {
|
|||||||
// update inplace
|
// update inplace
|
||||||
// else add(key, new_value)
|
// else add(key, new_value)
|
||||||
// else add(key, new_value)
|
// else add(key, new_value)
|
||||||
|
//
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
void Update(SequenceNumber seq,
|
void Update(SequenceNumber seq,
|
||||||
const Slice& key,
|
const Slice& key,
|
||||||
const Slice& value);
|
const Slice& value);
|
||||||
@ -155,6 +179,9 @@ class MemTable {
|
|||||||
// update inplace
|
// update inplace
|
||||||
// else add(key, new_value)
|
// else add(key, new_value)
|
||||||
// else return false
|
// else return false
|
||||||
|
//
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
bool UpdateCallback(SequenceNumber seq,
|
bool UpdateCallback(SequenceNumber seq,
|
||||||
const Slice& key,
|
const Slice& key,
|
||||||
const Slice& delta);
|
const Slice& delta);
|
||||||
@ -165,29 +192,46 @@ class MemTable {
|
|||||||
size_t CountSuccessiveMergeEntries(const LookupKey& key);
|
size_t CountSuccessiveMergeEntries(const LookupKey& key);
|
||||||
|
|
||||||
// Get total number of entries in the mem table.
|
// Get total number of entries in the mem table.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable (unless this Memtable is immutable).
|
||||||
uint64_t num_entries() const { return num_entries_; }
|
uint64_t num_entries() const { return num_entries_; }
|
||||||
|
|
||||||
|
// Get total number of deletes in the mem table.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable (unless this Memtable is immutable).
|
||||||
uint64_t num_deletes() const { return num_deletes_; }
|
uint64_t num_deletes() const { return num_deletes_; }
|
||||||
|
|
||||||
// Returns the edits area that is needed for flushing the memtable
|
// Returns the edits area that is needed for flushing the memtable
|
||||||
VersionEdit* GetEdits() { return &edit_; }
|
VersionEdit* GetEdits() { return &edit_; }
|
||||||
|
|
||||||
// Returns if there is no entry inserted to the mem table.
|
// Returns if there is no entry inserted to the mem table.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable (unless this Memtable is immutable).
|
||||||
bool IsEmpty() const { return first_seqno_ == 0; }
|
bool IsEmpty() const { return first_seqno_ == 0; }
|
||||||
|
|
||||||
// Returns the sequence number of the first element that was inserted
|
// Returns the sequence number of the first element that was inserted
|
||||||
// into the memtable
|
// into the memtable.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable (unless this Memtable is immutable).
|
||||||
SequenceNumber GetFirstSequenceNumber() { return first_seqno_; }
|
SequenceNumber GetFirstSequenceNumber() { return first_seqno_; }
|
||||||
|
|
||||||
// Returns the next active logfile number when this memtable is about to
|
// Returns the next active logfile number when this memtable is about to
|
||||||
// be flushed to storage
|
// be flushed to storage
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
uint64_t GetNextLogNumber() { return mem_next_logfile_number_; }
|
uint64_t GetNextLogNumber() { return mem_next_logfile_number_; }
|
||||||
|
|
||||||
// Sets the next active logfile number when this memtable is about to
|
// Sets the next active logfile number when this memtable is about to
|
||||||
// be flushed to storage
|
// be flushed to storage
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
|
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
|
||||||
|
|
||||||
// Notify the underlying storage that no more items will be added
|
// Notify the underlying storage that no more items will be added.
|
||||||
|
// REQUIRES: external synchronization to prevent simultaneous
|
||||||
|
// operations on the same MemTable.
|
||||||
|
// After MarkImmutable() is called, you should not attempt to
|
||||||
|
// write anything to this MemTable(). (Ie. do not call Add() or Update()).
|
||||||
void MarkImmutable() {
|
void MarkImmutable() {
|
||||||
table_->MarkReadOnly();
|
table_->MarkReadOnly();
|
||||||
allocator_.DoneAllocating();
|
allocator_.DoneAllocating();
|
||||||
|
@ -35,6 +35,9 @@ class MergeIteratorBuilder;
|
|||||||
// keeps a list of immutable memtables in a vector. the list is immutable
|
// keeps a list of immutable memtables in a vector. the list is immutable
|
||||||
// if refcount is bigger than one. It is used as a state for Get() and
|
// if refcount is bigger than one. It is used as a state for Get() and
|
||||||
// Iterator code paths
|
// Iterator code paths
|
||||||
|
//
|
||||||
|
// This class is not thread-safe. External synchronization is required
|
||||||
|
// (such as holding the db mutex or being on the write thread).
|
||||||
class MemTableListVersion {
|
class MemTableListVersion {
|
||||||
public:
|
public:
|
||||||
explicit MemTableListVersion(MemTableListVersion* old = nullptr);
|
explicit MemTableListVersion(MemTableListVersion* old = nullptr);
|
||||||
@ -77,6 +80,11 @@ class MemTableListVersion {
|
|||||||
// flushes can occur concurrently. However, they are 'committed'
|
// flushes can occur concurrently. However, they are 'committed'
|
||||||
// to the manifest in FIFO order to maintain correctness and
|
// to the manifest in FIFO order to maintain correctness and
|
||||||
// recoverability from a crash.
|
// recoverability from a crash.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Other than imm_flush_needed, this class is not thread-safe and requires
|
||||||
|
// external synchronization (such as holding the db mutex or being on the
|
||||||
|
// write thread.)
|
||||||
class MemTableList {
|
class MemTableList {
|
||||||
public:
|
public:
|
||||||
// A list of memtables.
|
// A list of memtables.
|
||||||
|
@ -84,7 +84,9 @@ class MemTableRep {
|
|||||||
virtual bool Contains(const char* key) const = 0;
|
virtual bool Contains(const char* key) const = 0;
|
||||||
|
|
||||||
// Notify this table rep that it will no longer be added to. By default, does
|
// Notify this table rep that it will no longer be added to. By default, does
|
||||||
// nothing.
|
// nothing. After MarkReadOnly() is called, this table rep will not be
|
||||||
|
// written to (ie No more calls to Allocate(), Insert(), or any writes done
|
||||||
|
// directly to entries accessed through the iterator.)
|
||||||
virtual void MarkReadOnly() { }
|
virtual void MarkReadOnly() { }
|
||||||
|
|
||||||
// Look up key from the mem table, since the first key in the mem table whose
|
// Look up key from the mem table, since the first key in the mem table whose
|
||||||
|
Loading…
Reference in New Issue
Block a user