leveldb-ruby 0.14 → 0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data/LICENSE +24 -0
  2. data/README +60 -16
  3. data/ext/leveldb/extconf.rb +1 -1
  4. data/ext/leveldb/leveldb.cc +187 -18
  5. data/leveldb/Makefile +82 -96
  6. data/leveldb/build_detect_platform +137 -51
  7. data/leveldb/db/c.cc +110 -0
  8. data/leveldb/db/db_bench.cc +105 -4
  9. data/leveldb/db/db_impl.cc +135 -45
  10. data/leveldb/db/db_impl.h +12 -10
  11. data/leveldb/db/db_test.cc +666 -431
  12. data/leveldb/db/dbformat.cc +20 -0
  13. data/leveldb/db/dbformat.h +12 -0
  14. data/leveldb/db/repair.cc +3 -1
  15. data/leveldb/db/skiplist.h +2 -1
  16. data/leveldb/db/table_cache.cc +42 -16
  17. data/leveldb/db/table_cache.h +11 -0
  18. data/leveldb/db/version_set.cc +46 -41
  19. data/leveldb/db/version_set.h +9 -0
  20. data/leveldb/db/write_batch.cc +13 -4
  21. data/leveldb/db/write_batch_internal.h +2 -0
  22. data/leveldb/db/write_batch_test.cc +31 -0
  23. data/leveldb/include/leveldb/c.h +29 -0
  24. data/leveldb/include/leveldb/db.h +2 -1
  25. data/leveldb/include/leveldb/filter_policy.h +70 -0
  26. data/leveldb/include/leveldb/options.h +8 -0
  27. data/leveldb/include/leveldb/status.h +6 -0
  28. data/leveldb/include/leveldb/table.h +15 -0
  29. data/leveldb/include/leveldb/table_builder.h +1 -0
  30. data/leveldb/port/atomic_pointer.h +13 -5
  31. data/leveldb/port/port.h +0 -2
  32. data/leveldb/port/port_example.h +10 -0
  33. data/leveldb/port/port_posix.cc +4 -0
  34. data/leveldb/port/port_posix.h +24 -9
  35. data/leveldb/table/block.cc +8 -4
  36. data/leveldb/table/block.h +3 -2
  37. data/leveldb/table/filter_block.cc +111 -0
  38. data/leveldb/table/filter_block.h +68 -0
  39. data/leveldb/table/filter_block_test.cc +128 -0
  40. data/leveldb/table/format.cc +17 -7
  41. data/leveldb/table/format.h +9 -4
  42. data/leveldb/table/table.cc +107 -6
  43. data/leveldb/table/table_builder.cc +49 -6
  44. data/leveldb/table/table_test.cc +8 -24
  45. data/leveldb/util/bloom.cc +95 -0
  46. data/leveldb/util/bloom_test.cc +159 -0
  47. data/leveldb/util/coding_test.cc +23 -0
  48. data/leveldb/util/comparator.cc +8 -3
  49. data/leveldb/util/env_posix.cc +46 -4
  50. data/leveldb/util/filter_policy.cc +11 -0
  51. data/leveldb/util/options.cc +2 -1
  52. data/lib/leveldb.rb +31 -5
  53. metadata +227 -109
  54. data/leveldb/port/port_android.cc +0 -64
  55. data/leveldb/port/port_android.h +0 -156
@@ -35,6 +35,17 @@
35
35
 
