Skip to content

Commit

Permalink
Prevents Table Cache to open same files more times (facebook#6707)
Browse files Browse the repository at this point in the history
Summary:
In highly concurrent requests table cache opens same file more times which lowers purpose of max_open_files. Fixes (facebook#6699)
Pull Request resolved: facebook#6707

Reviewed By: ltamasi

Differential Revision: D21044965

fbshipit-source-id: f6e91d90b60dad86e518b5147021da42460ee1d2
  • Loading branch information
koldat authored and facebook-github-bot committed Apr 21, 2020
1 parent f9155a3 commit 6ee66cf
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
12 changes: 11 additions & 1 deletion db/table_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,17 @@ void AppendVarint64(IterKey* key, uint64_t v) {

} // namespace

const int kLoadConcurency = 128;

TableCache::TableCache(const ImmutableCFOptions& ioptions,
const FileOptions& file_options, Cache* const cache,
BlockCacheTracer* const block_cache_tracer)
: ioptions_(ioptions),
file_options_(file_options),
cache_(cache),
immortal_tables_(false),
block_cache_tracer_(block_cache_tracer) {
block_cache_tracer_(block_cache_tracer),
loader_mutex_(kLoadConcurency, GetSliceNPHash64) {
if (ioptions_.row_cache) {
// If the same cache is shared by multiple instances, we need to
// disambiguate its entries.
Expand Down Expand Up @@ -155,6 +158,13 @@ Status TableCache::FindTable(const FileOptions& file_options,
if (no_io) { // Don't do IO and return a not-found status
return Status::Incomplete("Table not found in table_cache, no_io is set");
}
MutexLock load_lock(loader_mutex_.get(key));
// We check the cache again under loading mutex
*handle = cache_->Lookup(key);
if (*handle != nullptr) {
return s;
}

std::unique_ptr<TableReader> table_reader;
s = GetTableReader(file_options, internal_comparator, fd,
false /* sequential mode */, record_read_stats,
Expand Down
1 change: 1 addition & 0 deletions db/table_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class TableCache {
std::string row_cache_id_;
bool immortal_tables_;
BlockCacheTracer* const block_cache_tracer_;
Striped<port::Mutex, Slice> loader_mutex_;
};

} // namespace ROCKSDB_NAMESPACE
51 changes: 51 additions & 0 deletions util/mutexlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,55 @@ class SpinMutex {
std::atomic<bool> locked_;
};

// We want to prevent false sharing
template <class T>
struct ALIGN_AS(CACHE_LINE_SIZE) LockData {
T lock_;
};

//
// Inspired by Guava: https://github.com/google/guava/wiki/StripedExplained
// A striped Lock. This offers the underlying lock striping similar
// to that of ConcurrentHashMap in a reusable form, and extends it for
// semaphores and read-write locks. Conceptually, lock striping is the technique
// of dividing a lock into many <i>stripes</i>, increasing the granularity of a
// single lock and allowing independent operations to lock different stripes and
// proceed concurrently, instead of creating contention for a single lock.
//
template <class T, class P>
class Striped {
public:
Striped(size_t stripes, std::function<uint64_t(const P &)> hash)
: stripes_(stripes), hash_(hash) {

locks_ = reinterpret_cast<LockData<T> *>(
port::cacheline_aligned_alloc(sizeof(LockData<T>) * stripes));
for (size_t i = 0; i < stripes; i++) {
new (&locks_[i]) LockData<T>();
}

}

virtual ~Striped() {
if (locks_ != nullptr) {
assert(stripes_ > 0);
for (size_t i = 0; i < stripes_; i++) {
locks_[i].~LockData<T>();
}
port::cacheline_aligned_free(locks_);
}
}

T *get(const P &key) {
uint64_t h = hash_(key);
size_t index = h % stripes_;
return &reinterpret_cast<LockData<T> *>(&locks_[index])->lock_;
}

private:
size_t stripes_;
LockData<T> *locks_;
std::function<uint64_t(const P &)> hash_;
};

} // namespace ROCKSDB_NAMESPACE

0 comments on commit 6ee66cf

Please sign in to comment.