Browse Source

Squashed 'src/leveldb/' changes from aca1ffc..ae6c262

ae6c262 Merge branch 'leveldb' into ripple-fork
28fa222 Looks like a bit more delay is needed to smooth the latency.
a18f3e6 Tidy up JobQueue, add ripple_core module
ab82e57 Release leveldb 1.12
02c6259 Release leveldb 1.11
5bbb544 Rate limit compactions with a 25ms pause after each complete file.
8c29c47 LevelDB issue 178 fix: cannot resize a level 0 compaction set
18b245c Added GNU/kFreeBSD kernel name (TARGET_OS)
8be9d12 CondVar::SignalAll was broken, leading to deadlocks on Windows builds. http://code.google.com/p/leveldb/issues/detail?id=149
c9fc070 Upgrade LevelDB to 1.10.0, mostly for better write stall logging.
8215b15 Tweak to variable name to support unity build

git-subtree-dir: src/leveldb
git-subtree-split: ae6c2620b2ef3d5c69e63dc0eda865d6a39fa061
pull/145/head
Vinnie Falco 11 years ago
parent
commit
adae78ea99
  1. 3
      AUTHORS
  2. 6
      Makefile
  3. 6
      build_detect_platform
  4. 41
      db/db_impl.cc
  5. 1
      db/db_impl.h
  6. 85
      db/db_test.cc
  7. 2
      db/dbformat.cc
  8. 2
      db/filename_test.cc
  9. 21
      db/version_set.cc
  10. 2
      include/leveldb/db.h
  11. 10
      port/port_win.cc
  12. 13
      table/block.cc
  13. 1
      table/table.cc
  14. 30
      table/table_test.cc
  15. 5
      util/cache.cc
  16. 2
      util/coding_test.cc
  17. 4
      util/comparator.cc
  18. 10
      util/env_posix.cc
  19. 11
      util/hash.cc

3
AUTHORS

@ -6,3 +6,6 @@ Google Inc.
# Initial version authors: # Initial version authors:
Jeffrey Dean <jeff@google.com> Jeffrey Dean <jeff@google.com>
Sanjay Ghemawat <sanjay@google.com> Sanjay Ghemawat <sanjay@google.com>
# Partial list of contributors:
Kevin Regan <kevin.d.regan@gmail.com>

6
Makefile

@ -42,6 +42,7 @@ TESTS = \
env_test \ env_test \
filename_test \ filename_test \
filter_block_test \ filter_block_test \
issue178_test \
log_test \ log_test \
memenv_test \ memenv_test \
skiplist_test \ skiplist_test \
@ -69,7 +70,7 @@ SHARED = $(SHARED1)
else else
# Update db.h if you change these. # Update db.h if you change these.
SHARED_MAJOR = 1 SHARED_MAJOR = 1
SHARED_MINOR = 9 SHARED_MINOR = 12
SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
SHARED2 = $(SHARED1).$(SHARED_MAJOR) SHARED2 = $(SHARED1).$(SHARED_MAJOR)
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
@ -146,6 +147,9 @@ filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)

6
build_detect_platform

@ -94,6 +94,12 @@ case "$TARGET_OS" in
PLATFORM_LIBS="-lpthread" PLATFORM_LIBS="-lpthread"
PORT_FILE=port/port_posix.cc PORT_FILE=port/port_posix.cc
;; ;;
GNU/kFreeBSD)
PLATFORM=OS_KFREEBSD
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_KFREEBSD"
PLATFORM_LIBS="-lpthread"
PORT_FILE=port/port_posix.cc
;;
NetBSD) NetBSD)
PLATFORM=OS_NETBSD PLATFORM=OS_NETBSD
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD"

41
db/db_impl.cc