36
36
  namespace leveldb {
37
37
 
38
+ // Information kept for every waiting writer
39
+ struct DBImpl::Writer {
40
+ Status status;
41
+ WriteBatch* batch;
42
+ bool sync;
43
+ bool done;
44
+ port::CondVar cv;
45
+
46
+ explicit Writer(port::Mutex* mu) : cv(mu) { }
47
+ };
48
+
38
49
  struct DBImpl::CompactionState {
39
50
  Compaction* const compaction;
40
51
 
@@ -76,12 +87,14 @@ static void ClipToRange(T* ptr, V minvalue, V maxvalue) {
76
87
  }
77
88
  Options SanitizeOptions(const std::string& dbname,
78
89
  const InternalKeyComparator* icmp,
90
+ const InternalFilterPolicy* ipolicy,
79
91
  const Options& src) {
80
92
  Options result = src;
81
93
  result.comparator = icmp;
82
- ClipToRange(&result.max_open_files, 20, 50000);
83
- ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
84
- ClipToRange(&result.block_size, 1<<10, 4<<20);
94
+ result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL;
95
+ ClipToRange(&result.max_open_files, 20, 50000);
96
+ ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
97
+ ClipToRange(&result.block_size, 1<<10, 4<<20);
85
98
  if (result.info_log == NULL) {
86
99
  // Open a log file in the same directory as the db
87
100
  src.env->CreateDir(dbname); // In case it does not exist
@@ -101,7 +114,9 @@ Options SanitizeOptions(const std::string& dbname,
101
114
  DBImpl::DBImpl(const Options& options, const std::string& dbname)
102
115
  : env_(options.env),
103
116
  internal_comparator_(options.comparator),
104
- options_(SanitizeOptions(dbname, &internal_comparator_, options)),
117
+ internal_filter_policy_(options.filter_policy),
118
+ options_(SanitizeOptions(
119
+ dbname, &internal_comparator_, &internal_filter_policy_, options)),
105
120
  owns_info_log_(options_.info_log != options.info_log),
106
121
  owns_cache_(options_.block_cache != options.block_cache),
107
122
  dbname_(dbname),
@@ -113,8 +128,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
113
128
  logfile_(NULL),
114
129
  logfile_number_(0),
115
130
  log_(NULL),
116
- logger_(NULL),
117
- logger_cv_(&mutex_),
131
+ tmp_batch_(new WriteBatch),
118
132
  bg_compaction_scheduled_(false),
119
133
  manual_compaction_(NULL) {
120
134
  mem_->Ref();
@@ -144,6 +158,7 @@ DBImpl::~DBImpl() {
144
158
  delete versions_;
145
159
  if (mem_ != NULL) mem_->Unref();
146
160
  if (imm_ != NULL) imm_->Unref();
161
+ delete tmp_batch_;
147
162
  delete log_;
148
163
  delete logfile_;
149
164
  delete table_cache_;
@@ -554,13 +569,11 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) {
554
569
  }
555
570
 
556
571
  Status DBImpl::TEST_CompactMemTable() {
557
- MutexLock l(&mutex_);
558
- LoggerId self;
559
- AcquireLoggingResponsibility(&self);
560
- Status s = MakeRoomForWrite(true /* force compaction */);
561
- ReleaseLoggingResponsibility(&self);
572
+ // NULL batch means just wait for earlier writes to be done
573
+ Status s = Write(WriteOptions(), NULL);
562
574
  if (s.ok()) {
563
575
  // Wait until the compaction completes
576
+ MutexLock l(&mutex_);
564
577
  while (imm_ != NULL && bg_error_.ok()) {
565
578
  bg_cv_.Wait();
566
579
  }
@@ -595,8 +608,21 @@ void DBImpl::BackgroundCall() {
595
608
  MutexLock l(&mutex_);
596
609
  assert(bg_compaction_scheduled_);
597
610
  if (!shutting_down_.Acquire_Load()) {
598
- BackgroundCompaction();
611
+ Status s = BackgroundCompaction();
612
+ if (!s.ok()) {
613
+ // Wait a little bit before retrying background compaction in
614
+ // case this is an environmental problem and we do not want to
615
+ // chew up resources for failed compactions for the duration of
616
+ // the problem.
617
+ bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
618
+ Log(options_.info_log, "Waiting after background compaction error: %s",
619
+ s.ToString().c_str());
620
+ mutex_.Unlock();
621
+ env_->SleepForMicroseconds(1000000);
622
+ mutex_.Lock();
623
+ }
599
624
  }
625
+
600
626
  bg_compaction_scheduled_ = false;
601
627
 
602
628
  // Previous compaction may have produced too many files in a level,
@@ -605,12 +631,11 @@ void DBImpl::BackgroundCall() {
605
631
  bg_cv_.SignalAll();
606
632
  }
607
633
 
608
- void DBImpl::BackgroundCompaction() {
634
+ Status DBImpl::BackgroundCompaction() {
609
635
  mutex_.AssertHeld();
610
636
 
611
637
  if (imm_ != NULL) {
612
- CompactMemTable();
613
- return;
638
+ return CompactMemTable();
614
639
  }
615
640
 
616
641
  Compaction* c;
@@ -685,6 +710,7 @@ void DBImpl::BackgroundCompaction() {
685
710
  }
686
711
  manual_compaction_ = NULL;
687
712
  }
713
+ return status;
688
714
  }
689
715
 
690
716
  void DBImpl::CleanupCompaction(CompactionState* compact) {
@@ -1094,38 +1120,35 @@ Status DBImpl::Delete(const WriteOptions& options, const Slice& key) {
1094
1120
  return DB::Delete(options, key);
1095
1121
  }
1096
1122
 
1097
- // There is at most one thread that is the current logger. This call
1098
- // waits until preceding logger(s) have finished and becomes the
1099
- // current logger.
1100
- void DBImpl::AcquireLoggingResponsibility(LoggerId* self) {
1101
- while (logger_ != NULL) {
1102
- logger_cv_.Wait();
1103
- }
1104
- logger_ = self;
1105
- }
1106
-
1107
- void DBImpl::ReleaseLoggingResponsibility(LoggerId* self) {
1108
- assert(logger_ == self);
1109
- logger_ = NULL;
1110
- logger_cv_.SignalAll();
1111
- }
1123
+ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) {
1124
+ Writer w(&mutex_);
1125
+ w.batch = my_batch;
1126
+ w.sync = options.sync;
1127
+ w.done = false;
1112
1128
 
1113
- Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
1114
- Status status;
1115
1129
  MutexLock l(&mutex_);
1116
- LoggerId self;
1117
- AcquireLoggingResponsibility(&self);
1118
- status = MakeRoomForWrite(false); // May temporarily release lock and wait
1130
+ writers_.push_back(&w);
1131
+ while (!w.done && &w != writers_.front()) {
1132
+ w.cv.Wait();
1133
+ }
1134
+ if (w.done) {
1135
+ return w.status;
1136
+ }
1137
+
1138
+ // May temporarily unlock and wait.
1139
+ Status status = MakeRoomForWrite(my_batch == NULL);
1119
1140
  uint64_t last_sequence = versions_->LastSequence();
1120
- if (status.ok()) {
1141
+ Writer* last_writer = &w;
1142
+ if (status.ok() && my_batch != NULL) { // NULL batch is for compactions
1143
+ WriteBatch* updates = BuildBatchGroup(&last_writer);
1121
1144
  WriteBatchInternal::SetSequence(updates, last_sequence + 1);
1122
1145
  last_sequence += WriteBatchInternal::Count(updates);
1123
1146
 
1124
- // Add to log and apply to memtable. We can release the lock during
1125
- // this phase since the "logger_" flag protects against concurrent
1126
- // loggers and concurrent writes into mem_.
1147
+ // Add to log and apply to memtable. We can release the lock
1148
+ // during this phase since &w is currently responsible for logging
1149
+ // and protects against concurrent loggers and concurrent writes
1150
+ // into mem_.
1127
1151
  {
1128
- assert(logger_ == &self);
1129
1152
  mutex_.Unlock();
1130
1153
  status = log_->AddRecord(WriteBatchInternal::Contents(updates));
1131
1154
  if (status.ok() && options.sync) {
@@ -1135,20 +1158,85 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
1135
1158
  status = WriteBatchInternal::InsertInto(updates, mem_);
1136
1159
  }
1137
1160
  mutex_.Lock();
1138
- assert(logger_ == &self);
1139
1161
  }
1162
+ if (updates == tmp_batch_) tmp_batch_->Clear();
1140
1163
 
1141
1164
  versions_->SetLastSequence(last_sequence);
1142
1165
  }
1143
- ReleaseLoggingResponsibility(&self);
1166
+
1167
+ while (true) {
1168
+ Writer* ready = writers_.front();
1169
+ writers_.pop_front();
1170
+ if (ready != &w) {
1171
+ ready->status = status;
1172
+ ready->done = true;
1173
+ ready->cv.Signal();
1174
+ }
1175
+ if (ready == last_writer) break;
1176
+ }
1177
+
1178
+ // Notify new head of write queue
1179
+ if (!writers_.empty()) {
1180
+ writers_.front()->cv.Signal();
1181
+ }
1182
+
1144
1183
  return status;
1145
1184
  }
1146
1185
 
1186
+ // REQUIRES: Writer list must be non-empty
1187
+ // REQUIRES: First writer must have a non-NULL batch
1188
+ WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) {
1189
+ assert(!writers_.empty());
1190
+ Writer* first = writers_.front();
1191
+ WriteBatch* result = first->batch;
1192
+ assert(result != NULL);
1193
+
1194
+ size_t size = WriteBatchInternal::ByteSize(first->batch);
1195
+
1196
+ // Allow the group to grow up to a maximum size, but if the
1197
+ // original write is small, limit the growth so we do not slow
1198
+ // down the small write too much.
1199
+ size_t max_size = 1 << 20;
1200
+ if (size <= (128<<10)) {
1201
+ max_size = size + (128<<10);
1202
+ }
1203
+
1204
+ *last_writer = first;
1205
+ std::deque<Writer*>::iterator iter = writers_.begin();
1206
+ ++iter; // Advance past "first"
1207
+ for (; iter != writers_.end(); ++iter) {
1208
+ Writer* w = *iter;
1209
+ if (w->sync && !first->sync) {
1210
+ // Do not include a sync write into a batch handled by a non-sync write.
1211
+ break;
1212
+ }
1213
+
1214
+ if (w->batch != NULL) {
1215
+ size += WriteBatchInternal::ByteSize(w->batch);
1216
+ if (size > max_size) {
1217
+ // Do not make batch too big
1218
+ break;
1219
+ }
1220
+
1221
+ // Append to *reuslt
1222
+ if (result == first->batch) {
1223
+ // Switch to temporary batch instead of disturbing caller's batch
1224
+ result = tmp_batch_;
1225
+ assert(WriteBatchInternal::Count(result) == 0);
1226
+ WriteBatchInternal::Append(result, first->batch);
1227
+ }
1228
+ WriteBatchInternal::Append(result, w->batch);
1229
+ }
1230
+ *last_writer = w;
1231
+ }
1232
+ return result;
1233
+ }
1234
+
1147
1235
  // REQUIRES: mutex_ is held
1148
- // REQUIRES: this thread is the current logger
1236
+ // REQUIRES: this thread is currently at the front of the writer queue
1149
1237
  Status DBImpl::MakeRoomForWrite(bool force) {
1150
1238
  mutex_.AssertHeld();
1151
- assert(logger_ != NULL);
1239
+ assert(!writers_.empty());
1152
1240
  bool allow_delay = !force;
1153
1241
  Status s;
1154
1242
  while (true) {
@@ -1188,6 +1276,8 @@ Status DBImpl::MakeRoomForWrite(bool force) {
1188
1276
  WritableFile* lfile = NULL;
1189
1277
  s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);
1190
1278
  if (!s.ok()) {
1279
+ // Avoid chewing through file number space in a tight loop.
1280
+ versions_->ReuseFileNumber(new_log_number);
1191
1281
  break;
1192
1282
  }
1193
1283
  delete log_;
@@ -1356,7 +1446,7 @@ Status DestroyDB(const std::string& dbname, const Options& options) {
1356
1446
  FileType type;
1357
1447
  for (size_t i = 0; i < filenames.size(); i++) {
1358
1448
  if (ParseFileName(filenames[i], &number, &type) &&
1359
- filenames[i] != lockname) { // Lock file will be deleted at end
1449
+ type != kDBLockFile) { // Lock file will be deleted at end
1360
1450
  Status del = env->DeleteFile(dbname + "/" + filenames[i]);
1361
1451
  if (result.ok() && !del.ok()) {
1362
1452
  result = del;
@@ -5,6 +5,7 @@
5
5
  #ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_
6
6
  #define STORAGE_LEVELDB_DB_DB_IMPL_H_
7
7
 
8
+ #include <deque>
8
9
  #include <set>
9
10
  #include "db/dbformat.h"
10
11
  #include "db/log_writer.h"
@@ -59,6 +60,8 @@ class DBImpl : public DB {
59
60
 
60
61
  private:
61
62
  friend class DB;
63
+ struct CompactionState;
64
+ struct Writer;
62
65
 
63
66
  Iterator* NewInternalIterator(const ReadOptions&,
64
67
  SequenceNumber* latest_snapshot);
@@ -85,19 +88,13 @@ class DBImpl : public DB {
85
88
 
86
89
  Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base);
87
90
 
88
- // Only thread is allowed to log at a time.
89
- struct LoggerId { }; // Opaque identifier for logging thread
90
- void AcquireLoggingResponsibility(LoggerId* self);
91
- void ReleaseLoggingResponsibility(LoggerId* self);
92
-
93
91
  Status MakeRoomForWrite(bool force /* compact even if there is room? */);
94
-
95
- struct CompactionState;
92
+ WriteBatch* BuildBatchGroup(Writer** last_writer);
96
93
 
97
94
  void MaybeScheduleCompaction();
98
95
  static void BGWork(void* db);
99
96
  void BackgroundCall();
100
- void BackgroundCompaction();
97
+ Status BackgroundCompaction();
101
98
  void CleanupCompaction(CompactionState* compact);
102
99
  Status DoCompactionWork(CompactionState* compact);
103
100
 
@@ -108,6 +105,7 @@ class DBImpl : public DB {
108
105
  // Constant after construction
109
106
  Env* const env_;
110
107
  const InternalKeyComparator internal_comparator_;
108
+ const InternalFilterPolicy internal_filter_policy_;
111
109
  const Options options_; // options_.comparator == &internal_comparator_
112
110
  bool owns_info_log_;
113
111
  bool owns_cache_;
@@ -129,8 +127,11 @@ class DBImpl : public DB {
129
127
  WritableFile* logfile_;
130
128
  uint64_t logfile_number_;
131
129
  log::Writer* log_;
132
- LoggerId* logger_; // NULL, or the id of the current logging thread
133
- port::CondVar logger_cv_; // For threads waiting to log
130
+
131
+ // Queue of writers.
132
+ std::deque<Writer*> writers_;
133
+ WriteBatch* tmp_batch_;
134
+
134
135
  SnapshotList snapshots_;
135
136
 
136
137
  // Set of table files to protect from deletion because they are
@@ -185,6 +186,7 @@ class DBImpl : public DB {
185
186
  // it is not equal to src.info_log.
186
187
  extern Options SanitizeOptions(const std::string& db,
187
188
  const InternalKeyComparator* icmp,
189
+ const InternalFilterPolicy* ipolicy,
188
190
  const Options& src);
189
191
 
190
192
  } // namespace leveldb
@@ -3,12 +3,15 @@
3
3
  // found in the LICENSE file. See the AUTHORS file for names of contributors.
4
4
 
5
5
  #include "leveldb/db.h"
6
+ #include "leveldb/filter_policy.h"
6
7
  #include "db/db_impl.h"
7
8
  #include "db/filename.h"
8
9
  #include "db/version_set.h"
9
10
  #include "db/write_batch_internal.h"
11
+ #include "leveldb/cache.h"
10
12
  #include "leveldb/env.h"
11
13
  #include "leveldb/table.h"
14
+ #include "util/hash.h"
12
15
  #include "util/logging.h"
13
16
  #include "util/mutexlock.h"
14
17
  #include "util/testharness.h"
@@ -22,6 +25,28 @@ static std::string RandomString(Random* rnd, int len) {
22
25
  return r;
23
26
  }
24
27
 
28
+ namespace {
29
+ class AtomicCounter {
30
+ private:
31
+ port::Mutex mu_;
32
+ int count_;
33
+ public:
34
+ AtomicCounter() : count_(0) { }
35
+ void Increment() {
36
+ MutexLock l(&mu_);
37
+ count_++;
38
+ }
39
+ int Read() {
40
+ MutexLock l(&mu_);
41
+ return count_;
42
+ }
43
+ void Reset() {
44
+ MutexLock l(&mu_);
45
+ count_ = 0;
46
+ }
47
+ };
48
+ }
49
+
25
50
  // Special Env used to delay background operations
26
51
  class SpecialEnv : public EnvWrapper {
27
52
  public:
@@ -31,9 +56,19 @@ class SpecialEnv : public EnvWrapper {
31
56
  // Simulate no-space errors while this pointer is non-NULL.
32
57
  port::AtomicPointer no_space_;
33
58
 
59
+ // Simulate non-writable file system while this pointer is non-NULL
60
+ port::AtomicPointer non_writable_;
61
+
62
+ bool count_random_reads_;
63
+ AtomicCounter random_read_counter_;
64
+
65
+ AtomicCounter sleep_counter_;
66
+
34
67
  explicit SpecialEnv(Env* base) : EnvWrapper(base) {
35
68
  delay_sstable_sync_.Release_Store(NULL);
36
69
  no_space_.Release_Store(NULL);
70
+ non_writable_.Release_Store(NULL);
71
+ count_random_reads_ = false;
37
72
  }
38
73
 
39
74
  Status NewWritableFile(const std::string& f, WritableFile** r) {
@@ -66,6 +101,10 @@ class SpecialEnv : public EnvWrapper {
66
101
  }
67
102
  };
68
103
 
104
+ if (non_writable_.Acquire_Load() != NULL) {
105
+ return Status::IOError("simulated write error");
106
+ }
107
+
69
108
  Status s = target()->NewWritableFile(f, r);
70
109
  if (s.ok()) {
71
110
  if (strstr(f.c_str(), ".sst") != NULL) {
@@ -74,9 +113,50 @@ class SpecialEnv : public EnvWrapper {
74
113
  }
75
114
  return s;
76
115
  }
116
+
117
+ Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) {
118
+ class CountingFile : public RandomAccessFile {
119
+ private:
120
+ RandomAccessFile* target_;
121
+ AtomicCounter* counter_;
122
+ public:
123
+ CountingFile(RandomAccessFile* target, AtomicCounter* counter)
124
+ : target_(target), counter_(counter) {
125
+ }
126
+ virtual ~CountingFile() { delete target_; }
127
+ virtual Status Read(uint64_t offset, size_t n, Slice* result,
128
+ char* scratch) const {
129
+ counter_->Increment();
130
+ return target_->Read(offset, n, result, scratch);
131
+ }
132
+ };
133
+
134
+ Status s = target()->NewRandomAccessFile(f, r);
135
+ if (s.ok() && count_random_reads_) {
136
+ *r = new CountingFile(*r, &random_read_counter_);
137
+ }
138
+ return s;
139
+ }
140
+
141
+ virtual void SleepForMicroseconds(int micros) {
142
+ sleep_counter_.Increment();
143
+ target()->SleepForMicroseconds(micros);
144
+ }
77
145
  };
78
146
 
79
147
  class DBTest {
148
+ private:
149
+ const FilterPolicy* filter_policy_;
150
+
151
+ // Sequence of option configurations to try
152
+ enum OptionConfig {
153
+ kDefault,
154
+ kFilter,
155
+ kUncompressed,
156
+ kEnd
157
+ };
158
+ int option_config_;
159
+
80
160
  public:
81
161
  std::string dbname_;
82
162
  SpecialEnv* env_;
@@ -84,7 +164,9 @@ class DBTest {
84
164
 
85
165
  Options last_options_;
86
166
 
87
- DBTest() : env_(new SpecialEnv(Env::Default())) {
167
+ DBTest() : option_config_(kDefault),
168
+ env_(new SpecialEnv(Env::Default())) {
169
+ filter_policy_ = NewBloomFilterPolicy(10);
88
170
  dbname_ = test::TmpDir() + "/db_test";
89
171
  DestroyDB(dbname_, Options());
90
172
  db_ = NULL;
@@ -95,6 +177,35 @@ class DBTest {
95
177
  delete db_;
96
178
  DestroyDB(dbname_, Options());
97
179
  delete env_;
180
+ delete filter_policy_;
181
+ }
182
+
183
+ // Switch to a fresh database with the next option configuration to
184
+ // test. Return false if there are no more configurations to test.
185
+ bool ChangeOptions() {
186
+ option_config_++;
187
+ if (option_config_ >= kEnd) {
188
+ return false;
189
+ } else {
190
+ DestroyAndReopen();
191
+ return true;
192
+ }
193
+ }
194
+
195
+ // Return the current option configuration.
196
+ Options CurrentOptions() {
197
+ Options options;
198
+ switch (option_config_) {
199
+ case kFilter:
200
+ options.filter_policy = filter_policy_;
201
+ break;
202
+ case kUncompressed:
203
+ options.compression = kNoCompression;
204
+ break;
205
+ default:
206
+ break;
207
+ }
208
+ return options;
98
209
  }
99
210
 
100
211
  DBImpl* dbfull() {
@@ -105,6 +216,11 @@ class DBTest {
105
216
  ASSERT_OK(TryReopen(options));
106
217
  }
107
218
 
219
+ void Close() {
220
+ delete db_;
221
+ db_ = NULL;
222
+ }
223
+
108
224
  void DestroyAndReopen(Options* options = NULL) {
109
225
  delete db_;
110
226
  db_ = NULL;
@@ -119,6 +235,7 @@ class DBTest {
119
235
  if (options != NULL) {
120
236
  opts = *options;
121
237
  } else {
238
+ opts = CurrentOptions();
122
239
  opts.create_if_missing = true;
123
240
  }
124
241
  last_options_ = opts;
@@ -189,8 +306,7 @@ class DBTest {
189
306
  if (!ParseInternalKey(iter->key(), &ikey)) {
190
307
  result += "CORRUPTED";
191
308
  } else {
192
- if (last_options_.comparator->Compare(
193
- ikey.user_key, user_key) != 0) {
309
+ if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) {
194
310
  break;
195
311
  }
196
312
  if (!first) {
@@ -314,135 +430,157 @@ class DBTest {
314
430
  };
315
431
 
316
432
  TEST(DBTest, Empty) {
317
- ASSERT_TRUE(db_ != NULL);
318
- ASSERT_EQ("NOT_FOUND", Get("foo"));
433
+ do {
434
+ ASSERT_TRUE(db_ != NULL);
435
+ ASSERT_EQ("NOT_FOUND", Get("foo"));
436
+ } while (ChangeOptions());
319
437
  }
320
438
 
321
439
  TEST(DBTest, ReadWrite) {
322
- ASSERT_OK(Put("foo", "v1"));
323
- ASSERT_EQ("v1", Get("foo"));
324
- ASSERT_OK(Put("bar", "v2"));
325
- ASSERT_OK(Put("foo", "v3"));
326
- ASSERT_EQ("v3", Get("foo"));
327
- ASSERT_EQ("v2", Get("bar"));
440
+ do {
441
+ ASSERT_OK(Put("foo", "v1"));
442
+ ASSERT_EQ("v1", Get("foo"));
443
+ ASSERT_OK(Put("bar", "v2"));
444
+ ASSERT_OK(Put("foo", "v3"));
445
+ ASSERT_EQ("v3", Get("foo"));
446
+ ASSERT_EQ("v2", Get("bar"));
447
+ } while (ChangeOptions());
328
448
  }
329
449
 
330
450
  TEST(DBTest, PutDeleteGet) {
331
- ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
332
- ASSERT_EQ("v1", Get("foo"));
333
- ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
334
- ASSERT_EQ("v2", Get("foo"));
335
- ASSERT_OK(db_->Delete(WriteOptions(), "foo"));
336
- ASSERT_EQ("NOT_FOUND", Get("foo"));
451
+ do {
452
+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
453
+ ASSERT_EQ("v1", Get("foo"));
454
+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
455
+ ASSERT_EQ("v2", Get("foo"));
456
+ ASSERT_OK(db_->Delete(WriteOptions(), "foo"));
457
+ ASSERT_EQ("NOT_FOUND", Get("foo"));
458
+ } while (ChangeOptions());
337
459
  }
338
460
 
339
461
  TEST(DBTest, GetFromImmutableLayer) {
340
- Options options;
341
- options.env = env_;
342
- options.write_buffer_size = 100000; // Small write buffer
343
- Reopen(&options);
462
+ do {
463
+ Options options = CurrentOptions();
464
+ options.env = env_;
465
+ options.write_buffer_size = 100000; // Small write buffer
466
+ Reopen(&options);
344
467
 
345
- ASSERT_OK(Put("foo", "v1"));
346
- ASSERT_EQ("v1", Get("foo"));
468
+ ASSERT_OK(Put("foo", "v1"));
469
+ ASSERT_EQ("v1", Get("foo"));
347
470
 
348
- env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls
349
- Put("k1", std::string(100000, 'x')); // Fill memtable
350
- Put("k2", std::string(100000, 'y')); // Trigger compaction
351
- ASSERT_EQ("v1", Get("foo"));
352
- env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls
471
+ env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls
472
+ Put("k1", std::string(100000, 'x')); // Fill memtable
473
+ Put("k2", std::string(100000, 'y')); // Trigger compaction
474
+ ASSERT_EQ("v1", Get("foo"));
475
+ env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls
476
+ } while (ChangeOptions());
353
477
  }
354
478
 
355
479
  TEST(DBTest, GetFromVersions) {
356
- ASSERT_OK(Put("foo", "v1"));
357
- dbfull()->TEST_CompactMemTable();
358
- ASSERT_EQ("v1", Get("foo"));
480
+ do {
481
+ ASSERT_OK(Put("foo", "v1"));
482
+ dbfull()->TEST_CompactMemTable();
483
+ ASSERT_EQ("v1", Get("foo"));
484
+ } while (ChangeOptions());
359
485
  }
360
486
 
361
487
  TEST(DBTest, GetSnapshot) {
362
- // Try with both a short key and a long key
363
- for (int i = 0; i < 2; i++) {
364
- std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
365
- ASSERT_OK(Put(key, "v1"));
366
- const Snapshot* s1 = db_->GetSnapshot();
367
- ASSERT_OK(Put(key, "v2"));
368
- ASSERT_EQ("v2", Get(key));
369
- ASSERT_EQ("v1", Get(key, s1));
370
- dbfull()->TEST_CompactMemTable();
371
- ASSERT_EQ("v2", Get(key));
372
- ASSERT_EQ("v1", Get(key, s1));
373
- db_->ReleaseSnapshot(s1);
374
- }
488
+ do {
489
+ // Try with both a short key and a long key
490
+ for (int i = 0; i < 2; i++) {
491
+ std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
492
+ ASSERT_OK(Put(key, "v1"));
493
+ const Snapshot* s1 = db_->GetSnapshot();
494
+ ASSERT_OK(Put(key, "v2"));
495
+ ASSERT_EQ("v2", Get(key));
496
+ ASSERT_EQ("v1", Get(key, s1));
497
+ dbfull()->TEST_CompactMemTable();
498
+ ASSERT_EQ("v2", Get(key));
499
+ ASSERT_EQ("v1", Get(key, s1));
500
+ db_->ReleaseSnapshot(s1);
501
+ }
502
+ } while (ChangeOptions());
375
503
  }
376
504
 
377
505
  TEST(DBTest, GetLevel0Ordering) {
378
- // Check that we process level-0 files in correct order. The code
379
- // below generates two level-0 files where the earlier one comes
380
- // before the later one in the level-0 file list since the earlier
381
- // one has a smaller "smallest" key.
382
- ASSERT_OK(Put("bar", "b"));
383
- ASSERT_OK(Put("foo", "v1"));
384
- dbfull()->TEST_CompactMemTable();
385
- ASSERT_OK(Put("foo", "v2"));
386
- dbfull()->TEST_CompactMemTable();
387
- ASSERT_EQ("v2", Get("foo"));
506
+ do {
507
+ // Check that we process level-0 files in correct order. The code
508
+ // below generates two level-0 files where the earlier one comes
509
+ // before the later one in the level-0 file list since the earlier
510
+ // one has a smaller "smallest" key.
511
+ ASSERT_OK(Put("bar", "b"));
512
+ ASSERT_OK(Put("foo", "v1"));
513
+ dbfull()->TEST_CompactMemTable();
514
+ ASSERT_OK(Put("foo", "v2"));
515
+ dbfull()->TEST_CompactMemTable();
516
+ ASSERT_EQ("v2", Get("foo"));
517
+ } while (ChangeOptions());
388
518
  }
389
519
 
390
520
  TEST(DBTest, GetOrderedByLevels) {
391
- ASSERT_OK(Put("foo", "v1"));
392
- Compact("a", "z");
393
- ASSERT_EQ("v1", Get("foo"));
394
- ASSERT_OK(Put("foo", "v2"));
395
- ASSERT_EQ("v2", Get("foo"));
396
- dbfull()->TEST_CompactMemTable();
397
- ASSERT_EQ("v2", Get("foo"));
521
+ do {
522
+ ASSERT_OK(Put("foo", "v1"));
523
+ Compact("a", "z");
524
+ ASSERT_EQ("v1", Get("foo"));
525
+ ASSERT_OK(Put("foo", "v2"));
526
+ ASSERT_EQ("v2", Get("foo"));
527
+ dbfull()->TEST_CompactMemTable();
528
+ ASSERT_EQ("v2", Get("foo"));
529
+ } while (ChangeOptions());
398
530
  }
399
531
 
400
532
  TEST(DBTest, GetPicksCorrectFile) {
401
- // Arrange to have multiple files in a non-level-0 level.
402
- ASSERT_OK(Put("a", "va"));
403
- Compact("a", "b");
404
- ASSERT_OK(Put("x", "vx"));
405
- Compact("x", "y");
406
- ASSERT_OK(Put("f", "vf"));
407
- Compact("f", "g");
408
- ASSERT_EQ("va", Get("a"));
409
- ASSERT_EQ("vf", Get("f"));
410
- ASSERT_EQ("vx", Get("x"));
533
+ do {
534
+ // Arrange to have multiple files in a non-level-0 level.
535
+ ASSERT_OK(Put("a", "va"));
536
+ Compact("a", "b");
537
+ ASSERT_OK(Put("x", "vx"));
538
+ Compact("x", "y");
539
+ ASSERT_OK(Put("f", "vf"));
540
+ Compact("f", "g");
541
+ ASSERT_EQ("va", Get("a"));
542
+ ASSERT_EQ("vf", Get("f"));
543
+ ASSERT_EQ("vx", Get("x"));
544
+ } while (ChangeOptions());
411
545
  }
412
546
 
413
547
  TEST(DBTest, GetEncountersEmptyLevel) {
414
- // Arrange for the following to happen:
415
- // * sstable A in level 0
416
- // * nothing in level 1
417
- // * sstable B in level 2
418
- // Then do enough Get() calls to arrange for an automatic compaction
419
- // of sstable A. A bug would cause the compaction to be marked as
420
- // occuring at level 1 (instead of the correct level 0).
421
-
422
- // Step 1: First place sstables in levels 0 and 2
423
- int compaction_count = 0;
424
- while (NumTableFilesAtLevel(0) == 0 ||
425
- NumTableFilesAtLevel(2) == 0) {
426
- ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2";
427
- compaction_count++;
428
- Put("a", "begin");
429
- Put("z", "end");
430
- dbfull()->TEST_CompactMemTable();
431
- }
548
+ do {
549
+ // Arrange for the following to happen:
550
+ // * sstable A in level 0
551
+ // * nothing in level 1
552
+ // * sstable B in level 2
553
+ // Then do enough Get() calls to arrange for an automatic compaction
554
+ // of sstable A. A bug would cause the compaction to be marked as
555
+ // occuring at level 1 (instead of the correct level 0).
556
+
557
+ // Step 1: First place sstables in levels 0 and 2
558
+ int compaction_count = 0;
559
+ while (NumTableFilesAtLevel(0) == 0 ||
560
+ NumTableFilesAtLevel(2) == 0) {
561
+ ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2";
562
+ compaction_count++;
563
+ Put("a", "begin");
564
+ Put("z", "end");
565
+ dbfull()->TEST_CompactMemTable();
566
+ }
432
567
 
433
- // Step 2: clear level 1 if necessary.
434
- dbfull()->TEST_CompactRange(1, NULL, NULL);
435
- ASSERT_EQ(NumTableFilesAtLevel(0), 1);
436
- ASSERT_EQ(NumTableFilesAtLevel(1), 0);
437
- ASSERT_EQ(NumTableFilesAtLevel(2), 1);
568
+ // Step 2: clear level 1 if necessary.
569
+ dbfull()->TEST_CompactRange(1, NULL, NULL);
570
+ ASSERT_EQ(NumTableFilesAtLevel(0), 1);
571
+ ASSERT_EQ(NumTableFilesAtLevel(1), 0);
572
+ ASSERT_EQ(NumTableFilesAtLevel(2), 1);
438
573
 
439
- // Step 3: read until level 0 compaction disappears.
440
- int read_count = 0;
441
- while (NumTableFilesAtLevel(0) > 0) {
442
- ASSERT_LE(read_count, 10000) << "did not trigger level 0 compaction";
443
- read_count++;
444
- ASSERT_EQ("NOT_FOUND", Get("missing"));
445
- }
574
+ // Step 3: read a bunch of times
575
+ for (int i = 0; i < 1000; i++) {
576
+ ASSERT_EQ("NOT_FOUND", Get("missing"));
577
+ }
578
+
579
+ // Step 4: Wait for compaction to finish
580
+ env_->SleepForMicroseconds(1000000);
581
+
582
+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);
583
+ } while (ChangeOptions());
446
584
  }
447
585
 
448
586
  TEST(DBTest, IterEmpty) {
@@ -620,69 +758,77 @@ TEST(DBTest, IterSmallAndLargeMix) {
620
758
  }
621
759
 
622
760
  TEST(DBTest, IterMultiWithDelete) {
623
- ASSERT_OK(Put("a", "va"));
624
- ASSERT_OK(Put("b", "vb"));
625
- ASSERT_OK(Put("c", "vc"));
626
- ASSERT_OK(Delete("b"));
627
- ASSERT_EQ("NOT_FOUND", Get("b"));
761
+ do {
762
+ ASSERT_OK(Put("a", "va"));
763
+ ASSERT_OK(Put("b", "vb"));
764
+ ASSERT_OK(Put("c", "vc"));
765
+ ASSERT_OK(Delete("b"));
766
+ ASSERT_EQ("NOT_FOUND", Get("b"));
628
767
 
629
- Iterator* iter = db_->NewIterator(ReadOptions());
630
- iter->Seek("c");
631
- ASSERT_EQ(IterStatus(iter), "c->vc");
632
- iter->Prev();
633
- ASSERT_EQ(IterStatus(iter), "a->va");
634
- delete iter;
768
+ Iterator* iter = db_->NewIterator(ReadOptions());
769
+ iter->Seek("c");
770
+ ASSERT_EQ(IterStatus(iter), "c->vc");
771
+ iter->Prev();
772
+ ASSERT_EQ(IterStatus(iter), "a->va");
773
+ delete iter;
774
+ } while (ChangeOptions());
635
775
  }
636
776
 
637
777
  TEST(DBTest, Recover) {
638
- ASSERT_OK(Put("foo", "v1"));
639
- ASSERT_OK(Put("baz", "v5"));
778
+ do {
779
+ ASSERT_OK(Put("foo", "v1"));
780
+ ASSERT_OK(Put("baz", "v5"));
640
781
 
641
- Reopen();
642
- ASSERT_EQ("v1", Get("foo"));
782
+ Reopen();
783
+ ASSERT_EQ("v1", Get("foo"));
643
784
 
644
- ASSERT_EQ("v1", Get("foo"));
645
- ASSERT_EQ("v5", Get("baz"));
646
- ASSERT_OK(Put("bar", "v2"));
647
- ASSERT_OK(Put("foo", "v3"));
785
+ ASSERT_EQ("v1", Get("foo"));
786
+ ASSERT_EQ("v5", Get("baz"));
787
+ ASSERT_OK(Put("bar", "v2"));
788
+ ASSERT_OK(Put("foo", "v3"));
648
789
 
649
- Reopen();
650
- ASSERT_EQ("v3", Get("foo"));
651
- ASSERT_OK(Put("foo", "v4"));
652
- ASSERT_EQ("v4", Get("foo"));
653
- ASSERT_EQ("v2", Get("bar"));
654
- ASSERT_EQ("v5", Get("baz"));
790
+ Reopen();
791
+ ASSERT_EQ("v3", Get("foo"));
792
+ ASSERT_OK(Put("foo", "v4"));
793
+ ASSERT_EQ("v4", Get("foo"));
794
+ ASSERT_EQ("v2", Get("bar"));
795
+ ASSERT_EQ("v5", Get("baz"));
796
+ } while (ChangeOptions());
655
797
  }
656
798
 
657
799
  TEST(DBTest, RecoveryWithEmptyLog) {
658
- ASSERT_OK(Put("foo", "v1"));
659
- ASSERT_OK(Put("foo", "v2"));
660
- Reopen();
661
- Reopen();
662
- ASSERT_OK(Put("foo", "v3"));
663
- Reopen();
664
- ASSERT_EQ("v3", Get("foo"));
800
+ do {
801
+ ASSERT_OK(Put("foo", "v1"));
802
+ ASSERT_OK(Put("foo", "v2"));
803
+ Reopen();
804
+ Reopen();
805
+ ASSERT_OK(Put("foo", "v3"));
806
+ Reopen();
807
+ ASSERT_EQ("v3", Get("foo"));
808
+ } while (ChangeOptions());
665
809
  }
666
810
 
667
811
  // Check that writes done during a memtable compaction are recovered
668
812
  // if the database is shutdown during the memtable compaction.
669
813
  TEST(DBTest, RecoverDuringMemtableCompaction) {
670
- Options options;
671
- options.env = env_;
672
- options.write_buffer_size = 1000000;
673
- Reopen(&options);
814
+ do {
815
+ Options options = CurrentOptions();
816
+ options.env = env_;
817
+ options.write_buffer_size = 1000000;
818
+ Reopen(&options);
674
819
 
675
- // Trigger a long memtable compaction and reopen the database during it
676
- ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file
677
- ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable
678
- ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction
679
- ASSERT_OK(Put("bar", "v2")); // Goes to new log file
820
+ // Trigger a long memtable compaction and reopen the database during it
821
+ ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file
822
+ ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable
823
+ ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction
824
+ ASSERT_OK(Put("bar", "v2")); // Goes to new log file
680
825
 
681
- Reopen(&options);
682
- ASSERT_EQ("v1", Get("foo"));
683
- ASSERT_EQ("v2", Get("bar"));
684
- ASSERT_EQ(std::string(10000000, 'x'), Get("big1"));
685
- ASSERT_EQ(std::string(1000, 'y'), Get("big2"));
826
+ Reopen(&options);
827
+ ASSERT_EQ("v1", Get("foo"));
828
+ ASSERT_EQ("v2", Get("bar"));
829
+ ASSERT_EQ(std::string(10000000, 'x'), Get("big1"));
830
+ ASSERT_EQ(std::string(1000, 'y'), Get("big2"));
831
+ } while (ChangeOptions());
686
832
  }
687
833
 
688
834
  static std::string Key(int i) {
@@ -692,7 +838,7 @@ static std::string Key(int i) {
692
838
  }
693
839
 
694
840
  TEST(DBTest, MinorCompactionsHappen) {
695
- Options options;
841
+ Options options = CurrentOptions();
696
842
  options.write_buffer_size = 10000;
697
843
  Reopen(&options);
698
844
 
@@ -718,7 +864,7 @@ TEST(DBTest, MinorCompactionsHappen) {
718
864
 
719
865
  TEST(DBTest, RecoverWithLargeLog) {
720
866
  {
721
- Options options;
867
+ Options options = CurrentOptions();
722
868
  Reopen(&options);
723
869
  ASSERT_OK(Put("big1", std::string(200000, '1')));
724
870
  ASSERT_OK(Put("big2", std::string(200000, '2')));
@@ -729,7 +875,7 @@ TEST(DBTest, RecoverWithLargeLog) {
729
875
 
730
876
  // Make sure that if we re-open with a small write buffer size that
731
877
  // we flush table files in the middle of a large log file.
732
- Options options;
878
+ Options options = CurrentOptions();
733
879
  options.write_buffer_size = 100000;
734
880
  Reopen(&options);
735
881
  ASSERT_EQ(NumTableFilesAtLevel(0), 3);
@@ -741,7 +887,7 @@ TEST(DBTest, RecoverWithLargeLog) {
741
887
  }
742
888
 
743
889
  TEST(DBTest, CompactionsGenerateMultipleFiles) {
744
- Options options;
890
+ Options options = CurrentOptions();
745
891
  options.write_buffer_size = 100000000; // Large write buffer
746
892
  Reopen(&options);
747
893
 
@@ -767,7 +913,7 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) {
767
913
  }
768
914
 
769
915
  TEST(DBTest, RepeatedWritesToSameKey) {
770
- Options options;
916
+ Options options = CurrentOptions();
771
917
  options.env = env_;
772
918
  options.write_buffer_size = 100000; // Small write buffer
773
919
  Reopen(&options);
@@ -786,7 +932,7 @@ TEST(DBTest, RepeatedWritesToSameKey) {
786
932
  }
787
933
 
788
934
  TEST(DBTest, SparseMerge) {
789
- Options options;
935
+ Options options = CurrentOptions();
790
936
  options.compression = kNoCompression;
791
937
  Reopen(&options);
792
938
 
@@ -837,87 +983,91 @@ static bool Between(uint64_t val, uint64_t low, uint64_t high) {
837
983
  }
838
984
 
839
985
  TEST(DBTest, ApproximateSizes) {
840
- Options options;
841
- options.write_buffer_size = 100000000; // Large write buffer
842
- options.compression = kNoCompression;
843
- DestroyAndReopen();
986
+ do {
987
+ Options options = CurrentOptions();
988
+ options.write_buffer_size = 100000000; // Large write buffer
989
+ options.compression = kNoCompression;
990
+ DestroyAndReopen();
844
991
 
845
- ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
846
- Reopen(&options);
847
- ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
992
+ ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
993
+ Reopen(&options);
994
+ ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
848
995
 
849
- // Write 8MB (80 values, each 100K)
850
- ASSERT_EQ(NumTableFilesAtLevel(0), 0);
851
- const int N = 80;
852
- Random rnd(301);
853
- for (int i = 0; i < N; i++) {
854
- ASSERT_OK(Put(Key(i), RandomString(&rnd, 100000)));
855
- }
996
+ // Write 8MB (80 values, each 100K)
997
+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);
998
+ const int N = 80;
999
+ static const int S1 = 100000;
1000
+ static const int S2 = 105000; // Allow some expansion from metadata
1001
+ Random rnd(301);
1002
+ for (int i = 0; i < N; i++) {
1003
+ ASSERT_OK(Put(Key(i), RandomString(&rnd, S1)));
1004
+ }
856
1005
 
857
- // 0 because GetApproximateSizes() does not account for memtable space
858
- ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));
1006
+ // 0 because GetApproximateSizes() does not account for memtable space
1007
+ ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));
859
1008
 
860
- // Check sizes across recovery by reopening a few times
861
- for (int run = 0; run < 3; run++) {
862
- Reopen(&options);
1009
+ // Check sizes across recovery by reopening a few times
1010
+ for (int run = 0; run < 3; run++) {
1011
+ Reopen(&options);
863
1012
 
864
- for (int compact_start = 0; compact_start < N; compact_start += 10) {
865
- for (int i = 0; i < N; i += 10) {
866
- ASSERT_TRUE(Between(Size("", Key(i)), 100000*i, 100000*i + 10000));
867
- ASSERT_TRUE(Between(Size("", Key(i)+".suffix"),
868
- 100000 * (i+1), 100000 * (i+1) + 10000));
869
- ASSERT_TRUE(Between(Size(Key(i), Key(i+10)),
870
- 100000 * 10, 100000 * 10 + 10000));
1013
+ for (int compact_start = 0; compact_start < N; compact_start += 10) {
1014
+ for (int i = 0; i < N; i += 10) {
1015
+ ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i));
1016
+ ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1)));
1017
+ ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10));
1018
+ }
1019
+ ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50));
1020
+ ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50));
1021
+
1022
+ std::string cstart_str = Key(compact_start);
1023
+ std::string cend_str = Key(compact_start + 9);
1024
+ Slice cstart = cstart_str;
1025
+ Slice cend = cend_str;
1026
+ dbfull()->TEST_CompactRange(0, &cstart, &cend);
871
1027
  }
872
- ASSERT_TRUE(Between(Size("", Key(50)), 5000000, 5010000));
873
- ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), 5100000, 5110000));
874
-
875
- std::string cstart_str = Key(compact_start);
876
- std::string cend_str = Key(compact_start + 9);
877
- Slice cstart = cstart_str;
878
- Slice cend = cend_str;
879
- dbfull()->TEST_CompactRange(0, &cstart, &cend);
880
- }
881
1028
 
882
- ASSERT_EQ(NumTableFilesAtLevel(0), 0);
883
- ASSERT_GT(NumTableFilesAtLevel(1), 0);
884
- }
1029
+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1030
+ ASSERT_GT(NumTableFilesAtLevel(1), 0);
1031
+ }
1032
+ } while (ChangeOptions());
885
1033
  }
886
1034
 
887
1035
  TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
888
- Options options;
889
- options.compression = kNoCompression;
890
- Reopen();
891
-
892
- Random rnd(301);
893
- std::string big1 = RandomString(&rnd, 100000);
894
- ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000)));
895
- ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000)));
896
- ASSERT_OK(Put(Key(2), big1));
897
- ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000)));
898
- ASSERT_OK(Put(Key(4), big1));
899
- ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000)));
900
- ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000)));
901
- ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000)));
902
-
903
- // Check sizes across recovery by reopening a few times
904
- for (int run = 0; run < 3; run++) {
905
- Reopen(&options);
906
-
907
- ASSERT_TRUE(Between(Size("", Key(0)), 0, 0));
908
- ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000));
909
- ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000));
910
- ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000));
911
- ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000));
912
- ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000));
913
- ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000));
914
- ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000));
915
- ASSERT_TRUE(Between(Size("", Key(8)), 550000, 551000));
916
-
917
- ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000));
1036
+ do {
1037
+ Options options = CurrentOptions();
1038
+ options.compression = kNoCompression;
1039
+ Reopen();
918
1040
 
919
- dbfull()->TEST_CompactRange(0, NULL, NULL);
920
- }
1041
+ Random rnd(301);
1042
+ std::string big1 = RandomString(&rnd, 100000);
1043
+ ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000)));
1044
+ ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000)));
1045
+ ASSERT_OK(Put(Key(2), big1));
1046
+ ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000)));
1047
+ ASSERT_OK(Put(Key(4), big1));
1048
+ ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000)));
1049
+ ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000)));
1050
+ ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000)));
1051
+
1052
+ // Check sizes across recovery by reopening a few times
1053
+ for (int run = 0; run < 3; run++) {
1054
+ Reopen(&options);
1055
+
1056
+ ASSERT_TRUE(Between(Size("", Key(0)), 0, 0));
1057
+ ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000));
1058
+ ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000));
1059
+ ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000));
1060
+ ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000));
1061
+ ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000));
1062
+ ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000));
1063
+ ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000));
1064
+ ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000));
1065
+
1066
+ ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000));
1067
+
1068
+ dbfull()->TEST_CompactRange(0, NULL, NULL);
1069
+ }
1070
+ } while (ChangeOptions());
921
1071
  }
