| // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. See the AUTHORS file for names of contributors. |
| |
| #include "db/table_cache.h" |
| |
| #include "db/filename.h" |
| #include "leveldb/env.h" |
| #include "leveldb/table.h" |
| #include "util/coding.h" |
| |
| namespace leveldb { |
| |
| struct TableAndFile { |
| RandomAccessFile* file; |
| Table* table; |
| }; |
| |
| static void DeleteEntry(const Slice& key, void* value) { |
| TableAndFile* tf = reinterpret_cast<TableAndFile*>(value); |
| delete tf->table; |
| delete tf->file; |
| delete tf; |
| } |
| |
| static void UnrefEntry(void* arg1, void* arg2) { |
| Cache* cache = reinterpret_cast<Cache*>(arg1); |
| Cache::Handle* h = reinterpret_cast<Cache::Handle*>(arg2); |
| cache->Release(h); |
| } |
| |
| TableCache::TableCache(const std::string& dbname, |
| const Options* options, |
| int entries) |
| : env_(options->env), |
| dbname_(dbname), |
| options_(options), |
| cache_(NewLRUCache(entries)) { |
| } |
| |
| TableCache::~TableCache() { |
| delete cache_; |
| } |
| |
| Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, |
| Cache::Handle** handle) { |
| Status s; |
| char buf[sizeof(file_number)]; |
| EncodeFixed64(buf, file_number); |
| Slice key(buf, sizeof(buf)); |
| *handle = cache_->Lookup(key); |
| if (*handle == NULL) { |
| std::string fname = TableFileName(dbname_, file_number); |
| RandomAccessFile* file = NULL; |
| Table* table = NULL; |
| s = env_->NewRandomAccessFile(fname, &file); |
| if (!s.ok()) { |
| std::string old_fname = SSTTableFileName(dbname_, file_number); |
| if (env_->NewRandomAccessFile(old_fname, &file).ok()) { |
| s = Status::OK(); |
| } |
| } |
| if (s.ok()) { |
| s = Table::Open(*options_, file, file_size, &table); |
| } |
| |
| if (!s.ok()) { |
| assert(table == NULL); |
| delete file; |
| // We do not cache error results so that if the error is transient, |
| // or somebody repairs the file, we recover automatically. |
| } else { |
| TableAndFile* tf = new TableAndFile; |
| tf->file = file; |
| tf->table = table; |
| *handle = cache_->Insert(key, tf, 1, &DeleteEntry); |
| } |
| } |
| return s; |
| } |
| |
| Iterator* TableCache::NewIterator(const ReadOptions& options, |
| uint64_t file_number, |
| uint64_t file_size, |
| Table** tableptr) { |
| if (tableptr != NULL) { |
| *tableptr = NULL; |
| } |
| |
| Cache::Handle* handle = NULL; |
| Status s = FindTable(file_number, file_size, &handle); |
| if (!s.ok()) { |
| return NewErrorIterator(s); |
| } |
| |
| Table* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table; |
| Iterator* result = table->NewIterator(options); |
| result->RegisterCleanup(&UnrefEntry, cache_, handle); |
| if (tableptr != NULL) { |
| *tableptr = table; |
| } |
| return result; |
| } |
| |
| Status TableCache::Get(const ReadOptions& options, |
| uint64_t file_number, |
| uint64_t file_size, |
| const Slice& k, |
| void* arg, |
| void (*saver)(void*, const Slice&, const Slice&)) { |
| Cache::Handle* handle = NULL; |
| Status s = FindTable(file_number, file_size, &handle); |
| if (s.ok()) { |
| Table* t = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table; |
| s = t->InternalGet(options, k, arg, saver); |
| cache_->Release(handle); |
| } |
| return s; |
| } |
| |
| void TableCache::Evict(uint64_t file_number) { |
| char buf[sizeof(file_number)]; |
| EncodeFixed64(buf, file_number); |
| cache_->Erase(Slice(buf, sizeof(buf))); |
| } |
| |
| } // namespace leveldb |