@ -35,6 +35,8 @@
namespace leveldb { namespace leveldb {
const int kNumNonTableCacheFiles = 10;
// Information kept for every waiting writer // Information kept for every waiting writer
struct DBImpl::Writer { struct DBImpl::Writer {
Status status; Status status;
@ -92,9 +94,9 @@ Options SanitizeOptions(const std::string& dbname,
Options result = src; Options result = src;
result.comparator = icmp; result.comparator = icmp;
result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL;
ClipToRange(&result.max_open_files, 20, 50000); ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000);
ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
ClipToRange(&result.block_size, 1<<10, 4<<20); ClipToRange(&result.block_size, 1<<10, 4<<20);
if (result.info_log == NULL) { if (result.info_log == NULL) {
// Open a log file in the same directory as the db // Open a log file in the same directory as the db
src.env->CreateDir(dbname); // In case it does not exist src.env->CreateDir(dbname); // In case it does not exist
@ -130,12 +132,13 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
log_(NULL), log_(NULL),
tmp_batch_(new WriteBatch), tmp_batch_(new WriteBatch),
bg_compaction_scheduled_(false), bg_compaction_scheduled_(false),
manual_compaction_(NULL) { manual_compaction_(NULL),
consecutive_compaction_errors_(0) {
mem_->Ref(); mem_->Ref();
has_imm_.Release_Store(NULL); has_imm_.Release_Store(NULL);
// Reserve ten files or so for other uses and give the rest to TableCache. // Reserve ten files or so for other uses and give the rest to TableCache.
const int table_cache_size = options.max_open_files - 10; const int table_cache_size = options.max_open_files - kNumNonTableCacheFiles;
table_cache_ = new TableCache(dbname_, &options_, table_cache_size); table_cache_ = new TableCache(dbname_, &options_, table_cache_size);
versions_ = new VersionSet(dbname_, &options_, table_cache_, versions_ = new VersionSet(dbname_, &options_, table_cache_,
@ -310,16 +313,24 @@ Status DBImpl::Recover(VersionEdit* edit) {
if (!s.ok()) { if (!s.ok()) {
return s; return s;
} }
std::set<uint64_t> expected;
versions_->AddLiveFiles(&expected);
uint64_t number; uint64_t number;
FileType type; FileType type;
std::vector<uint64_t> logs; std::vector<uint64_t> logs;
for (size_t i = 0; i < filenames.size(); i++) { for (size_t i = 0; i < filenames.size(); i++) {
if (ParseFileName(filenames[i], &number, &type) if (ParseFileName(filenames[i], &number, &type)) {
&& type == kLogFile expected.erase(number);
&& ((number >= min_log) || (number == prev_log))) { if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
logs.push_back(number); logs.push_back(number);
} }
} }
if (!expected.empty()) {
char buf[50];
snprintf(buf, sizeof(buf), "%d missing files; e.g.",
static_cast<int>(expected.size()));
return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
}
// Recover in the order in which the logs were generated // Recover in the order in which the logs were generated
std::sort(logs.begin(), logs.end()); std::sort(logs.begin(), logs.end());
@ -611,6 +622,7 @@ void DBImpl::BackgroundCall() {
Status s = BackgroundCompaction(); Status s = BackgroundCompaction();
if (s.ok()) { if (s.ok()) {
// Success // Success
consecutive_compaction_errors_ = 0;
} else if (shutting_down_.Acquire_Load()) { } else if (shutting_down_.Acquire_Load()) {
// Error most likely due to shutdown; do not wait // Error most likely due to shutdown; do not wait
} else { } else {
@ -622,7 +634,12 @@ void DBImpl::BackgroundCall() {
Log(options_.info_log, "Waiting after background compaction error: %s", Log(options_.info_log, "Waiting after background compaction error: %s",
s.ToString().c_str()); s.ToString().c_str());
mutex_.Unlock(); mutex_.Unlock();
env_->SleepForMicroseconds(1000000); ++consecutive_compaction_errors_;
int seconds_to_sleep = 1;
for (int i = 0; i < 3 && i < consecutive_compaction_errors_ - 1; ++i) {
seconds_to_sleep *= 2;
}
env_->SleepForMicroseconds(seconds_to_sleep * 1000000);
mutex_.Lock(); mutex_.Lock();
} }
} }
@ -805,6 +822,9 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact,
(unsigned long long) output_number, (unsigned long long) output_number,
(unsigned long long) current_entries, (unsigned long long) current_entries,
(unsigned long long) current_bytes); (unsigned long long) current_bytes);
// rate-limit compaction file creation with a 100ms pause
env_->SleepForMicroseconds(100000);
} }
} }
return s; return s;
@ -1268,10 +1288,11 @@ Status DBImpl::MakeRoomForWrite(bool force) {
} else if (imm_ != NULL) { } else if (imm_ != NULL) {
// We have filled up the current memtable, but the previous // We have filled up the current memtable, but the previous
// one is still being compacted, so we wait. // one is still being compacted, so we wait.
Log(options_.info_log, "Current memtable full; waiting...\n");
bg_cv_.Wait(); bg_cv_.Wait();
} else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {
// There are too many level-0 files. // There are too many level-0 files.
Log(options_.info_log, "waiting...\n"); Log(options_.info_log, "Too many L0 files; waiting...\n");
bg_cv_.Wait(); bg_cv_.Wait();
} else { } else {
// Attempt to switch to a new memtable and trigger compaction of old // Attempt to switch to a new memtable and trigger compaction of old

1
db/db_impl.h

@ -163,6 +163,7 @@ class DBImpl : public DB {
// Have we encountered a background error in paranoid mode? // Have we encountered a background error in paranoid mode?
Status bg_error_; Status bg_error_;
int consecutive_compaction_errors_;
// Per level compaction stats. stats_[level] stores the stats for // Per level compaction stats. stats_[level] stores the stats for
// compactions that produced data for the specified "level". // compactions that produced data for the specified "level".

85
db/db_test.cc

@ -33,8 +33,11 @@ class AtomicCounter {
public: public:
AtomicCounter() : count_(0) { } AtomicCounter() : count_(0) { }
void Increment() { void Increment() {
IncrementBy(1);
}
void IncrementBy(int count) {
MutexLock l(&mu_); MutexLock l(&mu_);
count_++; count_ += count;
} }
int Read() { int Read() {
MutexLock l(&mu_); MutexLock l(&mu_);
@ -45,6 +48,10 @@ class AtomicCounter {
count_ = 0; count_ = 0;
} }
}; };
void DelayMilliseconds(int millis) {
Env::Default()->SleepForMicroseconds(millis * 1000);
}
} }
// Special Env used to delay background operations // Special Env used to delay background operations
@ -69,6 +76,7 @@ class SpecialEnv : public EnvWrapper {
AtomicCounter random_read_counter_; AtomicCounter random_read_counter_;
AtomicCounter sleep_counter_; AtomicCounter sleep_counter_;
AtomicCounter sleep_time_counter_;
explicit SpecialEnv(Env* base) : EnvWrapper(base) { explicit SpecialEnv(Env* base) : EnvWrapper(base) {
delay_sstable_sync_.Release_Store(NULL); delay_sstable_sync_.Release_Store(NULL);
@ -103,7 +111,7 @@ class SpecialEnv : public EnvWrapper {
Status Flush() { return base_->Flush(); } Status Flush() { return base_->Flush(); }
Status Sync() { Status Sync() {
while (env_->delay_sstable_sync_.Acquire_Load() != NULL) { while (env_->delay_sstable_sync_.Acquire_Load() != NULL) {
env_->SleepForMicroseconds(100000); DelayMilliseconds(100);
} }
return base_->Sync(); return base_->Sync();
} }
@ -174,8 +182,9 @@ class SpecialEnv : public EnvWrapper {
virtual void SleepForMicroseconds(int micros) { virtual void SleepForMicroseconds(int micros) {
sleep_counter_.Increment(); sleep_counter_.Increment();
target()->SleepForMicroseconds(micros); sleep_time_counter_.IncrementBy(micros);
} }
}; };
class DBTest { class DBTest {
@ -461,6 +470,20 @@ class DBTest {
} }
return result; return result;
} }
bool DeleteAnSSTFile() {
std::vector<std::string> filenames;
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
uint64_t number;
FileType type;
for (size_t i = 0; i < filenames.size(); i++) {
if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) {
ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number)));
return true;
}
}
return false;
}
}; };
TEST(DBTest, Empty) { TEST(DBTest, Empty) {
@ -611,7 +634,7 @@ TEST(DBTest, GetEncountersEmptyLevel) {
} }
// Step 4: Wait for compaction to finish // Step 4: Wait for compaction to finish
env_->SleepForMicroseconds(1000000); DelayMilliseconds(1000);
ASSERT_EQ(NumTableFilesAtLevel(0), 0); ASSERT_EQ(NumTableFilesAtLevel(0), 0);
} while (ChangeOptions()); } while (ChangeOptions());
@ -1295,7 +1318,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_a) {
Reopen(); Reopen();
Reopen(); Reopen();
ASSERT_EQ("(a->v)", Contents()); ASSERT_EQ("(a->v)", Contents());
env_->SleepForMicroseconds(1000000); // Wait for compaction to finish DelayMilliseconds(1000); // Wait for compaction to finish
ASSERT_EQ("(a->v)", Contents()); ASSERT_EQ("(a->v)", Contents());
} }
@ -1311,7 +1334,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) {
Put("",""); Put("","");
Reopen(); Reopen();
Put("",""); Put("","");
env_->SleepForMicroseconds(1000000); // Wait for compaction to finish DelayMilliseconds(1000); // Wait for compaction to finish
Reopen(); Reopen();
Put("d","dv"); Put("d","dv");
Reopen(); Reopen();
@ -1321,7 +1344,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) {
Delete("b"); Delete("b");
Reopen(); Reopen();
ASSERT_EQ("(->)(c->cv)", Contents()); ASSERT_EQ("(->)(c->cv)", Contents());
env_->SleepForMicroseconds(1000000); // Wait for compaction to finish DelayMilliseconds(1000); // Wait for compaction to finish
ASSERT_EQ("(->)(c->cv)", Contents()); ASSERT_EQ("(->)(c->cv)", Contents());
} }
@ -1506,6 +1529,30 @@ TEST(DBTest, NoSpace) {
ASSERT_GE(env_->sleep_counter_.Read(), 5); ASSERT_GE(env_->sleep_counter_.Read(), 5);
} }
TEST(DBTest, ExponentialBackoff) {
Options options = CurrentOptions();
options.env = env_;
Reopen(&options);
ASSERT_OK(Put("foo", "v1"));
ASSERT_EQ("v1", Get("foo"));
Compact("a", "z");
env_->non_writable_.Release_Store(env_); // Force errors for new files
env_->sleep_counter_.Reset();
env_->sleep_time_counter_.Reset();
for (int i = 0; i < 5; i++) {
dbfull()->TEST_CompactRange(2, NULL, NULL);
}
env_->non_writable_.Release_Store(NULL);
// Wait for compaction to finish
DelayMilliseconds(1000);
ASSERT_GE(env_->sleep_counter_.Read(), 5);
ASSERT_LT(env_->sleep_counter_.Read(), 10);
ASSERT_GE(env_->sleep_time_counter_.Read(), 10e6);
}
TEST(DBTest, NonWritableFileSystem) { TEST(DBTest, NonWritableFileSystem) {
Options options = CurrentOptions(); Options options = CurrentOptions();
options.write_buffer_size = 1000; options.write_buffer_size = 1000;
@ -1519,7 +1566,7 @@ TEST(DBTest, NonWritableFileSystem) {
fprintf(stderr, "iter %d; errors %d\n", i, errors); fprintf(stderr, "iter %d; errors %d\n", i, errors);
if (!Put("foo", big).ok()) { if (!Put("foo", big).ok()) {
errors++; errors++;
env_->SleepForMicroseconds(100000); DelayMilliseconds(100);
} }
} }
ASSERT_GT(errors, 0); ASSERT_GT(errors, 0);
@ -1567,6 +1614,24 @@ TEST(DBTest, ManifestWriteError) {
} }
} }
TEST(DBTest, MissingSSTFile) {
ASSERT_OK(Put("foo", "bar"));
ASSERT_EQ("bar", Get("foo"));
// Dump the memtable to disk.
dbfull()->TEST_CompactMemTable();
ASSERT_EQ("bar", Get("foo"));
Close();
ASSERT_TRUE(DeleteAnSSTFile());
Options options = CurrentOptions();
options.paranoid_checks = true;
Status s = TryReopen(&options);
ASSERT_TRUE(!s.ok());
ASSERT_TRUE(s.ToString().find("issing") != std::string::npos)
<< s.ToString();
}
TEST(DBTest, FilesDeletedAfterCompaction) { TEST(DBTest, FilesDeletedAfterCompaction) {
ASSERT_OK(Put("foo", "v2")); ASSERT_OK(Put("foo", "v2"));
Compact("a", "z"); Compact("a", "z");
@ -1711,13 +1776,13 @@ TEST(DBTest, MultiThreaded) {
} }
// Let them run for a while // Let them run for a while
env_->SleepForMicroseconds(kTestSeconds * 1000000); DelayMilliseconds(kTestSeconds * 1000);
// Stop the threads and wait for them to finish // Stop the threads and wait for them to finish
mt.stop.Release_Store(&mt); mt.stop.Release_Store(&mt);
for (int id = 0; id < kNumThreads; id++) { for (int id = 0; id < kNumThreads; id++) {
while (mt.thread_done[id].Acquire_Load() == NULL) { while (mt.thread_done[id].Acquire_Load() == NULL) {
env_->SleepForMicroseconds(100000); DelayMilliseconds(100);
} }
} }
} while (ChangeOptions()); } while (ChangeOptions());