922
1072
 
923
1073
  TEST(DBTest, IteratorPinsRef) {
@@ -943,59 +1093,63 @@ TEST(DBTest, IteratorPinsRef) {
943
1093
  }
944
1094
 
945
1095
  TEST(DBTest, Snapshot) {
946
- Put("foo", "v1");
947
- const Snapshot* s1 = db_->GetSnapshot();
948
- Put("foo", "v2");
949
- const Snapshot* s2 = db_->GetSnapshot();
950
- Put("foo", "v3");
951
- const Snapshot* s3 = db_->GetSnapshot();
952
-
953
- Put("foo", "v4");
954
- ASSERT_EQ("v1", Get("foo", s1));
955
- ASSERT_EQ("v2", Get("foo", s2));
956
- ASSERT_EQ("v3", Get("foo", s3));
957
- ASSERT_EQ("v4", Get("foo"));
958
-
959
- db_->ReleaseSnapshot(s3);
960
- ASSERT_EQ("v1", Get("foo", s1));
961
- ASSERT_EQ("v2", Get("foo", s2));
962
- ASSERT_EQ("v4", Get("foo"));
963
-
964
- db_->ReleaseSnapshot(s1);
965
- ASSERT_EQ("v2", Get("foo", s2));
966
- ASSERT_EQ("v4", Get("foo"));
967
-
968
- db_->ReleaseSnapshot(s2);
969
- ASSERT_EQ("v4", Get("foo"));
970
- }
1096
+ do {
1097
+ Put("foo", "v1");
1098
+ const Snapshot* s1 = db_->GetSnapshot();
1099
+ Put("foo", "v2");
1100
+ const Snapshot* s2 = db_->GetSnapshot();
1101
+ Put("foo", "v3");
1102
+ const Snapshot* s3 = db_->GetSnapshot();
1103
+
1104
+ Put("foo", "v4");
1105
+ ASSERT_EQ("v1", Get("foo", s1));
1106
+ ASSERT_EQ("v2", Get("foo", s2));
1107
+ ASSERT_EQ("v3", Get("foo", s3));
1108
+ ASSERT_EQ("v4", Get("foo"));
1109
+
1110
+ db_->ReleaseSnapshot(s3);
1111
+ ASSERT_EQ("v1", Get("foo", s1));
1112
+ ASSERT_EQ("v2", Get("foo", s2));
1113
+ ASSERT_EQ("v4", Get("foo"));
971
1114
 
972
- TEST(DBTest, HiddenValuesAreRemoved) {
973
- Random rnd(301);
974
- FillLevels("a", "z");
1115
+ db_->ReleaseSnapshot(s1);
1116
+ ASSERT_EQ("v2", Get("foo", s2));
1117
+ ASSERT_EQ("v4", Get("foo"));
975
1118
 
976
- std::string big = RandomString(&rnd, 50000);
977
- Put("foo", big);
978
- Put("pastfoo", "v");
979
- const Snapshot* snapshot = db_->GetSnapshot();
980
- Put("foo", "tiny");
981
- Put("pastfoo2", "v2"); // Advance sequence number one more
1119
+ db_->ReleaseSnapshot(s2);
1120
+ ASSERT_EQ("v4", Get("foo"));
1121
+ } while (ChangeOptions());
1122
+ }
982
1123
 
983
- ASSERT_OK(dbfull()->TEST_CompactMemTable());
984
- ASSERT_GT(NumTableFilesAtLevel(0), 0);
985
-
986
- ASSERT_EQ(big, Get("foo", snapshot));
987
- ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000));
988
- db_->ReleaseSnapshot(snapshot);
989
- ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]");
990
- Slice x("x");
991
- dbfull()->TEST_CompactRange(0, NULL, &x);
992
- ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
993
- ASSERT_EQ(NumTableFilesAtLevel(0), 0);
994
- ASSERT_GE(NumTableFilesAtLevel(1), 1);
995
- dbfull()->TEST_CompactRange(1, NULL, &x);
996
- ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
1124
+ TEST(DBTest, HiddenValuesAreRemoved) {
1125
+ do {
1126
+ Random rnd(301);
1127
+ FillLevels("a", "z");
1128
+
1129
+ std::string big = RandomString(&rnd, 50000);
1130
+ Put("foo", big);
1131
+ Put("pastfoo", "v");
1132
+ const Snapshot* snapshot = db_->GetSnapshot();
1133
+ Put("foo", "tiny");
1134
+ Put("pastfoo2", "v2"); // Advance sequence number one more
1135
+
1136
+ ASSERT_OK(dbfull()->TEST_CompactMemTable());
1137
+ ASSERT_GT(NumTableFilesAtLevel(0), 0);
1138
+
1139
+ ASSERT_EQ(big, Get("foo", snapshot));
1140
+ ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000));
1141
+ db_->ReleaseSnapshot(snapshot);
1142
+ ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]");
1143
+ Slice x("x");
1144
+ dbfull()->TEST_CompactRange(0, NULL, &x);
1145
+ ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
1146
+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1147
+ ASSERT_GE(NumTableFilesAtLevel(1), 1);
1148
+ dbfull()->TEST_CompactRange(1, NULL, &x);
1149
+ ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
997
1150
 
