leveldb-ruby 0.7 → 0.8
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/README +1 -1
- data/leveldb/Makefile +70 -29
- data/leveldb/build_detect_platform +74 -0
- data/leveldb/db/builder.cc +2 -4
- data/leveldb/db/builder.h +4 -6
- data/leveldb/db/c.cc +471 -0
- data/leveldb/db/corruption_test.cc +21 -16
- data/leveldb/db/db_bench.cc +400 -200
- data/leveldb/db/db_impl.cc +276 -131
- data/leveldb/db/db_impl.h +22 -10
- data/leveldb/db/db_iter.cc +2 -1
- data/leveldb/db/db_test.cc +391 -43
- data/leveldb/db/dbformat.cc +31 -0
- data/leveldb/db/dbformat.h +51 -1
- data/leveldb/db/filename.h +1 -1
- data/leveldb/db/log_format.h +1 -1
- data/leveldb/db/log_reader.cc +16 -11
- data/leveldb/db/memtable.cc +37 -0
- data/leveldb/db/memtable.h +6 -0
- data/leveldb/db/repair.cc +17 -14
- data/leveldb/db/skiplist_test.cc +2 -2
- data/leveldb/db/version_edit.cc +7 -9
- data/leveldb/db/version_edit.h +2 -1
- data/leveldb/db/version_set.cc +416 -104
- data/leveldb/db/version_set.h +78 -14
- data/leveldb/db/version_set_test.cc +179 -0
- data/leveldb/db/write_batch_internal.h +2 -0
- data/leveldb/include/leveldb/c.h +246 -0
- data/leveldb/include/leveldb/db.h +14 -2
- data/leveldb/include/leveldb/env.h +31 -10
- data/leveldb/include/leveldb/options.h +7 -18
- data/leveldb/include/leveldb/slice.h +2 -2
- data/leveldb/include/leveldb/status.h +1 -1
- data/leveldb/port/atomic_pointer.h +144 -0
- data/leveldb/port/port.h +0 -2
- data/leveldb/port/port_android.h +7 -1
- data/leveldb/port/port_example.h +11 -1
- data/leveldb/port/port_posix.h +56 -38
- data/leveldb/table/format.cc +12 -8
- data/leveldb/table/table_test.cc +16 -7
- data/leveldb/util/cache.cc +173 -100
- data/leveldb/util/cache_test.cc +28 -11
- data/leveldb/util/coding.h +4 -4
- data/leveldb/util/comparator.cc +1 -0
- data/leveldb/util/env.cc +10 -5
- data/leveldb/util/env_posix.cc +48 -87
- data/leveldb/util/histogram.cc +11 -0
- data/leveldb/util/histogram.h +1 -0
- data/leveldb/util/posix_logger.h +98 -0
- data/leveldb/util/testharness.cc +12 -0
- data/leveldb/util/testharness.h +10 -1
- data/lib/leveldb.rb +11 -3
- metadata +41 -22
data/leveldb/db/db_impl.h
CHANGED
@@ -38,14 +38,12 @@ class DBImpl : public DB {
|
|
38
38
|
virtual void ReleaseSnapshot(const Snapshot* snapshot);
|
39
39
|
virtual bool GetProperty(const Slice& property, std::string* value);
|
40
40
|
virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes);
|
41
|
+
virtual void CompactRange(const Slice* begin, const Slice* end);
|
41
42
|
|
42
43
|
// Extra methods (for testing) that are not in the public DB interface
|
43
44
|
|
44
|
-
// Compact any files in the named level that overlap [begin
|
45
|
-
void TEST_CompactRange(
|
46
|
-
int level,
|
47
|
-
const std::string& begin,
|
48
|
-
const std::string& end);
|
45
|
+
// Compact any files in the named level that overlap [*begin,*end]
|
46
|
+
void TEST_CompactRange(int level, const Slice* begin, const Slice* end);
|
49
47
|
|
50
48
|
// Force current memtable contents to be compacted.
|
51
49
|
Status TEST_CompactMemTable();
|
@@ -85,7 +83,12 @@ class DBImpl : public DB {
|
|
85
83
|
VersionEdit* edit,
|
86
84
|
SequenceNumber* max_sequence);
|
87
85
|
|
88
|
-
Status WriteLevel0Table(MemTable* mem, VersionEdit* edit);
|
86
|
+
Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base);
|
87
|
+
|
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);
|
89
92
|
|
90
93
|
Status MakeRoomForWrite(bool force /* compact even if there is room? */);
|
91
94
|
|
@@ -119,13 +122,15 @@ class DBImpl : public DB {
|
|
119
122
|
// State below is protected by mutex_
|
120
123
|
port::Mutex mutex_;
|
121
124
|
port::AtomicPointer shutting_down_;
|
122
|
-
port::CondVar bg_cv_; // Signalled when
|
123
|
-
port::CondVar compacting_cv_; // Signalled when !compacting_
|
125
|
+
port::CondVar bg_cv_; // Signalled when background work finishes
|
124
126
|
MemTable* mem_;
|
125
127
|
MemTable* imm_; // Memtable being compacted
|
126
128
|
port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_
|
127
129
|
WritableFile* logfile_;
|
130
|
+
uint64_t logfile_number_;
|
128
131
|
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
|
129
134
|
SnapshotList snapshots_;
|
130
135
|
|
131
136
|
// Set of table files to protect from deletion because they are
|
@@ -135,8 +140,15 @@ class DBImpl : public DB {
|
|
135
140
|
// Has a background compaction been scheduled or is running?
|
136
141
|
bool bg_compaction_scheduled_;
|
137
142
|
|
138
|
-
//
|
139
|
-
|
143
|
+
// Information for a manual compaction
|
144
|
+
struct ManualCompaction {
|
145
|
+
int level;
|
146
|
+
bool done;
|
147
|
+
const InternalKey* begin; // NULL means beginning of key range
|
148
|
+
const InternalKey* end; // NULL means end of key range
|
149
|
+
InternalKey tmp_storage; // Used to keep track of compaction progress
|
150
|
+
};
|
151
|
+
ManualCompaction* manual_compaction_;
|
140
152
|
|
141
153
|
VersionSet* versions_;
|
142
154
|
|
data/leveldb/db/db_iter.cc
CHANGED
@@ -216,7 +216,6 @@ void DBIter::FindPrevUserEntry() {
|
|
216
216
|
|
217
217
|
ValueType value_type = kTypeDeletion;
|
218
218
|
if (iter_->Valid()) {
|
219
|
-
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
|
220
219
|
do {
|
221
220
|
ParsedInternalKey ikey;
|
222
221
|
if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
|
@@ -227,6 +226,7 @@ void DBIter::FindPrevUserEntry() {
|
|
227
226
|
}
|
228
227
|
value_type = ikey.type;
|
229
228
|
if (value_type == kTypeDeletion) {
|
229
|
+
saved_key_.clear();
|
230
230
|
ClearSavedValue();
|
231
231
|
} else {
|
232
232
|
Slice raw_value = iter_->value();
|
@@ -234,6 +234,7 @@ void DBIter::FindPrevUserEntry() {
|
|
234
234
|
std::string empty;
|
235
235
|
swap(empty, saved_value_);
|
236
236
|
}
|
237
|
+
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
|
237
238
|
saved_value_.assign(raw_value.data(), raw_value.size());
|
238
239
|
}
|
239
240
|
}
|
data/leveldb/db/db_test.cc
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
#include "leveldb/env.h"
|
11
11
|
#include "leveldb/table.h"
|
12
12
|
#include "util/logging.h"
|
13
|
+
#include "util/mutexlock.h"
|
13
14
|
#include "util/testharness.h"
|
14
15
|
#include "util/testutil.h"
|
15
16
|
|
@@ -21,15 +22,58 @@ static std::string RandomString(Random* rnd, int len) {
|
|
21
22
|
return r;
|
22
23
|
}
|
23
24
|
|
25
|
+
// Special Env used to delay background operations
|
26
|
+
class SpecialEnv : public EnvWrapper {
|
27
|
+
public:
|
28
|
+
// sstable Sync() calls are blocked while this pointer is non-NULL.
|
29
|
+
port::AtomicPointer delay_sstable_sync_;
|
30
|
+
|
31
|
+
explicit SpecialEnv(Env* base) : EnvWrapper(base) {
|
32
|
+
delay_sstable_sync_.Release_Store(NULL);
|
33
|
+
}
|
34
|
+
|
35
|
+
Status NewWritableFile(const std::string& f, WritableFile** r) {
|
36
|
+
class SSTableFile : public WritableFile {
|
37
|
+
private:
|
38
|
+
SpecialEnv* env_;
|
39
|
+
WritableFile* base_;
|
40
|
+
|
41
|
+
public:
|
42
|
+
SSTableFile(SpecialEnv* env, WritableFile* base)
|
43
|
+
: env_(env),
|
44
|
+
base_(base) {
|
45
|
+
}
|
46
|
+
~SSTableFile() { delete base_; }
|
47
|
+
Status Append(const Slice& data) { return base_->Append(data); }
|
48
|
+
Status Close() { return base_->Close(); }
|
49
|
+
Status Flush() { return base_->Flush(); }
|
50
|
+
Status Sync() {
|
51
|
+
while (env_->delay_sstable_sync_.Acquire_Load() != NULL) {
|
52
|
+
env_->SleepForMicroseconds(100000);
|
53
|
+
}
|
54
|
+
return base_->Sync();
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
Status s = target()->NewWritableFile(f, r);
|
59
|
+
if (s.ok()) {
|
60
|
+
if (strstr(f.c_str(), ".sst") != NULL) {
|
61
|
+
*r = new SSTableFile(this, *r);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
return s;
|
65
|
+
}
|
66
|
+
};
|
67
|
+
|
24
68
|
class DBTest {
|
25
69
|
public:
|
26
70
|
std::string dbname_;
|
27
|
-
|
71
|
+
SpecialEnv* env_;
|
28
72
|
DB* db_;
|
29
73
|
|
30
74
|
Options last_options_;
|
31
75
|
|
32
|
-
DBTest() : env_(Env::Default()) {
|
76
|
+
DBTest() : env_(new SpecialEnv(Env::Default())) {
|
33
77
|
dbname_ = test::TmpDir() + "/db_test";
|
34
78
|
DestroyDB(dbname_, Options());
|
35
79
|
db_ = NULL;
|
@@ -39,6 +83,7 @@ class DBTest {
|
|
39
83
|
~DBTest() {
|
40
84
|
delete db_;
|
41
85
|
DestroyDB(dbname_, Options());
|
86
|
+
delete env_;
|
42
87
|
}
|
43
88
|
|
44
89
|
DBImpl* dbfull() {
|
@@ -142,6 +187,31 @@ class DBTest {
|
|
142
187
|
return atoi(property.c_str());
|
143
188
|
}
|
144
189
|
|
190
|
+
int TotalTableFiles() {
|
191
|
+
int result = 0;
|
192
|
+
for (int level = 0; level < config::kNumLevels; level++) {
|
193
|
+
result += NumTableFilesAtLevel(level);
|
194
|
+
}
|
195
|
+
return result;
|
196
|
+
}
|
197
|
+
|
198
|
+
// Return spread of files per level
|
199
|
+
std::string FilesPerLevel() {
|
200
|
+
std::string result;
|
201
|
+
int last_non_zero_offset = 0;
|
202
|
+
for (int level = 0; level < config::kNumLevels; level++) {
|
203
|
+
int f = NumTableFilesAtLevel(level);
|
204
|
+
char buf[100];
|
205
|
+
snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f);
|
206
|
+
result += buf;
|
207
|
+
if (f > 0) {
|
208
|
+
last_non_zero_offset = result.size();
|
209
|
+
}
|
210
|
+
}
|
211
|
+
result.resize(last_non_zero_offset);
|
212
|
+
return result;
|
213
|
+
}
|
214
|
+
|
145
215
|
uint64_t Size(const Slice& start, const Slice& limit) {
|
146
216
|
Range r(start, limit);
|
147
217
|
uint64_t size;
|
@@ -150,18 +220,25 @@ class DBTest {
|
|
150
220
|
}
|
151
221
|
|
152
222
|
void Compact(const Slice& start, const Slice& limit) {
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
223
|
+
db_->CompactRange(&start, &limit);
|
224
|
+
}
|
225
|
+
|
226
|
+
// Do n memtable compactions, each of which produces an sstable
|
227
|
+
// covering the range [small,large].
|
228
|
+
void MakeTables(int n, const std::string& small, const std::string& large) {
|
229
|
+
for (int i = 0; i < n; i++) {
|
230
|
+
Put(small, "begin");
|
231
|
+
Put(large, "end");
|
232
|
+
dbfull()->TEST_CompactMemTable();
|
162
233
|
}
|
163
234
|
}
|
164
235
|
|
236
|
+
// Prevent pushing of new sstables into deeper levels by adding
|
237
|
+
// tables that cover a specified range to all levels.
|
238
|
+
void FillLevels(const std::string& smallest, const std::string& largest) {
|
239
|
+
MakeTables(config::kNumLevels, smallest, largest);
|
240
|
+
}
|
241
|
+
|
165
242
|
void DumpFileCounts(const char* label) {
|
166
243
|
fprintf(stderr, "---\n%s:\n", label);
|
167
244
|
fprintf(stderr, "maxoverlap: %lld\n",
|
@@ -175,6 +252,12 @@ class DBTest {
|
|
175
252
|
}
|
176
253
|
}
|
177
254
|
|
255
|
+
std::string DumpSSTableList() {
|
256
|
+
std::string property;
|
257
|
+
db_->GetProperty("leveldb.sstables", &property);
|
258
|
+
return property;
|
259
|
+
}
|
260
|
+
|
178
261
|
std::string IterStatus(Iterator* iter) {
|
179
262
|
std::string result;
|
180
263
|
if (iter->Valid()) {
|
@@ -209,6 +292,115 @@ TEST(DBTest, PutDeleteGet) {
|
|
209
292
|
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
210
293
|
}
|
211
294
|
|
295
|
+
TEST(DBTest, GetFromImmutableLayer) {
|
296
|
+
Options options;
|
297
|
+
options.env = env_;
|
298
|
+
options.write_buffer_size = 100000; // Small write buffer
|
299
|
+
Reopen(&options);
|
300
|
+
|
301
|
+
ASSERT_OK(Put("foo", "v1"));
|
302
|
+
ASSERT_EQ("v1", Get("foo"));
|
303
|
+
|
304
|
+
env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls
|
305
|
+
Put("k1", std::string(100000, 'x')); // Fill memtable
|
306
|
+
Put("k2", std::string(100000, 'y')); // Trigger compaction
|
307
|
+
ASSERT_EQ("v1", Get("foo"));
|
308
|
+
env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls
|
309
|
+
}
|
310
|
+
|
311
|
+
TEST(DBTest, GetFromVersions) {
|
312
|
+
ASSERT_OK(Put("foo", "v1"));
|
313
|
+
dbfull()->TEST_CompactMemTable();
|
314
|
+
ASSERT_EQ("v1", Get("foo"));
|
315
|
+
}
|
316
|
+
|
317
|
+
TEST(DBTest, GetSnapshot) {
|
318
|
+
// Try with both a short key and a long key
|
319
|
+
for (int i = 0; i < 2; i++) {
|
320
|
+
std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
|
321
|
+
ASSERT_OK(Put(key, "v1"));
|
322
|
+
const Snapshot* s1 = db_->GetSnapshot();
|
323
|
+
ASSERT_OK(Put(key, "v2"));
|
324
|
+
ASSERT_EQ("v2", Get(key));
|
325
|
+
ASSERT_EQ("v1", Get(key, s1));
|
326
|
+
dbfull()->TEST_CompactMemTable();
|
327
|
+
ASSERT_EQ("v2", Get(key));
|
328
|
+
ASSERT_EQ("v1", Get(key, s1));
|
329
|
+
db_->ReleaseSnapshot(s1);
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
TEST(DBTest, GetLevel0Ordering) {
|
334
|
+
// Check that we process level-0 files in correct order. The code
|
335
|
+
// below generates two level-0 files where the earlier one comes
|
336
|
+
// before the later one in the level-0 file list since the earlier
|
337
|
+
// one has a smaller "smallest" key.
|
338
|
+
ASSERT_OK(Put("bar", "b"));
|
339
|
+
ASSERT_OK(Put("foo", "v1"));
|
340
|
+
dbfull()->TEST_CompactMemTable();
|
341
|
+
ASSERT_OK(Put("foo", "v2"));
|
342
|
+
dbfull()->TEST_CompactMemTable();
|
343
|
+
ASSERT_EQ("v2", Get("foo"));
|
344
|
+
}
|
345
|
+
|
346
|
+
TEST(DBTest, GetOrderedByLevels) {
|
347
|
+
ASSERT_OK(Put("foo", "v1"));
|
348
|
+
Compact("a", "z");
|
349
|
+
ASSERT_EQ("v1", Get("foo"));
|
350
|
+
ASSERT_OK(Put("foo", "v2"));
|
351
|
+
ASSERT_EQ("v2", Get("foo"));
|
352
|
+
dbfull()->TEST_CompactMemTable();
|
353
|
+
ASSERT_EQ("v2", Get("foo"));
|
354
|
+
}
|
355
|
+
|
356
|
+
TEST(DBTest, GetPicksCorrectFile) {
|
357
|
+
// Arrange to have multiple files in a non-level-0 level.
|
358
|
+
ASSERT_OK(Put("a", "va"));
|
359
|
+
Compact("a", "b");
|
360
|
+
ASSERT_OK(Put("x", "vx"));
|
361
|
+
Compact("x", "y");
|
362
|
+
ASSERT_OK(Put("f", "vf"));
|
363
|
+
Compact("f", "g");
|
364
|
+
ASSERT_EQ("va", Get("a"));
|
365
|
+
ASSERT_EQ("vf", Get("f"));
|
366
|
+
ASSERT_EQ("vx", Get("x"));
|
367
|
+
}
|
368
|
+
|
369
|
+
TEST(DBTest, GetEncountersEmptyLevel) {
|
370
|
+
// Arrange for the following to happen:
|
371
|
+
// * sstable A in level 0
|
372
|
+
// * nothing in level 1
|
373
|
+
// * sstable B in level 2
|
374
|
+
// Then do enough Get() calls to arrange for an automatic compaction
|
375
|
+
// of sstable A. A bug would cause the compaction to be marked as
|
376
|
+
// occuring at level 1 (instead of the correct level 0).
|
377
|
+
|
378
|
+
// Step 1: First place sstables in levels 0 and 2
|
379
|
+
int compaction_count = 0;
|
380
|
+
while (NumTableFilesAtLevel(0) == 0 ||
|
381
|
+
NumTableFilesAtLevel(2) == 0) {
|
382
|
+
ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2";
|
383
|
+
compaction_count++;
|
384
|
+
Put("a", "begin");
|
385
|
+
Put("z", "end");
|
386
|
+
dbfull()->TEST_CompactMemTable();
|
387
|
+
}
|
388
|
+
|
389
|
+
// Step 2: clear level 1 if necessary.
|
390
|
+
dbfull()->TEST_CompactRange(1, NULL, NULL);
|
391
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
392
|
+
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
393
|
+
ASSERT_EQ(NumTableFilesAtLevel(2), 1);
|
394
|
+
|
395
|
+
// Step 3: read until level 0 compaction disappears.
|
396
|
+
int read_count = 0;
|
397
|
+
while (NumTableFilesAtLevel(0) > 0) {
|
398
|
+
ASSERT_LE(read_count, 10000) << "did not trigger level 0 compaction";
|
399
|
+
read_count++;
|
400
|
+
ASSERT_EQ("NOT_FOUND", Get("missing"));
|
401
|
+
}
|
402
|
+
}
|
403
|
+
|
212
404
|
TEST(DBTest, IterEmpty) {
|
213
405
|
Iterator* iter = db_->NewIterator(ReadOptions());
|
214
406
|
|
@@ -383,6 +575,21 @@ TEST(DBTest, IterSmallAndLargeMix) {
|
|
383
575
|
delete iter;
|
384
576
|
}
|
385
577
|
|
578
|
+
TEST(DBTest, IterMultiWithDelete) {
|
579
|
+
ASSERT_OK(Put("a", "va"));
|
580
|
+
ASSERT_OK(Put("b", "vb"));
|
581
|
+
ASSERT_OK(Put("c", "vc"));
|
582
|
+
ASSERT_OK(Delete("b"));
|
583
|
+
ASSERT_EQ("NOT_FOUND", Get("b"));
|
584
|
+
|
585
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
586
|
+
iter->Seek("c");
|
587
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
588
|
+
iter->Prev();
|
589
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
590
|
+
delete iter;
|
591
|
+
}
|
592
|
+
|
386
593
|
TEST(DBTest, Recover) {
|
387
594
|
ASSERT_OK(Put("foo", "v1"));
|
388
595
|
ASSERT_OK(Put("baz", "v5"));
|
@@ -413,6 +620,27 @@ TEST(DBTest, RecoveryWithEmptyLog) {
|
|
413
620
|
ASSERT_EQ("v3", Get("foo"));
|
414
621
|
}
|
415
622
|
|
623
|
+
// Check that writes done during a memtable compaction are recovered
|
624
|
+
// if the database is shutdown during the memtable compaction.
|
625
|
+
TEST(DBTest, RecoverDuringMemtableCompaction) {
|
626
|
+
Options options;
|
627
|
+
options.env = env_;
|
628
|
+
options.write_buffer_size = 1000000;
|
629
|
+
Reopen(&options);
|
630
|
+
|
631
|
+
// Trigger a long memtable compaction and reopen the database during it
|
632
|
+
ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file
|
633
|
+
ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable
|
634
|
+
ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction
|
635
|
+
ASSERT_OK(Put("bar", "v2")); // Goes to new log file
|
636
|
+
|
637
|
+
Reopen(&options);
|
638
|
+
ASSERT_EQ("v1", Get("foo"));
|
639
|
+
ASSERT_EQ("v2", Get("bar"));
|
640
|
+
ASSERT_EQ(std::string(10000000, 'x'), Get("big1"));
|
641
|
+
ASSERT_EQ(std::string(1000, 'y'), Get("big2"));
|
642
|
+
}
|
643
|
+
|
416
644
|
static std::string Key(int i) {
|
417
645
|
char buf[100];
|
418
646
|
snprintf(buf, sizeof(buf), "key%06d", i);
|
@@ -426,11 +654,11 @@ TEST(DBTest, MinorCompactionsHappen) {
|
|
426
654
|
|
427
655
|
const int N = 500;
|
428
656
|
|
429
|
-
int starting_num_tables =
|
657
|
+
int starting_num_tables = TotalTableFiles();
|
430
658
|
for (int i = 0; i < N; i++) {
|
431
659
|
ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v')));
|
432
660
|
}
|
433
|
-
int ending_num_tables =
|
661
|
+
int ending_num_tables = TotalTableFiles();
|
434
662
|
ASSERT_GT(ending_num_tables, starting_num_tables);
|
435
663
|
|
436
664
|
for (int i = 0; i < N; i++) {
|
@@ -485,7 +713,7 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) {
|
|
485
713
|
|
486
714
|
// Reopening moves updates to level-0
|
487
715
|
Reopen(&options);
|
488
|
-
dbfull()->TEST_CompactRange(0,
|
716
|
+
dbfull()->TEST_CompactRange(0, NULL, NULL);
|
489
717
|
|
490
718
|
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
491
719
|
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
@@ -494,11 +722,32 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) {
|
|
494
722
|
}
|
495
723
|
}
|
496
724
|
|
725
|
+
TEST(DBTest, RepeatedWritesToSameKey) {
|
726
|
+
Options options;
|
727
|
+
options.env = env_;
|
728
|
+
options.write_buffer_size = 100000; // Small write buffer
|
729
|
+
Reopen(&options);
|
730
|
+
|
731
|
+
// We must have at most one file per level except for level-0,
|
732
|
+
// which may have up to kL0_StopWritesTrigger files.
|
733
|
+
const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger;
|
734
|
+
|
735
|
+
Random rnd(301);
|
736
|
+
std::string value = RandomString(&rnd, 2 * options.write_buffer_size);
|
737
|
+
for (int i = 0; i < 5 * kMaxFiles; i++) {
|
738
|
+
Put("key", value);
|
739
|
+
ASSERT_LE(TotalTableFiles(), kMaxFiles);
|
740
|
+
fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles());
|
741
|
+
}
|
742
|
+
}
|
743
|
+
|
497
744
|
TEST(DBTest, SparseMerge) {
|
498
745
|
Options options;
|
499
746
|
options.compression = kNoCompression;
|
500
747
|
Reopen(&options);
|
501
748
|
|
749
|
+
FillLevels("A", "Z");
|
750
|
+
|
502
751
|
// Suppose there is:
|
503
752
|
// small amount of data with prefix A
|
504
753
|
// large amount of data with prefix B
|
@@ -514,7 +763,8 @@ TEST(DBTest, SparseMerge) {
|
|
514
763
|
Put(key, value);
|
515
764
|
}
|
516
765
|
Put("C", "vc");
|
517
|
-
|
766
|
+
dbfull()->TEST_CompactMemTable();
|
767
|
+
dbfull()->TEST_CompactRange(0, NULL, NULL);
|
518
768
|
|
519
769
|
// Make sparse update
|
520
770
|
Put("A", "va2");
|
@@ -525,9 +775,9 @@ TEST(DBTest, SparseMerge) {
|
|
525
775
|
// Compactions should not cause us to create a situation where
|
526
776
|
// a file overlaps too much data at the next level.
|
527
777
|
ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);
|
528
|
-
dbfull()->TEST_CompactRange(0,
|
778
|
+
dbfull()->TEST_CompactRange(0, NULL, NULL);
|
529
779
|
ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);
|
530
|
-
dbfull()->TEST_CompactRange(1,
|
780
|
+
dbfull()->TEST_CompactRange(1, NULL, NULL);
|
531
781
|
ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);
|
532
782
|
}
|
533
783
|
|
@@ -578,9 +828,11 @@ TEST(DBTest, ApproximateSizes) {
|
|
578
828
|
ASSERT_TRUE(Between(Size("", Key(50)), 5000000, 5010000));
|
579
829
|
ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), 5100000, 5110000));
|
580
830
|
|
581
|
-
|
582
|
-
|
583
|
-
|
831
|
+
std::string cstart_str = Key(compact_start);
|
832
|
+
std::string cend_str = Key(compact_start + 9);
|
833
|
+
Slice cstart = cstart_str;
|
834
|
+
Slice cend = cend_str;
|
835
|
+
dbfull()->TEST_CompactRange(0, &cstart, &cend);
|
584
836
|
}
|
585
837
|
|
586
838
|
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
@@ -620,7 +872,7 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
|
620
872
|
|
621
873
|
ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000));
|
622
874
|
|
623
|
-
dbfull()->TEST_CompactRange(0,
|
875
|
+
dbfull()->TEST_CompactRange(0, NULL, NULL);
|
624
876
|
}
|
625
877
|
}
|
626
878
|
|
@@ -675,6 +927,8 @@ TEST(DBTest, Snapshot) {
|
|
675
927
|
|
676
928
|
TEST(DBTest, HiddenValuesAreRemoved) {
|
677
929
|
Random rnd(301);
|
930
|
+
FillLevels("a", "z");
|
931
|
+
|
678
932
|
std::string big = RandomString(&rnd, 50000);
|
679
933
|
Put("foo", big);
|
680
934
|
Put("pastfoo", "v");
|
@@ -689,11 +943,12 @@ TEST(DBTest, HiddenValuesAreRemoved) {
|
|
689
943
|
ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000));
|
690
944
|
db_->ReleaseSnapshot(snapshot);
|
691
945
|
ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]");
|
692
|
-
|
946
|
+
Slice x("x");
|
947
|
+
dbfull()->TEST_CompactRange(0, NULL, &x);
|
693
948
|
ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
|
694
949
|
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
695
950
|
ASSERT_GE(NumTableFilesAtLevel(1), 1);
|
696
|
-
dbfull()->TEST_CompactRange(1,
|
951
|
+
dbfull()->TEST_CompactRange(1, NULL, &x);
|
697
952
|
ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
|
698
953
|
|
699
954
|
ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000));
|
@@ -702,43 +957,97 @@ TEST(DBTest, HiddenValuesAreRemoved) {
|
|
702
957
|
TEST(DBTest, DeletionMarkers1) {
|
703
958
|
Put("foo", "v1");
|
704
959
|
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
705
|
-
|
706
|
-
|
707
|
-
|
960
|
+
const int last = config::kMaxMemCompactLevel;
|
961
|
+
ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level
|
962
|
+
|
963
|
+
// Place a table at level last-1 to prevent merging with preceding mutation
|
964
|
+
Put("a", "begin");
|
965
|
+
Put("z", "end");
|
966
|
+
dbfull()->TEST_CompactMemTable();
|
967
|
+
ASSERT_EQ(NumTableFilesAtLevel(last), 1);
|
968
|
+
ASSERT_EQ(NumTableFilesAtLevel(last-1), 1);
|
969
|
+
|
708
970
|
Delete("foo");
|
709
971
|
Put("foo", "v2");
|
710
972
|
ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");
|
711
|
-
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
973
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2
|
712
974
|
ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");
|
713
|
-
|
975
|
+
Slice z("z");
|
976
|
+
dbfull()->TEST_CompactRange(last-2, NULL, &z);
|
714
977
|
// DEL eliminated, but v1 remains because we aren't compacting that level
|
715
978
|
// (DEL can be eliminated because v2 hides v1).
|
716
979
|
ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]");
|
717
|
-
dbfull()->TEST_CompactRange(1,
|
718
|
-
// Merging
|
719
|
-
// (as is v1).
|
980
|
+
dbfull()->TEST_CompactRange(last-1, NULL, NULL);
|
981
|
+
// Merging last-1 w/ last, so we are the base level for "foo", so
|
982
|
+
// DEL is removed. (as is v1).
|
720
983
|
ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]");
|
721
984
|
}
|
722
985
|
|
723
986
|
TEST(DBTest, DeletionMarkers2) {
|
724
987
|
Put("foo", "v1");
|
725
988
|
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
726
|
-
|
727
|
-
|
728
|
-
|
989
|
+
const int last = config::kMaxMemCompactLevel;
|
990
|
+
ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level
|
991
|
+
|
992
|
+
// Place a table at level last-1 to prevent merging with preceding mutation
|
993
|
+
Put("a", "begin");
|
994
|
+
Put("z", "end");
|
995
|
+
dbfull()->TEST_CompactMemTable();
|
996
|
+
ASSERT_EQ(NumTableFilesAtLevel(last), 1);
|
997
|
+
ASSERT_EQ(NumTableFilesAtLevel(last-1), 1);
|
998
|
+
|
729
999
|
Delete("foo");
|
730
1000
|
ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
|
731
|
-
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
1001
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2
|
732
1002
|
ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
|
733
|
-
dbfull()->TEST_CompactRange(
|
734
|
-
// DEL kept:
|
1003
|
+
dbfull()->TEST_CompactRange(last-2, NULL, NULL);
|
1004
|
+
// DEL kept: "last" file overlaps
|
735
1005
|
ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
|
736
|
-
dbfull()->TEST_CompactRange(1,
|
737
|
-
// Merging
|
738
|
-
// (as is v1).
|
1006
|
+
dbfull()->TEST_CompactRange(last-1, NULL, NULL);
|
1007
|
+
// Merging last-1 w/ last, so we are the base level for "foo", so
|
1008
|
+
// DEL is removed. (as is v1).
|
739
1009
|
ASSERT_EQ(AllEntriesFor("foo"), "[ ]");
|
740
1010
|
}
|
741
1011
|
|
1012
|
+
TEST(DBTest, OverlapInLevel0) {
|
1013
|
+
ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config";
|
1014
|
+
|
1015
|
+
// Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0.
|
1016
|
+
ASSERT_OK(Put("100", "v100"));
|
1017
|
+
ASSERT_OK(Put("999", "v999"));
|
1018
|
+
dbfull()->TEST_CompactMemTable();
|
1019
|
+
ASSERT_OK(Delete("100"));
|
1020
|
+
ASSERT_OK(Delete("999"));
|
1021
|
+
dbfull()->TEST_CompactMemTable();
|
1022
|
+
ASSERT_EQ("0,1,1", FilesPerLevel());
|
1023
|
+
|
1024
|
+
// Make files spanning the following ranges in level-0:
|
1025
|
+
// files[0] 200 .. 900
|
1026
|
+
// files[1] 300 .. 500
|
1027
|
+
// Note that files are sorted by smallest key.
|
1028
|
+
ASSERT_OK(Put("300", "v300"));
|
1029
|
+
ASSERT_OK(Put("500", "v500"));
|
1030
|
+
dbfull()->TEST_CompactMemTable();
|
1031
|
+
ASSERT_OK(Put("200", "v200"));
|
1032
|
+
ASSERT_OK(Put("600", "v600"));
|
1033
|
+
ASSERT_OK(Put("900", "v900"));
|
1034
|
+
dbfull()->TEST_CompactMemTable();
|
1035
|
+
ASSERT_EQ("2,1,1", FilesPerLevel());
|
1036
|
+
|
1037
|
+
// Compact away the placeholder files we created initially
|
1038
|
+
dbfull()->TEST_CompactRange(1, NULL, NULL);
|
1039
|
+
dbfull()->TEST_CompactRange(2, NULL, NULL);
|
1040
|
+
ASSERT_EQ("2", FilesPerLevel());
|
1041
|
+
|
1042
|
+
// Do a memtable compaction. Before bug-fix, the compaction would
|
1043
|
+
// not detect the overlap with level-0 files and would incorrectly place
|
1044
|
+
// the deletion in a deeper level.
|
1045
|
+
ASSERT_OK(Delete("600"));
|
1046
|
+
dbfull()->TEST_CompactMemTable();
|
1047
|
+
ASSERT_EQ("3", FilesPerLevel());
|
1048
|
+
ASSERT_EQ("NOT_FOUND", Get("600"));
|
1049
|
+
}
|
1050
|
+
|
742
1051
|
TEST(DBTest, ComparatorCheck) {
|
743
1052
|
class NewComparator : public Comparator {
|
744
1053
|
public:
|
@@ -762,6 +1071,40 @@ TEST(DBTest, ComparatorCheck) {
|
|
762
1071
|
<< s.ToString();
|
763
1072
|
}
|
764
1073
|
|
1074
|
+
TEST(DBTest, ManualCompaction) {
|
1075
|
+
ASSERT_EQ(config::kMaxMemCompactLevel, 2)
|
1076
|
+
<< "Need to update this test to match kMaxMemCompactLevel";
|
1077
|
+
|
1078
|
+
MakeTables(3, "p", "q");
|
1079
|
+
ASSERT_EQ("1,1,1", FilesPerLevel());
|
1080
|
+
|
1081
|
+
// Compaction range falls before files
|
1082
|
+
Compact("", "c");
|
1083
|
+
ASSERT_EQ("1,1,1", FilesPerLevel());
|
1084
|
+
|
1085
|
+
// Compaction range falls after files
|
1086
|
+
Compact("r", "z");
|
1087
|
+
ASSERT_EQ("1,1,1", FilesPerLevel());
|
1088
|
+
|
1089
|
+
// Compaction range overlaps files
|
1090
|
+
Compact("p1", "p9");
|
1091
|
+
ASSERT_EQ("0,0,1", FilesPerLevel());
|
1092
|
+
|
1093
|
+
// Populate a different range
|
1094
|
+
MakeTables(3, "c", "e");
|
1095
|
+
ASSERT_EQ("1,1,2", FilesPerLevel());
|
1096
|
+
|
1097
|
+
// Compact just the new range
|
1098
|
+
Compact("b", "f");
|
1099
|
+
ASSERT_EQ("0,0,2", FilesPerLevel());
|
1100
|
+
|
1101
|
+
// Compact all
|
1102
|
+
MakeTables(1, "a", "z");
|
1103
|
+
ASSERT_EQ("0,1,2", FilesPerLevel());
|
1104
|
+
db_->CompactRange(NULL, NULL);
|
1105
|
+
ASSERT_EQ("0,0,1", FilesPerLevel());
|
1106
|
+
}
|
1107
|
+
|
765
1108
|
TEST(DBTest, DBOpen_Options) {
|
766
1109
|
std::string dbname = test::TmpDir() + "/db_options_test";
|
767
1110
|
DestroyDB(dbname, Options());
|
@@ -941,7 +1284,6 @@ class ModelDB: public DB {
|
|
941
1284
|
delete reinterpret_cast<const ModelSnapshot*>(snapshot);
|
942
1285
|
}
|
943
1286
|
virtual Status Write(const WriteOptions& options, WriteBatch* batch) {
|
944
|
-
assert(options.post_write_snapshot == NULL); // Not supported
|
945
1287
|
class Handler : public WriteBatch::Handler {
|
946
1288
|
public:
|
947
1289
|
KVMap* map_;
|
@@ -965,6 +1307,9 @@ class ModelDB: public DB {
|
|
965
1307
|
sizes[i] = 0;
|
966
1308
|
}
|
967
1309
|
}
|
1310
|
+
virtual void CompactRange(const Slice* start, const Slice* end) {
|
1311
|
+
}
|
1312
|
+
|
968
1313
|
private:
|
969
1314
|
class ModelIter: public Iterator {
|
970
1315
|
public:
|
@@ -1145,6 +1490,9 @@ void BM_LogAndApply(int iters, int num_base_files) {
|
|
1145
1490
|
|
1146
1491
|
Env* env = Env::Default();
|
1147
1492
|
|
1493
|
+
port::Mutex mu;
|
1494
|
+
MutexLock l(&mu);
|
1495
|
+
|
1148
1496
|
InternalKeyComparator cmp(BytewiseComparator());
|
1149
1497
|
Options options;
|
1150
1498
|
VersionSet vset(dbname, &options, NULL, &cmp);
|
@@ -1156,7 +1504,7 @@ void BM_LogAndApply(int iters, int num_base_files) {
|
|
1156
1504
|
InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion);
|
1157
1505
|
vbase.AddFile(2, fnum++, 1 /* file size */, start, limit);
|
1158
1506
|
}
|
1159
|
-
ASSERT_OK(vset.LogAndApply(&vbase));
|
1507
|
+
ASSERT_OK(vset.LogAndApply(&vbase, &mu));
|
1160
1508
|
|
1161
1509
|
uint64_t start_micros = env->NowMicros();
|
1162
1510
|
|
@@ -1166,7 +1514,7 @@ void BM_LogAndApply(int iters, int num_base_files) {
|
|
1166
1514
|
InternalKey start(MakeKey(2*fnum), 1, kTypeValue);
|
1167
1515
|
InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion);
|
1168
1516
|
vedit.AddFile(2, fnum++, 1 /* file size */, start, limit);
|
1169
|
-
vset.LogAndApply(&vedit);
|
1517
|
+
vset.LogAndApply(&vedit, &mu);
|
1170
1518
|
}
|
1171
1519
|
uint64_t stop_micros = env->NowMicros();
|
1172
1520
|
unsigned int us = stop_micros - start_micros;
|