2
db/dbformat.cc

@ -26,7 +26,7 @@ std::string ParsedInternalKey::DebugString() const {
(unsigned long long) sequence, (unsigned long long) sequence,
int(type)); int(type));
std::string result = "'"; std::string result = "'";
result += user_key.ToString(); result += EscapeString(user_key.ToString());
result += buf; result += buf;
return result; return result;
} }

2
db/filename_test.cc

@ -70,7 +70,7 @@ TEST(FileNameTest, Parse) {
for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {
std::string f = errors[i]; std::string f = errors[i];
ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;
}; }
} }
TEST(FileNameTest, Construction) { TEST(FileNameTest, Construction) {

21
db/version_set.cc

@ -1331,14 +1331,19 @@ Compaction* VersionSet::CompactRange(
} }
// Avoid compacting too much in one shot in case the range is large. // Avoid compacting too much in one shot in case the range is large.
const uint64_t limit = MaxFileSizeForLevel(level); // But we cannot do this for level-0 since level-0 files can overlap
uint64_t total = 0; // and we must not pick one file and drop another older file if the
for (size_t i = 0; i < inputs.size(); i++) { // two files overlap.
uint64_t s = inputs[i]->file_size; if (level > 0) {
total += s; const uint64_t limit = MaxFileSizeForLevel(level);
if (total >= limit) { uint64_t total = 0;
inputs.resize(i + 1); for (size_t i = 0; i < inputs.size(); i++) {
break; uint64_t s = inputs[i]->file_size;
total += s;
if (total >= limit) {
inputs.resize(i + 1);
break;
}
} }
} }

2
include/leveldb/db.h

@ -14,7 +14,7 @@ namespace leveldb {
// Update Makefile if you change these // Update Makefile if you change these
static const int kMajorVersion = 1; static const int kMajorVersion = 1;
static const int kMinorVersion = 9; static const int kMinorVersion = 12;
struct Options; struct Options;
struct ReadOptions; struct ReadOptions;

10
port/port_win.cc

@ -109,12 +109,10 @@ void CondVar::Signal() {
void CondVar::SignalAll() { void CondVar::SignalAll() {
wait_mtx_.Lock(); wait_mtx_.Lock();
for(long i = 0; i < waiting_; ++i) { ::ReleaseSemaphore(sem1_, waiting_, NULL);
::ReleaseSemaphore(sem1_, 1, NULL); while(waiting_ > 0) {
while(waiting_ > 0) { --waiting_;
--waiting_; ::WaitForSingleObject(sem2_, INFINITE);
::WaitForSingleObject(sem2_, INFINITE);
}
} }
wait_mtx_.Unlock(); wait_mtx_.Unlock();
} }

13
table/block.cc

@ -16,7 +16,7 @@
namespace leveldb { namespace leveldb {
inline uint32_t Block::NumRestarts() const { inline uint32_t Block::NumRestarts() const {
assert(size_ >= 2*sizeof(uint32_t)); assert(size_ >= sizeof(uint32_t));
return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); return DecodeFixed32(data_ + size_ - sizeof(uint32_t));
} }
@ -27,11 +27,12 @@ Block::Block(const BlockContents& contents)
if (size_ < sizeof(uint32_t)) { if (size_ < sizeof(uint32_t)) {
size_ = 0; // Error marker size_ = 0; // Error marker
} else { } else {
restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t);
if (restart_offset_ > size_ - sizeof(uint32_t)) { if (NumRestarts() > max_restarts_allowed) {
// The size is too small for NumRestarts() and therefore // The size is too small for NumRestarts()
// restart_offset_ wrapped around.
size_ = 0; size_ = 0;
} else {
restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
} }
} }
} }
@ -253,7 +254,7 @@ class Block::Iter : public Iterator {
}; };
Iterator* Block::NewIterator(const Comparator* cmp) { Iterator* Block::NewIterator(const Comparator* cmp) {
if (size_ < 2*sizeof(uint32_t)) { if (size_ < sizeof(uint32_t)) {
return NewErrorIterator(Status::Corruption("bad block contents")); return NewErrorIterator(Status::Corruption("bad block contents"));
} }
const uint32_t num_restarts = NumRestarts(); const uint32_t num_restarts = NumRestarts();

1
table/table.cc

@ -228,7 +228,6 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k,
!filter->KeyMayMatch(handle.offset(), k)) { !filter->KeyMayMatch(handle.offset(), k)) {
// Not found // Not found
} else { } else {
Slice handle = iiter->value();
Iterator* block_iter = BlockReader(this, options, iiter->value()); Iterator* block_iter = BlockReader(this, options, iiter->value());
block_iter->Seek(k); block_iter->Seek(k);
if (block_iter->Valid()) { if (block_iter->Valid()) {

30
table/table_test.cc

@ -644,6 +644,36 @@ class Harness {
Constructor* constructor_; Constructor* constructor_;
}; };
// Test empty table/block.
TEST(Harness, Empty) {
for (int i = 0; i < kNumTestArgs; i++) {
Init(kTestArgList[i]);
Random rnd(test::RandomSeed() + 1);
Test(&rnd);
}
}
// Special test for a block with no restart entries. The C++ leveldb
// code never generates such blocks, but the Java version of leveldb
// seems to.
TEST(Harness, ZeroRestartPointsInBlock) {
char data[sizeof(uint32_t)];
memset(data, 0, sizeof(data));
BlockContents contents;
contents.data = Slice(data, sizeof(data));
contents.cachable = false;
contents.heap_allocated = false;
Block block(contents);
Iterator* iter = block.NewIterator(BytewiseComparator());
iter->SeekToFirst();
ASSERT_TRUE(!iter->Valid());
iter->SeekToLast();
ASSERT_TRUE(!iter->Valid());
iter->Seek("foo");
ASSERT_TRUE(!iter->Valid());
delete iter;
}
// Test the empty key // Test the empty key
TEST(Harness, SimpleEmptyKey) { TEST(Harness, SimpleEmptyKey) {
for (int i = 0; i < kNumTestArgs; i++) { for (int i = 0; i < kNumTestArgs; i++) {

5
util/cache.cc

@ -116,7 +116,6 @@ class HandleTable {
LRUHandle* h = list_[i]; LRUHandle* h = list_[i];
while (h != NULL) { while (h != NULL) {
LRUHandle* next = h->next_hash; LRUHandle* next = h->next_hash;
Slice key = h->key();
uint32_t hash = h->hash; uint32_t hash = h->hash;
LRUHandle** ptr = &new_list[hash & (new_length - 1)]; LRUHandle** ptr = &new_list[hash & (new_length - 1)];
h->next_hash = *ptr; h->next_hash = *ptr;
@ -160,7 +159,6 @@ class LRUCache {
// mutex_ protects the following state. // mutex_ protects the following state.
port::Mutex mutex_; port::Mutex mutex_;
size_t usage_; size_t usage_;
uint64_t last_id_;
// Dummy head of LRU list. // Dummy head of LRU list.
// lru.prev is newest entry, lru.next is oldest entry. // lru.prev is newest entry, lru.next is oldest entry.
@ -170,8 +168,7 @@ class LRUCache {
}; };
LRUCache::LRUCache() LRUCache::LRUCache()
: usage_(0), : usage_(0) {
last_id_(0) {
// Make empty circular linked list // Make empty circular linked list
lru_.next = &lru_; lru_.next = &lru_;
lru_.prev = &lru_; lru_.prev = &lru_;

2
util/coding_test.cc

@ -109,7 +109,7 @@ TEST(Coding, Varint64) {
values.push_back(power); values.push_back(power);
values.push_back(power-1); values.push_back(power-1);
values.push_back(power+1); values.push_back(power+1);
}; }
std::string s; std::string s;
for (int i = 0; i < values.size(); i++) { for (int i = 0; i < values.size(); i++) {

4
util/comparator.cc

@ -66,7 +66,7 @@ class BytewiseComparatorImpl : public Comparator {
}; };
} // namespace } // namespace
static port::OnceType once = LEVELDB_ONCE_INIT; static port::OnceType once_comparator = LEVELDB_ONCE_INIT;
static const Comparator* bytewise; static const Comparator* bytewise;
static void InitModule() { static void InitModule() {
@ -74,7 +74,7 @@ static void InitModule() {
} }
const Comparator* BytewiseComparator() { const Comparator* BytewiseComparator() {
port::InitOnce(&once, InitModule); port::InitOnce(&once_comparator, InitModule);
return bytewise; return bytewise;
} }

10
util/env_posix.cc

@ -386,7 +386,7 @@ class PosixEnv : public Env {
PosixEnv(); PosixEnv();
virtual ~PosixEnv() { virtual ~PosixEnv() {
fprintf(stderr, "Destroying Env::Default()\n"); fprintf(stderr, "Destroying Env::Default()\n");
exit(1); abort();
} }
virtual Status NewSequentialFile(const std::string& fname, virtual Status NewSequentialFile(const std::string& fname,
@ -467,7 +467,7 @@ class PosixEnv : public Env {
result = IOError(fname, errno); result = IOError(fname, errno);
} }
return result; return result;
}; }
virtual Status CreateDir(const std::string& name) { virtual Status CreateDir(const std::string& name) {
Status result; Status result;
@ -475,7 +475,7 @@ class PosixEnv : public Env {
result = IOError(name, errno); result = IOError(name, errno);
} }
return result; return result;
}; }
virtual Status DeleteDir(const std::string& name) { virtual Status DeleteDir(const std::string& name) {
Status result; Status result;
@ -483,7 +483,7 @@ class PosixEnv : public Env {
result = IOError(name, errno); result = IOError(name, errno);
} }
return result; return result;
}; }
virtual Status GetFileSize(const std::string& fname, uint64_t* size) { virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
Status s; Status s;
@ -589,7 +589,7 @@ class PosixEnv : public Env {
void PthreadCall(const char* label, int result) { void PthreadCall(const char* label, int result) {
if (result != 0) { if (result != 0) {
fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
exit(1); abort();
} }
} }

11
util/hash.cc

@ -6,6 +6,13 @@
#include "util/coding.h" #include "util/coding.h"
#include "util/hash.h" #include "util/hash.h"
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
// between switch labels. The real definition should be provided externally.
// This one is a fallback version for unsupported compilers.
#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED do { } while (0)
#endif
namespace leveldb { namespace leveldb {
uint32_t Hash(const char* data, size_t n, uint32_t seed) { uint32_t Hash(const char* data, size_t n, uint32_t seed) {
@ -28,10 +35,10 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) {
switch (limit - data) { switch (limit - data) {
case 3: case 3:
h += data[2] << 16; h += data[2] << 16;
// fall through FALLTHROUGH_INTENDED;
case 2: case 2:
h += data[1] << 8; h += data[1] << 8;
// fall through FALLTHROUGH_INTENDED;
case 1: case 1:
h += data[0]; h += data[0];
h *= m; h *= m;

Loading…
Cancel
Save