998
- ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000));
1151
+ ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000));
1152
+ } while (ChangeOptions());
999
1153
  }
1000
1154
 
1001
1155
  TEST(DBTest, DeletionMarkers1) {
@@ -1054,85 +1208,87 @@ TEST(DBTest, DeletionMarkers2) {
1054
1208
  }
1055
1209
 
1056
1210
  TEST(DBTest, OverlapInLevel0) {
1057
- ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config";
1211
+ do {
1212
+ ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config";
1058
1213
 
1059
- // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0.
1060
- ASSERT_OK(Put("100", "v100"));
1061
- ASSERT_OK(Put("999", "v999"));
1062
- dbfull()->TEST_CompactMemTable();
1063
- ASSERT_OK(Delete("100"));
1064
- ASSERT_OK(Delete("999"));
1065
- dbfull()->TEST_CompactMemTable();
1066
- ASSERT_EQ("0,1,1", FilesPerLevel());
1067
-
1068
- // Make files spanning the following ranges in level-0:
1069
- // files[0] 200 .. 900
1070
- // files[1] 300 .. 500
1071
- // Note that files are sorted by smallest key.
1072
- ASSERT_OK(Put("300", "v300"));
1073
- ASSERT_OK(Put("500", "v500"));
1074
- dbfull()->TEST_CompactMemTable();
1075
- ASSERT_OK(Put("200", "v200"));
1076
- ASSERT_OK(Put("600", "v600"));
1077
- ASSERT_OK(Put("900", "v900"));
1078
- dbfull()->TEST_CompactMemTable();
1079
- ASSERT_EQ("2,1,1", FilesPerLevel());
1214
+ // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0.
1215
+ ASSERT_OK(Put("100", "v100"));
1216
+ ASSERT_OK(Put("999", "v999"));
1217
+ dbfull()->TEST_CompactMemTable();
1218
+ ASSERT_OK(Delete("100"));
1219
+ ASSERT_OK(Delete("999"));
1220
+ dbfull()->TEST_CompactMemTable();
1221
+ ASSERT_EQ("0,1,1", FilesPerLevel());
1222
+
1223
+ // Make files spanning the following ranges in level-0:
1224
+ // files[0] 200 .. 900
1225
+ // files[1] 300 .. 500
1226
+ // Note that files are sorted by smallest key.
1227
+ ASSERT_OK(Put("300", "v300"));
1228
+ ASSERT_OK(Put("500", "v500"));
1229
+ dbfull()->TEST_CompactMemTable();
1230
+ ASSERT_OK(Put("200", "v200"));
1231
+ ASSERT_OK(Put("600", "v600"));
1232
+ ASSERT_OK(Put("900", "v900"));
1233
+ dbfull()->TEST_CompactMemTable();
1234
+ ASSERT_EQ("2,1,1", FilesPerLevel());
1080
1235
 
1081
- // Compact away the placeholder files we created initially
1082
- dbfull()->TEST_CompactRange(1, NULL, NULL);
1083
- dbfull()->TEST_CompactRange(2, NULL, NULL);
1084
- ASSERT_EQ("2", FilesPerLevel());
1236
+ // Compact away the placeholder files we created initially
1237
+ dbfull()->TEST_CompactRange(1, NULL, NULL);
1238
+ dbfull()->TEST_CompactRange(2, NULL, NULL);
1239
+ ASSERT_EQ("2", FilesPerLevel());
1085
1240
 
1086
- // Do a memtable compaction. Before bug-fix, the compaction would
1087
- // not detect the overlap with level-0 files and would incorrectly place
1088
- // the deletion in a deeper level.
1089
- ASSERT_OK(Delete("600"));
1090
- dbfull()->TEST_CompactMemTable();
1091
- ASSERT_EQ("3", FilesPerLevel());
1092
- ASSERT_EQ("NOT_FOUND", Get("600"));
1241
+ // Do a memtable compaction. Before bug-fix, the compaction would
1242
+ // not detect the overlap with level-0 files and would incorrectly place
1243
+ // the deletion in a deeper level.
1244
+ ASSERT_OK(Delete("600"));
1245
+ dbfull()->TEST_CompactMemTable();
1246
+ ASSERT_EQ("3", FilesPerLevel());
1247
+ ASSERT_EQ("NOT_FOUND", Get("600"));
1248
+ } while (ChangeOptions());
1093
1249
  }
1094
1250
 
1095
1251
  TEST(DBTest, L0_CompactionBug_Issue44_a) {
1096
- Reopen();
1097
- ASSERT_OK(Put("b", "v"));
1098
- Reopen();
1099
- ASSERT_OK(Delete("b"));
1100
- ASSERT_OK(Delete("a"));
1101
- Reopen();
1102
- ASSERT_OK(Delete("a"));
1103
- Reopen();
1104
- ASSERT_OK(Put("a", "v"));
1105
- Reopen();
1106
- Reopen();
1107
- ASSERT_EQ("(a->v)", Contents());
1108
- env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
1109
- ASSERT_EQ("(a->v)", Contents());
1252
+ Reopen();
1253
+ ASSERT_OK(Put("b", "v"));
1254
+ Reopen();
1255
+ ASSERT_OK(Delete("b"));
1256
+ ASSERT_OK(Delete("a"));
1257
+ Reopen();
1258
+ ASSERT_OK(Delete("a"));
1259
+ Reopen();
1260
+ ASSERT_OK(Put("a", "v"));
1261
+ Reopen();
1262
+ Reopen();
1263
+ ASSERT_EQ("(a->v)", Contents());
1264
+ env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
1265
+ ASSERT_EQ("(a->v)", Contents());
1110
1266
  }
1111
1267
 
1112
1268
  TEST(DBTest, L0_CompactionBug_Issue44_b) {
1113
- Reopen();
1114
- Put("","");
1115
- Reopen();
1116
- Delete("e");
1117
- Put("","");
1118
- Reopen();
1119
- Put("c", "cv");
1120
- Reopen();
1121
- Put("","");
1122
- Reopen();
1123
- Put("","");
1124
- env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
1125
- Reopen();
1126
- Put("d","dv");
1127
- Reopen();
1128
- Put("","");
1129
- Reopen();
1130
- Delete("d");
1131
- Delete("b");
1132
- Reopen();
1133
- ASSERT_EQ("(->)(c->cv)", Contents());
1134
- env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
1135
- ASSERT_EQ("(->)(c->cv)", Contents());
1269
+ Reopen();
1270
+ Put("","");
1271
+ Reopen();
1272
+ Delete("e");
1273
+ Put("","");
1274
+ Reopen();
1275
+ Put("c", "cv");
1276
+ Reopen();
1277
+ Put("","");
1278
+ Reopen();
1279
+ Put("","");
1280
+ env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
1281
+ Reopen();
1282
+ Put("d","dv");
1283
+ Reopen();
1284
+ Put("","");
1285
+ Reopen();
1286
+ Delete("d");
1287
+ Delete("b");
1288
+ Reopen();
1289
+ ASSERT_EQ("(->)(c->cv)", Contents());
1290
+ env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
1291
+ ASSERT_EQ("(->)(c->cv)", Contents());
1136
1292
  }
1137
1293
 
1138
1294
  TEST(DBTest, ComparatorCheck) {
@@ -1150,7 +1306,7 @@ TEST(DBTest, ComparatorCheck) {
1150
1306
  }
1151
1307
  };
1152
1308
  NewComparator cmp;
1153
- Options new_options;
1309
+ Options new_options = CurrentOptions();
1154
1310
  new_options.comparator = &cmp;
1155
1311
  Status s = TryReopen(&new_options);
1156
1312
  ASSERT_TRUE(!s.ok());
@@ -1185,9 +1341,10 @@ TEST(DBTest, CustomComparator) {
1185
1341
  }
1186
1342
  };
