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.
- data/LICENSE +24 -0
- data/README +60 -16
- data/ext/leveldb/extconf.rb +1 -1
- data/ext/leveldb/leveldb.cc +187 -18
- data/leveldb/Makefile +82 -96
- data/leveldb/build_detect_platform +137 -51
- data/leveldb/db/c.cc +110 -0
- data/leveldb/db/db_bench.cc +105 -4
- data/leveldb/db/db_impl.cc +135 -45
- data/leveldb/db/db_impl.h +12 -10
- data/leveldb/db/db_test.cc +666 -431
- data/leveldb/db/dbformat.cc +20 -0
- data/leveldb/db/dbformat.h +12 -0
- data/leveldb/db/repair.cc +3 -1
- data/leveldb/db/skiplist.h +2 -1
- data/leveldb/db/table_cache.cc +42 -16
- data/leveldb/db/table_cache.h +11 -0
- data/leveldb/db/version_set.cc +46 -41
- data/leveldb/db/version_set.h +9 -0
- data/leveldb/db/write_batch.cc +13 -4
- data/leveldb/db/write_batch_internal.h +2 -0
- data/leveldb/db/write_batch_test.cc +31 -0
- data/leveldb/include/leveldb/c.h +29 -0
- data/leveldb/include/leveldb/db.h +2 -1
- data/leveldb/include/leveldb/filter_policy.h +70 -0
- data/leveldb/include/leveldb/options.h +8 -0
- data/leveldb/include/leveldb/status.h +6 -0
- data/leveldb/include/leveldb/table.h +15 -0
- data/leveldb/include/leveldb/table_builder.h +1 -0
- data/leveldb/port/atomic_pointer.h +13 -5
- data/leveldb/port/port.h +0 -2
- data/leveldb/port/port_example.h +10 -0
- data/leveldb/port/port_posix.cc +4 -0
- data/leveldb/port/port_posix.h +24 -9
- data/leveldb/table/block.cc +8 -4
- data/leveldb/table/block.h +3 -2
- data/leveldb/table/filter_block.cc +111 -0
- data/leveldb/table/filter_block.h +68 -0
- data/leveldb/table/filter_block_test.cc +128 -0
- data/leveldb/table/format.cc +17 -7
- data/leveldb/table/format.h +9 -4
- data/leveldb/table/table.cc +107 -6
- data/leveldb/table/table_builder.cc +49 -6
- data/leveldb/table/table_test.cc +8 -24
- data/leveldb/util/bloom.cc +95 -0
- data/leveldb/util/bloom_test.cc +159 -0
- data/leveldb/util/coding_test.cc +23 -0
- data/leveldb/util/comparator.cc +8 -3
- data/leveldb/util/env_posix.cc +46 -4
- data/leveldb/util/filter_policy.cc +11 -0
- data/leveldb/util/options.cc +2 -1
- data/lib/leveldb.rb +31 -5
- metadata +227 -109
- data/leveldb/port/port_android.cc +0 -64
- data/leveldb/port/port_android.h +0 -156
data/leveldb/db/db_impl.cc
CHANGED
@@ -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
|
-
|
83
|
-
ClipToRange(&result.
|
84
|
-
ClipToRange(&result.
|
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
|
-
|
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
|
-
|
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
|
-
|
558
|
-
|
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
|
-
|
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
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
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
|
-
|
1117
|
-
|
1118
|
-
|
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
|
-
|
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
|
1125
|
-
// this phase since
|
1126
|
-
// loggers and concurrent writes
|
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
|
-
|
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
|
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(
|
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
|
-
|
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;
|
data/leveldb/db/db_impl.h
CHANGED
@@ -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
|
-
|
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
|
-
|
133
|
-
|
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
|
data/leveldb/db/db_test.cc
CHANGED
@@ -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() :
|
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
|
-
|
318
|
-
|
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
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
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
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
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
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
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
|
-
|
346
|
-
|
468
|
+
ASSERT_OK(Put("foo", "v1"));
|
469
|
+
ASSERT_EQ("v1", Get("foo"));
|
347
470
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
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
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
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
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
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
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
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
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
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
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
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
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
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
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
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
|
-
|
639
|
-
|
778
|
+
do {
|
779
|
+
ASSERT_OK(Put("foo", "v1"));
|
780
|
+
ASSERT_OK(Put("baz", "v5"));
|
640
781
|
|
641
|
-
|
642
|
-
|
782
|
+
Reopen();
|
783
|
+
ASSERT_EQ("v1", Get("foo"));
|
643
784
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
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
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
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
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
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
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
814
|
+
do {
|
815
|
+
Options options = CurrentOptions();
|
816
|
+
options.env = env_;
|
817
|
+
options.write_buffer_size = 1000000;
|
818
|
+
Reopen(&options);
|
674
819
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
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
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
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
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
986
|
+
do {
|
987
|
+
Options options = CurrentOptions();
|
988
|
+
options.write_buffer_size = 100000000; // Large write buffer
|
989
|
+
options.compression = kNoCompression;
|
990
|
+
DestroyAndReopen();
|
844
991
|
|
845
|
-
|
846
|
-
|
847
|
-
|
992
|
+
ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
|
993
|
+
Reopen(&options);
|
994
|
+
ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
|
848
995
|
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
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
|
-
|
858
|
-
|
1006
|
+
// 0 because GetApproximateSizes() does not account for memtable space
|
1007
|
+
ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));
|
859
1008
|
|
860
|
-
|
861
|
-
|
862
|
-
|
1009
|
+
// Check sizes across recovery by reopening a few times
|
1010
|
+
for (int run = 0; run < 3; run++) {
|
1011
|
+
Reopen(&options);
|
863
1012
|
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
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
|
-
|
883
|
-
|
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
|
-
|
889
|
-
|
890
|
-
|
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
|
-
|
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
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
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
|
-
|
973
|
-
|
974
|
-
|
1115
|
+
db_->ReleaseSnapshot(s1);
|
1116
|
+
ASSERT_EQ("v2", Get("foo", s2));
|
1117
|
+
ASSERT_EQ("v4", Get("foo"));
|
975
1118
|
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
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
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
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
|
-
|
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
|
-
|
1211
|
+
do {
|
1212
|
+
ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config";
|
1058
1213
|
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
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
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
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
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
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
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
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
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
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
|
-
|
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 +
|
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
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
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
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
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
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
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
|
-
|
1632
|
-
|
1865
|
+
Reopen();
|
1866
|
+
ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
|
1633
1867
|
|
1634
|
-
|
1635
|
-
|
1868
|
+
model_snap = model.GetSnapshot();
|
1869
|
+
db_snap = db_->GetSnapshot();
|
1870
|
+
}
|
1636
1871
|
}
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
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) {
|