row_cache class is meant to cache data for given table by wrapping some underlying data source. It gives away a mutation_reader which uses in-memory data if possible, or delegates to the underlying reader and populates the cache on-the-fly. Accesses to data in cache is tracked for eviction purposes by a separate entity, the cache_tracker. There is one such tracker for the whole shard.
133 lines
3.9 KiB
C++
133 lines
3.9 KiB
C++
/*
|
|
* Copyright 2015 Cloudius Systems
|
|
*/
|
|
|
|
#include "row_cache.hh"
|
|
#include "core/memory.hh"
|
|
#include "core/do_with.hh"
|
|
#include "core/future-util.hh"
|
|
|
|
static thread_local logging::logger logger("cache");
|
|
|
|
cache_tracker& global_cache_tracker() {
|
|
static thread_local cache_tracker instance;
|
|
return instance;
|
|
}
|
|
|
|
cache_tracker::cache_tracker()
|
|
: _reclaimer([this] {
|
|
logger.warn("Clearing cache from reclaimer hook");
|
|
// FIXME: perform incremental eviction. We should first switch to a
|
|
// compacting memory allocator to avoid problems with memory
|
|
// fragmentation.
|
|
clear();
|
|
})
|
|
{ }
|
|
|
|
cache_tracker::~cache_tracker() {
|
|
clear();
|
|
}
|
|
|
|
void cache_tracker::clear() {
|
|
_lru.clear_and_dispose(std::default_delete<cache_entry>());
|
|
}
|
|
|
|
void cache_tracker::touch(cache_entry& e) {
|
|
_lru.erase(_lru.iterator_to(e));
|
|
_lru.push_front(e);
|
|
}
|
|
|
|
void cache_tracker::insert(cache_entry& entry) {
|
|
_lru.push_front(entry);
|
|
}
|
|
|
|
// Reader which populates the cache using data from the delegate.
|
|
class populating_reader {
|
|
row_cache& _cache;
|
|
mutation_reader _delegate;
|
|
public:
|
|
populating_reader(row_cache& cache, mutation_reader delegate)
|
|
: _cache(cache)
|
|
, _delegate(std::move(delegate))
|
|
{ }
|
|
|
|
future<mutation_opt> operator()() {
|
|
return _delegate().then([this] (mutation_opt&& mo) {
|
|
if (mo) {
|
|
_cache.populate(*mo);
|
|
}
|
|
return std::move(mo);
|
|
});
|
|
}
|
|
};
|
|
|
|
mutation_reader
|
|
row_cache::make_reader(const query::partition_range& range) {
|
|
if (range.is_singular()) {
|
|
const query::ring_position& pos = range.start_value();
|
|
|
|
if (!pos.has_key()) {
|
|
warn(unimplemented::cause::RANGE_QUERIES);
|
|
return populating_reader(*this, _underlying(range));
|
|
}
|
|
|
|
const dht::decorated_key& dk = pos.as_decorated_key();
|
|
auto i = _partitions.find(dk, cache_entry::compare(_schema));
|
|
if (i != _partitions.end()) {
|
|
cache_entry& e = *i;
|
|
_tracker.touch(e);
|
|
++_stats.hits;
|
|
return make_reader_returning(mutation(_schema, dk, e.partition()));
|
|
} else {
|
|
++_stats.misses;
|
|
return populating_reader(*this, _underlying(range));
|
|
}
|
|
}
|
|
|
|
warn(unimplemented::cause::RANGE_QUERIES);
|
|
return populating_reader(*this, _underlying(range));
|
|
}
|
|
|
|
row_cache::~row_cache() {
|
|
_partitions.clear_and_dispose(std::default_delete<cache_entry>());
|
|
}
|
|
|
|
void row_cache::populate(const mutation& m) {
|
|
populate(mutation(m));
|
|
}
|
|
|
|
void row_cache::populate(mutation&& m) {
|
|
auto i = _partitions.lower_bound(m.decorated_key(), cache_entry::compare(_schema));
|
|
if (i == _partitions.end()) {
|
|
cache_entry* entry = new cache_entry(m.decorated_key(), std::move(m.partition()));
|
|
_tracker.insert(*entry);
|
|
_partitions.insert(i, *entry);
|
|
} else {
|
|
cache_entry& entry = *i;
|
|
_tracker.touch(entry);
|
|
entry.partition().apply(*m.schema(), m.partition());
|
|
}
|
|
}
|
|
|
|
future<> row_cache::update(mutation_reader reader) {
|
|
return do_with(std::move(reader), [this] (mutation_reader& r) {
|
|
return consume(r, [this] (mutation&& m) {
|
|
auto i = _partitions.find(m.decorated_key(), cache_entry::compare(_schema));
|
|
// If cache doesn't contain the entry we cannot insert it because the mutation may be incomplete.
|
|
if (i != _partitions.end()) {
|
|
cache_entry& entry = *i;
|
|
_tracker.touch(entry);
|
|
entry.partition().apply(*m.schema(), std::move(m.partition()));
|
|
}
|
|
return stop_iteration::no;
|
|
});
|
|
});
|
|
}
|
|
|
|
row_cache::row_cache(schema_ptr s, mutation_source fallback_factory, cache_tracker& tracker)
|
|
: _tracker(tracker)
|
|
, _schema(std::move(s))
|
|
, _partitions(cache_entry::compare(_schema))
|
|
, _underlying(std::move(fallback_factory))
|
|
{ }
|