1187
1343
  NumberComparator cmp;
1188
- Options new_options;
1344
+ Options new_options = CurrentOptions();
1189
1345
  new_options.create_if_missing = true;
1190
1346
  new_options.comparator = &cmp;
1347
+ new_options.filter_policy = NULL; // Cannot use bloom filters
1191
1348
  new_options.write_buffer_size = 1000; // Compact more often
1192
1349
  DestroyAndReopen(&new_options);
1193
1350
  ASSERT_OK(Put("[10]", "ten"));
@@ -1197,6 +1354,8 @@ TEST(DBTest, CustomComparator) {
1197
1354
  ASSERT_EQ("ten", Get("[0xa]"));
1198
1355
  ASSERT_EQ("twenty", Get("[20]"));
1199
1356
  ASSERT_EQ("twenty", Get("[0x14]"));
1357
+ ASSERT_EQ("NOT_FOUND", Get("[15]"));
1358
+ ASSERT_EQ("NOT_FOUND", Get("[0xf]"));
1200
1359
  Compact("[0]", "[9999]");
1201
1360
  }
1202
1361
 
@@ -1285,7 +1444,7 @@ TEST(DBTest, DBOpen_Options) {
1285
1444
 
1286
1445
  // Check that number of files does not grow when we are out of space
1287
1446
  TEST(DBTest, NoSpace) {
1288
- Options options;
1447
+ Options options = CurrentOptions();
1289
1448
  options.env = env_;
1290
1449
  Reopen(&options);
1291
1450
 
@@ -1294,13 +1453,37 @@ TEST(DBTest, NoSpace) {
1294
1453
  Compact("a", "z");
1295
1454
  const int num_files = CountFiles();
1296
1455
  env_->no_space_.Release_Store(env_); // Force out-of-space errors
1297
- for (int i = 0; i < 10; i++) {
1456
+ env_->sleep_counter_.Reset();
1457
+ for (int i = 0; i < 5; i++) {
1298
1458
  for (int level = 0; level < config::kNumLevels-1; level++) {
1299
1459
  dbfull()->TEST_CompactRange(level, NULL, NULL);
1300
1460
  }
1301
1461
  }
1302
1462
  env_->no_space_.Release_Store(NULL);
1303
- ASSERT_LT(CountFiles(), num_files + 5);
1463
+ ASSERT_LT(CountFiles(), num_files + 3);
1464
+
1465
+ // Check that compaction attempts slept after errors
1466
+ ASSERT_GE(env_->sleep_counter_.Read(), 5);
1467
+ }
1468
+
1469
+ TEST(DBTest, NonWritableFileSystem) {
1470
+ Options options = CurrentOptions();
1471
+ options.write_buffer_size = 1000;
1472
+ options.env = env_;
1473
+ Reopen(&options);
1474
+ ASSERT_OK(Put("foo", "v1"));
1475
+ env_->non_writable_.Release_Store(env_); // Force errors for new files
1476
+ std::string big(100000, 'x');
1477
+ int errors = 0;
1478
+ for (int i = 0; i < 20; i++) {
1479
+ fprintf(stderr, "iter %d; errors %d\n", i, errors);
1480
+ if (!Put("foo", big).ok()) {
1481
+ errors++;
1482
+ env_->SleepForMicroseconds(100000);
1483
+ }
1484
+ }
1485
+ ASSERT_GT(errors, 0);
1486
+ env_->non_writable_.Release_Store(NULL);
1304
1487
  }
1305
1488
 
1306
1489
  TEST(DBTest, FilesDeletedAfterCompaction) {
@@ -1314,6 +1497,53 @@ TEST(DBTest, FilesDeletedAfterCompaction) {
1314
1497
  ASSERT_EQ(CountFiles(), num_files);
1315
1498
  }
1316
1499
 
1500
+ TEST(DBTest, BloomFilter) {
1501
+ env_->count_random_reads_ = true;
1502
+ Options options = CurrentOptions();
1503
+ options.env = env_;
1504
+ options.block_cache = NewLRUCache(0); // Prevent cache hits
1505
+ options.filter_policy = NewBloomFilterPolicy(10);
1506
+ Reopen(&options);
1507
+
1508
+ // Populate multiple layers
1509
+ const int N = 10000;
1510
+ for (int i = 0; i < N; i++) {
1511
+ ASSERT_OK(Put(Key(i), Key(i)));
1512
+ }
1513
+ Compact("a", "z");
1514
+ for (int i = 0; i < N; i += 100) {
1515
+ ASSERT_OK(Put(Key(i), Key(i)));
1516
+ }
1517
+ dbfull()->TEST_CompactMemTable();
1518
+
1519
+ // Prevent auto compactions triggered by seeks
1520
+ env_->delay_sstable_sync_.Release_Store(env_);
1521
+
1522
+ // Lookup present keys. Should rarely read from small sstable.
1523
+ env_->random_read_counter_.Reset();
1524
+ for (int i = 0; i < N; i++) {
1525
+ ASSERT_EQ(Key(i), Get(Key(i)));
1526
+ }
1527
+ int reads = env_->random_read_counter_.Read();
1528
+ fprintf(stderr, "%d present => %d reads\n", N, reads);
1529
+ ASSERT_GE(reads, N);
1530
+ ASSERT_LE(reads, N + 2*N/100);
1531
+
1532
+ // Lookup present keys. Should rarely read from either sstable.
1533
+ env_->random_read_counter_.Reset();
1534
+ for (int i = 0; i < N; i++) {
1535
+ ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing"));
1536
+ }
1537
+ reads = env_->random_read_counter_.Read();
1538
+ fprintf(stderr, "%d missing => %d reads\n", N, reads);
1539
+ ASSERT_LE(reads, 3*N/100);
1540
+
1541
+ env_->delay_sstable_sync_.Release_Store(NULL);
1542
+ Close();
1543
+ delete options.block_cache;
1544
+ delete options.filter_policy;
1545
+ }
1546
+
1317
1547
  // Multi-threaded test:
1318
1548
  namespace {
1319
1549
 
@@ -1381,33 +1611,35 @@ static void MTThreadBody(void* arg) {
1381
1611
  } // namespace
1382
1612
 
1383
1613
  TEST(DBTest, MultiThreaded) {
1384
- // Initialize state
1385
- MTState mt;
1386
- mt.test = this;
1387
- mt.stop.Release_Store(0);
1388
- for (int id = 0; id < kNumThreads; id++) {
1389
- mt.counter[id].Release_Store(0);
1390
- mt.thread_done[id].Release_Store(0);
1391
- }
1392
-
1393
- // Start threads
1394
- MTThread thread[kNumThreads];
1395
- for (int id = 0; id < kNumThreads; id++) {
1396
- thread[id].state = &mt;
1397
- thread[id].id = id;
1398
- env_->StartThread(MTThreadBody, &thread[id]);
1399
- }
1400
-
1401
- // Let them run for a while
1402
- env_->SleepForMicroseconds(kTestSeconds * 1000000);
1403
-
1404
- // Stop the threads and wait for them to finish
1405
- mt.stop.Release_Store(&mt);
1406
- for (int id = 0; id < kNumThreads; id++) {
1407
- while (mt.thread_done[id].Acquire_Load() == NULL) {
1408
- env_->SleepForMicroseconds(100000);
1614
+ do {
1615
+ // Initialize state
1616
+ MTState mt;
1617
+ mt.test = this;
1618
+ mt.stop.Release_Store(0);
1619
+ for (int id = 0; id < kNumThreads; id++) {
1620
+ mt.counter[id].Release_Store(0);
1621
+ mt.thread_done[id].Release_Store(0);
1409
1622
  }
1410
- }
1623
+
1624
+ // Start threads
1625
+ MTThread thread[kNumThreads];
1626
+ for (int id = 0; id < kNumThreads; id++) {
1627
+ thread[id].state = &mt;
1628
+ thread[id].id = id;
1629
+ env_->StartThread(MTThreadBody, &thread[id]);
1630
+ }
1631
+
1632
+ // Let them run for a while
1633
+ env_->SleepForMicroseconds(kTestSeconds * 1000000);
1634
+
1635
+ // Stop the threads and wait for them to finish
1636
+ mt.stop.Release_Store(&mt);
1637
+ for (int id = 0; id < kNumThreads; id++) {
1638
+ while (mt.thread_done[id].Acquire_Load() == NULL) {
1639
+ env_->SleepForMicroseconds(100000);
1640
+ }
1641
+ }
1642
+ } while (ChangeOptions());
1411
1643
  }
1412
1644
 
1413
1645
  namespace {
@@ -1573,70 +1805,73 @@ static bool CompareIterators(int step,
1573
1805
 
1574
1806
  TEST(DBTest, Randomized) {
1575
1807
  Random rnd(test::RandomSeed());
1576
- ModelDB model(last_options_);
1577
- const int N = 10000;
1578
- const Snapshot* model_snap = NULL;
1579
- const Snapshot* db_snap = NULL;
1580
- std::string k, v;
1581
- for (int step = 0; step < N; step++) {
1582
- if (step % 100 == 0) {
1583
- fprintf(stderr, "Step %d of %d\n", step, N);
1584
- }
1585
- int p = rnd.Uniform(100);
1586
- if (p < 45) { // Put
1587
- k = RandomKey(&rnd);
1588
- v = RandomString(&rnd,
1589
- rnd.OneIn(20)
1590
- ? 100 + rnd.Uniform(100)
1591
- : rnd.Uniform(8));
1592
- ASSERT_OK(model.Put(WriteOptions(), k, v));
1593
- ASSERT_OK(db_->Put(WriteOptions(), k, v));
1594
-
1595
- } else if (p < 90) { // Delete
1596
- k = RandomKey(&rnd);
1597
- ASSERT_OK(model.Delete(WriteOptions(), k));
1598
- ASSERT_OK(db_->Delete(WriteOptions(), k));
1599
-
1600
-
1601
- } else { // Multi-element batch
1602
- WriteBatch b;
1603
- const int num = rnd.Uniform(8);
1604
- for (int i = 0; i < num; i++) {
1605
- if (i == 0 || !rnd.OneIn(10)) {
1606
- k = RandomKey(&rnd);
1607
- } else {
1608
- // Periodically re-use the same key from the previous iter, so
1609
- // we have multiple entries in the write batch for the same key
1610
- }
1611
- if (rnd.OneIn(2)) {
1612
- v = RandomString(&rnd, rnd.Uniform(10));
1613
- b.Put(k, v);
1614
- } else {
1615
- b.Delete(k);
1808
+ do {
1809
+ ModelDB model(CurrentOptions());
1810
+ const int N = 10000;
1811
+ const Snapshot* model_snap = NULL;
1812
+ const Snapshot* db_snap = NULL;
1813
+ std::string k, v;
1814
+ for (int step = 0; step < N; step++) {
1815
+ if (step % 100 == 0) {
1816
+ fprintf(stderr, "Step %d of %d\n", step, N);
1817
+ }
1818
+ // TODO(sanjay): Test Get() works
1819
+ int p = rnd.Uniform(100);
1820
+ if (p < 45) { // Put
1821
+ k = RandomKey(&rnd);
1822
+ v = RandomString(&rnd,
1823
+ rnd.OneIn(20)
1824
+ ? 100 + rnd.Uniform(100)
1825
+ : rnd.Uniform(8));
1826
+ ASSERT_OK(model.Put(WriteOptions(), k, v));
1827
+ ASSERT_OK(db_->Put(WriteOptions(), k, v));
1828
+
1829
+ } else if (p < 90) { // Delete
1830
+ k = RandomKey(&rnd);
1831
+ ASSERT_OK(model.Delete(WriteOptions(), k));
1832
+ ASSERT_OK(db_->Delete(WriteOptions(), k));
1833
+
1834
+
1835
+ } else { // Multi-element batch
1836
+ WriteBatch b;
1837
+ const int num = rnd.Uniform(8);
1838
+ for (int i = 0; i < num; i++) {
1839
+ if (i == 0 || !rnd.OneIn(10)) {
1840
+ k = RandomKey(&rnd);
1841
+ } else {
1842
+ // Periodically re-use the same key from the previous iter, so
1843
+ // we have multiple entries in the write batch for the same key
1844
+ }
1845
+ if (rnd.OneIn(2)) {
1846
+ v = RandomString(&rnd, rnd.Uniform(10));
1847
+ b.Put(k, v);
1848
+ } else {
1849
+ b.Delete(k);
1850
+ }
1616
1851
  }
1852
+ ASSERT_OK(model.Write(WriteOptions(), &b));
1853
+ ASSERT_OK(db_->Write(WriteOptions(), &b));
1617
1854
  }
1618
- ASSERT_OK(model.Write(WriteOptions(), &b));
1619
- ASSERT_OK(db_->Write(WriteOptions(), &b));
1620
- }
1621
1855
 
1622
- if ((step % 100) == 0) {
1623
- ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
1624
- ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap));
1625
- // Save a snapshot from each DB this time that we'll use next
1626
- // time we compare things, to make sure the current state is
1627
- // preserved with the snapshot
1628
- if (model_snap != NULL) model.ReleaseSnapshot(model_snap);
1629
- if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);
1856
+ if ((step % 100) == 0) {
1857
+ ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
1858
+ ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap));
1859
+ // Save a snapshot from each DB this time that we'll use next
1860
+ // time we compare things, to make sure the current state is
1861
+ // preserved with the snapshot
1862
+ if (model_snap != NULL) model.ReleaseSnapshot(model_snap);
1863
+ if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);
1630
1864
 
1631
- Reopen();
1632
- ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
1865
+ Reopen();
1866
+ ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
1633
1867
 
1634
- model_snap = model.GetSnapshot();
1635
- db_snap = db_->GetSnapshot();
1868
+ model_snap = model.GetSnapshot();
1869
+ db_snap = db_->GetSnapshot();
1870
+ }
1636
1871
  }
1637
- }
1638
- if (model_snap != NULL) model.ReleaseSnapshot(model_snap);
1639
- if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);
1872
+ if (model_snap != NULL) model.ReleaseSnapshot(model_snap);
1873
+ if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);
1874
+ } while (ChangeOptions());
1640
1875
  }
1641
1876
 
1642
1877
  std::string MakeKey(unsigned int num) {