leveldb-ruby 0.14 → 0.15

Sign up to get free protection for your applications and to get access to all the features.
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) {