leveldb-ruby 0.1
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 +17 -0
- data/ext/leveldb/extconf.rb +10 -0
- data/ext/leveldb/leveldb.cc +181 -0
- data/leveldb/Makefile +172 -0
- data/leveldb/db/builder.cc +90 -0
- data/leveldb/db/builder.h +36 -0
- data/leveldb/db/corruption_test.cc +354 -0
- data/leveldb/db/db_bench.cc +677 -0
- data/leveldb/db/db_impl.cc +1236 -0
- data/leveldb/db/db_impl.h +180 -0
- data/leveldb/db/db_iter.cc +298 -0
- data/leveldb/db/db_iter.h +26 -0
- data/leveldb/db/db_test.cc +1192 -0
- data/leveldb/db/dbformat.cc +87 -0
- data/leveldb/db/dbformat.h +165 -0
- data/leveldb/db/dbformat_test.cc +112 -0
- data/leveldb/db/filename.cc +135 -0
- data/leveldb/db/filename.h +80 -0
- data/leveldb/db/filename_test.cc +122 -0
- data/leveldb/db/log_format.h +35 -0
- data/leveldb/db/log_reader.cc +254 -0
- data/leveldb/db/log_reader.h +108 -0
- data/leveldb/db/log_test.cc +500 -0
- data/leveldb/db/log_writer.cc +103 -0
- data/leveldb/db/log_writer.h +48 -0
- data/leveldb/db/memtable.cc +108 -0
- data/leveldb/db/memtable.h +85 -0
- data/leveldb/db/repair.cc +384 -0
- data/leveldb/db/skiplist.h +378 -0
- data/leveldb/db/skiplist_test.cc +378 -0
- data/leveldb/db/snapshot.h +66 -0
- data/leveldb/db/table_cache.cc +95 -0
- data/leveldb/db/table_cache.h +50 -0
- data/leveldb/db/version_edit.cc +268 -0
- data/leveldb/db/version_edit.h +106 -0
- data/leveldb/db/version_edit_test.cc +46 -0
- data/leveldb/db/version_set.cc +1060 -0
- data/leveldb/db/version_set.h +306 -0
- data/leveldb/db/write_batch.cc +138 -0
- data/leveldb/db/write_batch_internal.h +45 -0
- data/leveldb/db/write_batch_test.cc +89 -0
- data/leveldb/include/leveldb/cache.h +99 -0
- data/leveldb/include/leveldb/comparator.h +63 -0
- data/leveldb/include/leveldb/db.h +148 -0
- data/leveldb/include/leveldb/env.h +302 -0
- data/leveldb/include/leveldb/iterator.h +100 -0
- data/leveldb/include/leveldb/options.h +198 -0
- data/leveldb/include/leveldb/slice.h +109 -0
- data/leveldb/include/leveldb/status.h +100 -0
- data/leveldb/include/leveldb/table.h +70 -0
- data/leveldb/include/leveldb/table_builder.h +91 -0
- data/leveldb/include/leveldb/write_batch.h +64 -0
- data/leveldb/port/port.h +23 -0
- data/leveldb/port/port_android.cc +64 -0
- data/leveldb/port/port_android.h +150 -0
- data/leveldb/port/port_chromium.cc +80 -0
- data/leveldb/port/port_chromium.h +97 -0
- data/leveldb/port/port_example.h +115 -0
- data/leveldb/port/port_osx.cc +50 -0
- data/leveldb/port/port_osx.h +125 -0
- data/leveldb/port/port_posix.cc +50 -0
- data/leveldb/port/port_posix.h +94 -0
- data/leveldb/port/sha1_portable.cc +298 -0
- data/leveldb/port/sha1_portable.h +25 -0
- data/leveldb/port/sha1_test.cc +39 -0
- data/leveldb/port/win/stdint.h +24 -0
- data/leveldb/table/block.cc +263 -0
- data/leveldb/table/block.h +43 -0
- data/leveldb/table/block_builder.cc +109 -0
- data/leveldb/table/block_builder.h +57 -0
- data/leveldb/table/format.cc +131 -0
- data/leveldb/table/format.h +103 -0
- data/leveldb/table/iterator.cc +67 -0
- data/leveldb/table/iterator_wrapper.h +63 -0
- data/leveldb/table/merger.cc +197 -0
- data/leveldb/table/merger.h +26 -0
- data/leveldb/table/table.cc +175 -0
- data/leveldb/table/table_builder.cc +227 -0
- data/leveldb/table/table_test.cc +845 -0
- data/leveldb/table/two_level_iterator.cc +182 -0
- data/leveldb/table/two_level_iterator.h +34 -0
- data/leveldb/util/arena.cc +68 -0
- data/leveldb/util/arena.h +68 -0
- data/leveldb/util/arena_test.cc +68 -0
- data/leveldb/util/cache.cc +255 -0
- data/leveldb/util/cache_test.cc +169 -0
- data/leveldb/util/coding.cc +194 -0
- data/leveldb/util/coding.h +104 -0
- data/leveldb/util/coding_test.cc +173 -0
- data/leveldb/util/comparator.cc +72 -0
- data/leveldb/util/crc32c.cc +332 -0
- data/leveldb/util/crc32c.h +45 -0
- data/leveldb/util/crc32c_test.cc +72 -0
- data/leveldb/util/env.cc +77 -0
- data/leveldb/util/env_chromium.cc +612 -0
- data/leveldb/util/env_posix.cc +606 -0
- data/leveldb/util/env_test.cc +102 -0
- data/leveldb/util/hash.cc +45 -0
- data/leveldb/util/hash.h +19 -0
- data/leveldb/util/histogram.cc +128 -0
- data/leveldb/util/histogram.h +41 -0
- data/leveldb/util/logging.cc +81 -0
- data/leveldb/util/logging.h +47 -0
- data/leveldb/util/mutexlock.h +39 -0
- data/leveldb/util/options.cc +28 -0
- data/leveldb/util/random.h +59 -0
- data/leveldb/util/status.cc +75 -0
- data/leveldb/util/testharness.cc +65 -0
- data/leveldb/util/testharness.h +129 -0
- data/leveldb/util/testutil.cc +51 -0
- data/leveldb/util/testutil.h +53 -0
- data/lib/leveldb.rb +36 -0
- metadata +183 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
4
|
+
|
|
5
|
+
#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_
|
|
6
|
+
#define STORAGE_LEVELDB_DB_DB_ITER_H_
|
|
7
|
+
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include "leveldb/db.h"
|
|
10
|
+
#include "db/dbformat.h"
|
|
11
|
+
|
|
12
|
+
namespace leveldb {
|
|
13
|
+
|
|
14
|
+
// Return a new iterator that converts internal keys (yielded by
|
|
15
|
+
// "*internal_iter") that were live at the specified "sequence" number
|
|
16
|
+
// into appropriate user keys.
|
|
17
|
+
extern Iterator* NewDBIterator(
|
|
18
|
+
const std::string* dbname,
|
|
19
|
+
Env* env,
|
|
20
|
+
const Comparator* user_key_comparator,
|
|
21
|
+
Iterator* internal_iter,
|
|
22
|
+
const SequenceNumber& sequence);
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#endif // STORAGE_LEVELDB_DB_DB_ITER_H_
|
|
@@ -0,0 +1,1192 @@
|
|
|
1
|
+
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
4
|
+
|
|
5
|
+
#include "leveldb/db.h"
|
|
6
|
+
#include "db/db_impl.h"
|
|
7
|
+
#include "db/filename.h"
|
|
8
|
+
#include "db/version_set.h"
|
|
9
|
+
#include "db/write_batch_internal.h"
|
|
10
|
+
#include "leveldb/env.h"
|
|
11
|
+
#include "leveldb/table.h"
|
|
12
|
+
#include "util/logging.h"
|
|
13
|
+
#include "util/testharness.h"
|
|
14
|
+
#include "util/testutil.h"
|
|
15
|
+
|
|
16
|
+
namespace leveldb {
|
|
17
|
+
|
|
18
|
+
static std::string RandomString(Random* rnd, int len) {
|
|
19
|
+
std::string r;
|
|
20
|
+
test::RandomString(rnd, len, &r);
|
|
21
|
+
return r;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class DBTest {
|
|
25
|
+
public:
|
|
26
|
+
std::string dbname_;
|
|
27
|
+
Env* env_;
|
|
28
|
+
DB* db_;
|
|
29
|
+
|
|
30
|
+
Options last_options_;
|
|
31
|
+
|
|
32
|
+
DBTest() : env_(Env::Default()) {
|
|
33
|
+
dbname_ = test::TmpDir() + "/db_test";
|
|
34
|
+
DestroyDB(dbname_, Options());
|
|
35
|
+
db_ = NULL;
|
|
36
|
+
Reopen();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
~DBTest() {
|
|
40
|
+
delete db_;
|
|
41
|
+
DestroyDB(dbname_, Options());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
DBImpl* dbfull() {
|
|
45
|
+
return reinterpret_cast<DBImpl*>(db_);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void Reopen(Options* options = NULL) {
|
|
49
|
+
ASSERT_OK(TryReopen(options));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void DestroyAndReopen(Options* options = NULL) {
|
|
53
|
+
delete db_;
|
|
54
|
+
db_ = NULL;
|
|
55
|
+
DestroyDB(dbname_, Options());
|
|
56
|
+
ASSERT_OK(TryReopen(options));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Status TryReopen(Options* options) {
|
|
60
|
+
delete db_;
|
|
61
|
+
db_ = NULL;
|
|
62
|
+
Options opts;
|
|
63
|
+
if (options != NULL) {
|
|
64
|
+
opts = *options;
|
|
65
|
+
} else {
|
|
66
|
+
opts.create_if_missing = true;
|
|
67
|
+
}
|
|
68
|
+
last_options_ = opts;
|
|
69
|
+
|
|
70
|
+
return DB::Open(opts, dbname_, &db_);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
Status Put(const std::string& k, const std::string& v) {
|
|
74
|
+
return db_->Put(WriteOptions(), k, v);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Status Delete(const std::string& k) {
|
|
78
|
+
return db_->Delete(WriteOptions(), k);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
std::string Get(const std::string& k, const Snapshot* snapshot = NULL) {
|
|
82
|
+
ReadOptions options;
|
|
83
|
+
options.snapshot = snapshot;
|
|
84
|
+
std::string result;
|
|
85
|
+
Status s = db_->Get(options, k, &result);
|
|
86
|
+
if (s.IsNotFound()) {
|
|
87
|
+
result = "NOT_FOUND";
|
|
88
|
+
} else if (!s.ok()) {
|
|
89
|
+
result = s.ToString();
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
std::string AllEntriesFor(const Slice& user_key) {
|
|
95
|
+
Iterator* iter = dbfull()->TEST_NewInternalIterator();
|
|
96
|
+
InternalKey target(user_key, kMaxSequenceNumber, kTypeValue);
|
|
97
|
+
iter->Seek(target.Encode());
|
|
98
|
+
std::string result;
|
|
99
|
+
if (!iter->status().ok()) {
|
|
100
|
+
result = iter->status().ToString();
|
|
101
|
+
} else {
|
|
102
|
+
result = "[ ";
|
|
103
|
+
bool first = true;
|
|
104
|
+
while (iter->Valid()) {
|
|
105
|
+
ParsedInternalKey ikey;
|
|
106
|
+
if (!ParseInternalKey(iter->key(), &ikey)) {
|
|
107
|
+
result += "CORRUPTED";
|
|
108
|
+
} else {
|
|
109
|
+
if (last_options_.comparator->Compare(
|
|
110
|
+
ikey.user_key, user_key) != 0) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (!first) {
|
|
114
|
+
result += ", ";
|
|
115
|
+
}
|
|
116
|
+
first = false;
|
|
117
|
+
switch (ikey.type) {
|
|
118
|
+
case kTypeValue:
|
|
119
|
+
result += iter->value().ToString();
|
|
120
|
+
break;
|
|
121
|
+
case kTypeDeletion:
|
|
122
|
+
result += "DEL";
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
iter->Next();
|
|
127
|
+
}
|
|
128
|
+
if (!first) {
|
|
129
|
+
result += " ";
|
|
130
|
+
}
|
|
131
|
+
result += "]";
|
|
132
|
+
}
|
|
133
|
+
delete iter;
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
int NumTableFilesAtLevel(int level) {
|
|
138
|
+
std::string property;
|
|
139
|
+
ASSERT_TRUE(
|
|
140
|
+
db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level),
|
|
141
|
+
&property));
|
|
142
|
+
return atoi(property.c_str());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
uint64_t Size(const Slice& start, const Slice& limit) {
|
|
146
|
+
Range r(start, limit);
|
|
147
|
+
uint64_t size;
|
|
148
|
+
db_->GetApproximateSizes(&r, 1, &size);
|
|
149
|
+
return size;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void Compact(const Slice& start, const Slice& limit) {
|
|
153
|
+
dbfull()->TEST_CompactMemTable();
|
|
154
|
+
int max_level_with_files = 1;
|
|
155
|
+
for (int level = 1; level < config::kNumLevels; level++) {
|
|
156
|
+
if (NumTableFilesAtLevel(level) > 0) {
|
|
157
|
+
max_level_with_files = level;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (int level = 0; level < max_level_with_files; level++) {
|
|
161
|
+
dbfull()->TEST_CompactRange(level, "", "~");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
void DumpFileCounts(const char* label) {
|
|
166
|
+
fprintf(stderr, "---\n%s:\n", label);
|
|
167
|
+
fprintf(stderr, "maxoverlap: %lld\n",
|
|
168
|
+
static_cast<long long>(
|
|
169
|
+
dbfull()->TEST_MaxNextLevelOverlappingBytes()));
|
|
170
|
+
for (int level = 0; level < config::kNumLevels; level++) {
|
|
171
|
+
int num = NumTableFilesAtLevel(level);
|
|
172
|
+
if (num > 0) {
|
|
173
|
+
fprintf(stderr, " level %3d : %d files\n", level, num);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
std::string IterStatus(Iterator* iter) {
|
|
179
|
+
std::string result;
|
|
180
|
+
if (iter->Valid()) {
|
|
181
|
+
result = iter->key().ToString() + "->" + iter->value().ToString();
|
|
182
|
+
} else {
|
|
183
|
+
result = "(invalid)";
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
TEST(DBTest, Empty) {
|
|
190
|
+
ASSERT_TRUE(db_ != NULL);
|
|
191
|
+
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
TEST(DBTest, ReadWrite) {
|
|
195
|
+
ASSERT_OK(Put("foo", "v1"));
|
|
196
|
+
ASSERT_EQ("v1", Get("foo"));
|
|
197
|
+
ASSERT_OK(Put("bar", "v2"));
|
|
198
|
+
ASSERT_OK(Put("foo", "v3"));
|
|
199
|
+
ASSERT_EQ("v3", Get("foo"));
|
|
200
|
+
ASSERT_EQ("v2", Get("bar"));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
TEST(DBTest, PutDeleteGet) {
|
|
204
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
|
|
205
|
+
ASSERT_EQ("v1", Get("foo"));
|
|
206
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
|
|
207
|
+
ASSERT_EQ("v2", Get("foo"));
|
|
208
|
+
ASSERT_OK(db_->Delete(WriteOptions(), "foo"));
|
|
209
|
+
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
TEST(DBTest, IterEmpty) {
|
|
213
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
214
|
+
|
|
215
|
+
iter->SeekToFirst();
|
|
216
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
217
|
+
|
|
218
|
+
iter->SeekToLast();
|
|
219
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
220
|
+
|
|
221
|
+
iter->Seek("foo");
|
|
222
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
223
|
+
|
|
224
|
+
delete iter;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
TEST(DBTest, IterSingle) {
|
|
228
|
+
ASSERT_OK(Put("a", "va"));
|
|
229
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
230
|
+
|
|
231
|
+
iter->SeekToFirst();
|
|
232
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
233
|
+
iter->Next();
|
|
234
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
235
|
+
iter->SeekToFirst();
|
|
236
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
237
|
+
iter->Prev();
|
|
238
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
239
|
+
|
|
240
|
+
iter->SeekToLast();
|
|
241
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
242
|
+
iter->Next();
|
|
243
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
244
|
+
iter->SeekToLast();
|
|
245
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
246
|
+
iter->Prev();
|
|
247
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
248
|
+
|
|
249
|
+
iter->Seek("");
|
|
250
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
251
|
+
iter->Next();
|
|
252
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
253
|
+
|
|
254
|
+
iter->Seek("a");
|
|
255
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
256
|
+
iter->Next();
|
|
257
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
258
|
+
|
|
259
|
+
iter->Seek("b");
|
|
260
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
261
|
+
|
|
262
|
+
delete iter;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
TEST(DBTest, IterMulti) {
|
|
266
|
+
ASSERT_OK(Put("a", "va"));
|
|
267
|
+
ASSERT_OK(Put("b", "vb"));
|
|
268
|
+
ASSERT_OK(Put("c", "vc"));
|
|
269
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
270
|
+
|
|
271
|
+
iter->SeekToFirst();
|
|
272
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
273
|
+
iter->Next();
|
|
274
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
275
|
+
iter->Next();
|
|
276
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
277
|
+
iter->Next();
|
|
278
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
279
|
+
iter->SeekToFirst();
|
|
280
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
281
|
+
iter->Prev();
|
|
282
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
283
|
+
|
|
284
|
+
iter->SeekToLast();
|
|
285
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
286
|
+
iter->Prev();
|
|
287
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
288
|
+
iter->Prev();
|
|
289
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
290
|
+
iter->Prev();
|
|
291
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
292
|
+
iter->SeekToLast();
|
|
293
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
294
|
+
iter->Next();
|
|
295
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
296
|
+
|
|
297
|
+
iter->Seek("");
|
|
298
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
299
|
+
iter->Seek("a");
|
|
300
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
301
|
+
iter->Seek("ax");
|
|
302
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
303
|
+
iter->Seek("b");
|
|
304
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
305
|
+
iter->Seek("z");
|
|
306
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
307
|
+
|
|
308
|
+
// Switch from reverse to forward
|
|
309
|
+
iter->SeekToLast();
|
|
310
|
+
iter->Prev();
|
|
311
|
+
iter->Prev();
|
|
312
|
+
iter->Next();
|
|
313
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
314
|
+
|
|
315
|
+
// Switch from forward to reverse
|
|
316
|
+
iter->SeekToFirst();
|
|
317
|
+
iter->Next();
|
|
318
|
+
iter->Next();
|
|
319
|
+
iter->Prev();
|
|
320
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
321
|
+
|
|
322
|
+
// Make sure iter stays at snapshot
|
|
323
|
+
ASSERT_OK(Put("a", "va2"));
|
|
324
|
+
ASSERT_OK(Put("a2", "va3"));
|
|
325
|
+
ASSERT_OK(Put("b", "vb2"));
|
|
326
|
+
ASSERT_OK(Put("c", "vc2"));
|
|
327
|
+
ASSERT_OK(Delete("b"));
|
|
328
|
+
iter->SeekToFirst();
|
|
329
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
330
|
+
iter->Next();
|
|
331
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
332
|
+
iter->Next();
|
|
333
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
334
|
+
iter->Next();
|
|
335
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
336
|
+
iter->SeekToLast();
|
|
337
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
338
|
+
iter->Prev();
|
|
339
|
+
ASSERT_EQ(IterStatus(iter), "b->vb");
|
|
340
|
+
iter->Prev();
|
|
341
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
342
|
+
iter->Prev();
|
|
343
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
344
|
+
|
|
345
|
+
delete iter;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
TEST(DBTest, IterSmallAndLargeMix) {
|
|
349
|
+
ASSERT_OK(Put("a", "va"));
|
|
350
|
+
ASSERT_OK(Put("b", std::string(100000, 'b')));
|
|
351
|
+
ASSERT_OK(Put("c", "vc"));
|
|
352
|
+
ASSERT_OK(Put("d", std::string(100000, 'd')));
|
|
353
|
+
ASSERT_OK(Put("e", std::string(100000, 'e')));
|
|
354
|
+
|
|
355
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
356
|
+
|
|
357
|
+
iter->SeekToFirst();
|
|
358
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
359
|
+
iter->Next();
|
|
360
|
+
ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));
|
|
361
|
+
iter->Next();
|
|
362
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
363
|
+
iter->Next();
|
|
364
|
+
ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));
|
|
365
|
+
iter->Next();
|
|
366
|
+
ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));
|
|
367
|
+
iter->Next();
|
|
368
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
369
|
+
|
|
370
|
+
iter->SeekToLast();
|
|
371
|
+
ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));
|
|
372
|
+
iter->Prev();
|
|
373
|
+
ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));
|
|
374
|
+
iter->Prev();
|
|
375
|
+
ASSERT_EQ(IterStatus(iter), "c->vc");
|
|
376
|
+
iter->Prev();
|
|
377
|
+
ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));
|
|
378
|
+
iter->Prev();
|
|
379
|
+
ASSERT_EQ(IterStatus(iter), "a->va");
|
|
380
|
+
iter->Prev();
|
|
381
|
+
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
|
382
|
+
|
|
383
|
+
delete iter;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
TEST(DBTest, Recover) {
|
|
387
|
+
ASSERT_OK(Put("foo", "v1"));
|
|
388
|
+
ASSERT_OK(Put("baz", "v5"));
|
|
389
|
+
|
|
390
|
+
Reopen();
|
|
391
|
+
ASSERT_EQ("v1", Get("foo"));
|
|
392
|
+
|
|
393
|
+
ASSERT_EQ("v1", Get("foo"));
|
|
394
|
+
ASSERT_EQ("v5", Get("baz"));
|
|
395
|
+
ASSERT_OK(Put("bar", "v2"));
|
|
396
|
+
ASSERT_OK(Put("foo", "v3"));
|
|
397
|
+
|
|
398
|
+
Reopen();
|
|
399
|
+
ASSERT_EQ("v3", Get("foo"));
|
|
400
|
+
ASSERT_OK(Put("foo", "v4"));
|
|
401
|
+
ASSERT_EQ("v4", Get("foo"));
|
|
402
|
+
ASSERT_EQ("v2", Get("bar"));
|
|
403
|
+
ASSERT_EQ("v5", Get("baz"));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
TEST(DBTest, RecoveryWithEmptyLog) {
|
|
407
|
+
ASSERT_OK(Put("foo", "v1"));
|
|
408
|
+
ASSERT_OK(Put("foo", "v2"));
|
|
409
|
+
Reopen();
|
|
410
|
+
Reopen();
|
|
411
|
+
ASSERT_OK(Put("foo", "v3"));
|
|
412
|
+
Reopen();
|
|
413
|
+
ASSERT_EQ("v3", Get("foo"));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
static std::string Key(int i) {
|
|
417
|
+
char buf[100];
|
|
418
|
+
snprintf(buf, sizeof(buf), "key%06d", i);
|
|
419
|
+
return std::string(buf);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
TEST(DBTest, MinorCompactionsHappen) {
|
|
423
|
+
Options options;
|
|
424
|
+
options.write_buffer_size = 10000;
|
|
425
|
+
Reopen(&options);
|
|
426
|
+
|
|
427
|
+
const int N = 500;
|
|
428
|
+
|
|
429
|
+
int starting_num_tables = NumTableFilesAtLevel(0);
|
|
430
|
+
for (int i = 0; i < N; i++) {
|
|
431
|
+
ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v')));
|
|
432
|
+
}
|
|
433
|
+
int ending_num_tables = NumTableFilesAtLevel(0);
|
|
434
|
+
ASSERT_GT(ending_num_tables, starting_num_tables);
|
|
435
|
+
|
|
436
|
+
for (int i = 0; i < N; i++) {
|
|
437
|
+
ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i)));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
Reopen();
|
|
441
|
+
|
|
442
|
+
for (int i = 0; i < N; i++) {
|
|
443
|
+
ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i)));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
TEST(DBTest, RecoverWithLargeLog) {
|
|
448
|
+
{
|
|
449
|
+
Options options;
|
|
450
|
+
Reopen(&options);
|
|
451
|
+
ASSERT_OK(Put("big1", std::string(200000, '1')));
|
|
452
|
+
ASSERT_OK(Put("big2", std::string(200000, '2')));
|
|
453
|
+
ASSERT_OK(Put("small3", std::string(10, '3')));
|
|
454
|
+
ASSERT_OK(Put("small4", std::string(10, '4')));
|
|
455
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Make sure that if we re-open with a small write buffer size that
|
|
459
|
+
// we flush table files in the middle of a large log file.
|
|
460
|
+
Options options;
|
|
461
|
+
options.write_buffer_size = 100000;
|
|
462
|
+
Reopen(&options);
|
|
463
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 3);
|
|
464
|
+
ASSERT_EQ(std::string(200000, '1'), Get("big1"));
|
|
465
|
+
ASSERT_EQ(std::string(200000, '2'), Get("big2"));
|
|
466
|
+
ASSERT_EQ(std::string(10, '3'), Get("small3"));
|
|
467
|
+
ASSERT_EQ(std::string(10, '4'), Get("small4"));
|
|
468
|
+
ASSERT_GT(NumTableFilesAtLevel(0), 1);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
TEST(DBTest, CompactionsGenerateMultipleFiles) {
|
|
472
|
+
Options options;
|
|
473
|
+
options.write_buffer_size = 100000000; // Large write buffer
|
|
474
|
+
Reopen(&options);
|
|
475
|
+
|
|
476
|
+
Random rnd(301);
|
|
477
|
+
|
|
478
|
+
// Write 8MB (80 values, each 100K)
|
|
479
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
|
480
|
+
std::vector<std::string> values;
|
|
481
|
+
for (int i = 0; i < 80; i++) {
|
|
482
|
+
values.push_back(RandomString(&rnd, 100000));
|
|
483
|
+
ASSERT_OK(Put(Key(i), values[i]));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Reopening moves updates to level-0
|
|
487
|
+
Reopen(&options);
|
|
488
|
+
dbfull()->TEST_CompactRange(0, "", Key(100000));
|
|
489
|
+
|
|
490
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
|
491
|
+
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
|
492
|
+
for (int i = 0; i < 80; i++) {
|
|
493
|
+
ASSERT_EQ(Get(Key(i)), values[i]);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
TEST(DBTest, SparseMerge) {
|
|
498
|
+
Options options;
|
|
499
|
+
options.compression = kNoCompression;
|
|
500
|
+
Reopen(&options);
|
|
501
|
+
|
|
502
|
+
// Suppose there is:
|
|
503
|
+
// small amount of data with prefix A
|
|
504
|
+
// large amount of data with prefix B
|
|
505
|
+
// small amount of data with prefix C
|
|
506
|
+
// and that recent updates have made small changes to all three prefixes.
|
|
507
|
+
// Check that we do not do a compaction that merges all of B in one shot.
|
|
508
|
+
const std::string value(1000, 'x');
|
|
509
|
+
Put("A", "va");
|
|
510
|
+
// Write approximately 100MB of "B" values
|
|
511
|
+
for (int i = 0; i < 100000; i++) {
|
|
512
|
+
char key[100];
|
|
513
|
+
snprintf(key, sizeof(key), "B%010d", i);
|
|
514
|
+
Put(key, value);
|
|
515
|
+
}
|
|
516
|
+
Put("C", "vc");
|
|
517
|
+
Compact("", "z");
|
|
518
|
+
|
|
519
|
+
// Make sparse update
|
|
520
|
+
Put("A", "va2");
|
|
521
|
+
Put("B100", "bvalue2");
|
|
522
|
+
Put("C", "vc2");
|
|
523
|
+
dbfull()->TEST_CompactMemTable();
|
|
524
|
+
|
|
525
|
+
// Compactions should not cause us to create a situation where
|
|
526
|
+
// a file overlaps too much data at the next level.
|
|
527
|
+
ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);
|
|
528
|
+
dbfull()->TEST_CompactRange(0, "", "z");
|
|
529
|
+
ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);
|
|
530
|
+
dbfull()->TEST_CompactRange(1, "", "z");
|
|
531
|
+
ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
static bool Between(uint64_t val, uint64_t low, uint64_t high) {
|
|
535
|
+
bool result = (val >= low) && (val <= high);
|
|
536
|
+
if (!result) {
|
|
537
|
+
fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
|
|
538
|
+
(unsigned long long)(val),
|
|
539
|
+
(unsigned long long)(low),
|
|
540
|
+
(unsigned long long)(high));
|
|
541
|
+
}
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
TEST(DBTest, ApproximateSizes) {
|
|
546
|
+
Options options;
|
|
547
|
+
options.write_buffer_size = 100000000; // Large write buffer
|
|
548
|
+
options.compression = kNoCompression;
|
|
549
|
+
DestroyAndReopen();
|
|
550
|
+
|
|
551
|
+
ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
|
|
552
|
+
Reopen(&options);
|
|
553
|
+
ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));
|
|
554
|
+
|
|
555
|
+
// Write 8MB (80 values, each 100K)
|
|
556
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
|
557
|
+
const int N = 80;
|
|
558
|
+
Random rnd(301);
|
|
559
|
+
for (int i = 0; i < N; i++) {
|
|
560
|
+
ASSERT_OK(Put(Key(i), RandomString(&rnd, 100000)));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// 0 because GetApproximateSizes() does not account for memtable space
|
|
564
|
+
ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));
|
|
565
|
+
|
|
566
|
+
// Check sizes across recovery by reopening a few times
|
|
567
|
+
for (int run = 0; run < 3; run++) {
|
|
568
|
+
Reopen(&options);
|
|
569
|
+
|
|
570
|
+
for (int compact_start = 0; compact_start < N; compact_start += 10) {
|
|
571
|
+
for (int i = 0; i < N; i += 10) {
|
|
572
|
+
ASSERT_TRUE(Between(Size("", Key(i)), 100000*i, 100000*i + 10000));
|
|
573
|
+
ASSERT_TRUE(Between(Size("", Key(i)+".suffix"),
|
|
574
|
+
100000 * (i+1), 100000 * (i+1) + 10000));
|
|
575
|
+
ASSERT_TRUE(Between(Size(Key(i), Key(i+10)),
|
|
576
|
+
100000 * 10, 100000 * 10 + 10000));
|
|
577
|
+
}
|
|
578
|
+
ASSERT_TRUE(Between(Size("", Key(50)), 5000000, 5010000));
|
|
579
|
+
ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), 5100000, 5110000));
|
|
580
|
+
|
|
581
|
+
dbfull()->TEST_CompactRange(0,
|
|
582
|
+
Key(compact_start),
|
|
583
|
+
Key(compact_start + 9));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
|
587
|
+
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
|
592
|
+
Options options;
|
|
593
|
+
options.compression = kNoCompression;
|
|
594
|
+
Reopen();
|
|
595
|
+
|
|
596
|
+
Random rnd(301);
|
|
597
|
+
std::string big1 = RandomString(&rnd, 100000);
|
|
598
|
+
ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000)));
|
|
599
|
+
ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000)));
|
|
600
|
+
ASSERT_OK(Put(Key(2), big1));
|
|
601
|
+
ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000)));
|
|
602
|
+
ASSERT_OK(Put(Key(4), big1));
|
|
603
|
+
ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000)));
|
|
604
|
+
ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000)));
|
|
605
|
+
ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000)));
|
|
606
|
+
|
|
607
|
+
// Check sizes across recovery by reopening a few times
|
|
608
|
+
for (int run = 0; run < 3; run++) {
|
|
609
|
+
Reopen(&options);
|
|
610
|
+
|
|
611
|
+
ASSERT_TRUE(Between(Size("", Key(0)), 0, 0));
|
|
612
|
+
ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000));
|
|
613
|
+
ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000));
|
|
614
|
+
ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000));
|
|
615
|
+
ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000));
|
|
616
|
+
ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000));
|
|
617
|
+
ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000));
|
|
618
|
+
ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000));
|
|
619
|
+
ASSERT_TRUE(Between(Size("", Key(8)), 550000, 551000));
|
|
620
|
+
|
|
621
|
+
ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000));
|
|
622
|
+
|
|
623
|
+
dbfull()->TEST_CompactRange(0, Key(0), Key(100));
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
TEST(DBTest, IteratorPinsRef) {
|
|
628
|
+
Put("foo", "hello");
|
|
629
|
+
|
|
630
|
+
// Get iterator that will yield the current contents of the DB.
|
|
631
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
632
|
+
|
|
633
|
+
// Write to force compactions
|
|
634
|
+
Put("foo", "newvalue1");
|
|
635
|
+
for (int i = 0; i < 100; i++) {
|
|
636
|
+
ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values
|
|
637
|
+
}
|
|
638
|
+
Put("foo", "newvalue2");
|
|
639
|
+
|
|
640
|
+
iter->SeekToFirst();
|
|
641
|
+
ASSERT_TRUE(iter->Valid());
|
|
642
|
+
ASSERT_EQ("foo", iter->key().ToString());
|
|
643
|
+
ASSERT_EQ("hello", iter->value().ToString());
|
|
644
|
+
iter->Next();
|
|
645
|
+
ASSERT_TRUE(!iter->Valid());
|
|
646
|
+
delete iter;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
TEST(DBTest, Snapshot) {
|
|
650
|
+
Put("foo", "v1");
|
|
651
|
+
const Snapshot* s1 = db_->GetSnapshot();
|
|
652
|
+
Put("foo", "v2");
|
|
653
|
+
const Snapshot* s2 = db_->GetSnapshot();
|
|
654
|
+
Put("foo", "v3");
|
|
655
|
+
const Snapshot* s3 = db_->GetSnapshot();
|
|
656
|
+
|
|
657
|
+
Put("foo", "v4");
|
|
658
|
+
ASSERT_EQ("v1", Get("foo", s1));
|
|
659
|
+
ASSERT_EQ("v2", Get("foo", s2));
|
|
660
|
+
ASSERT_EQ("v3", Get("foo", s3));
|
|
661
|
+
ASSERT_EQ("v4", Get("foo"));
|
|
662
|
+
|
|
663
|
+
db_->ReleaseSnapshot(s3);
|
|
664
|
+
ASSERT_EQ("v1", Get("foo", s1));
|
|
665
|
+
ASSERT_EQ("v2", Get("foo", s2));
|
|
666
|
+
ASSERT_EQ("v4", Get("foo"));
|
|
667
|
+
|
|
668
|
+
db_->ReleaseSnapshot(s1);
|
|
669
|
+
ASSERT_EQ("v2", Get("foo", s2));
|
|
670
|
+
ASSERT_EQ("v4", Get("foo"));
|
|
671
|
+
|
|
672
|
+
db_->ReleaseSnapshot(s2);
|
|
673
|
+
ASSERT_EQ("v4", Get("foo"));
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
TEST(DBTest, HiddenValuesAreRemoved) {
|
|
677
|
+
Random rnd(301);
|
|
678
|
+
std::string big = RandomString(&rnd, 50000);
|
|
679
|
+
Put("foo", big);
|
|
680
|
+
Put("pastfoo", "v");
|
|
681
|
+
const Snapshot* snapshot = db_->GetSnapshot();
|
|
682
|
+
Put("foo", "tiny");
|
|
683
|
+
Put("pastfoo2", "v2"); // Advance sequence number one more
|
|
684
|
+
|
|
685
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
|
686
|
+
ASSERT_GT(NumTableFilesAtLevel(0), 0);
|
|
687
|
+
|
|
688
|
+
ASSERT_EQ(big, Get("foo", snapshot));
|
|
689
|
+
ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000));
|
|
690
|
+
db_->ReleaseSnapshot(snapshot);
|
|
691
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]");
|
|
692
|
+
dbfull()->TEST_CompactRange(0, "", "x");
|
|
693
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
|
|
694
|
+
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
|
695
|
+
ASSERT_GE(NumTableFilesAtLevel(1), 1);
|
|
696
|
+
dbfull()->TEST_CompactRange(1, "", "x");
|
|
697
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");
|
|
698
|
+
|
|
699
|
+
ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000));
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
TEST(DBTest, DeletionMarkers1) {
|
|
703
|
+
Put("foo", "v1");
|
|
704
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
|
705
|
+
dbfull()->TEST_CompactRange(0, "", "z");
|
|
706
|
+
dbfull()->TEST_CompactRange(1, "", "z");
|
|
707
|
+
ASSERT_EQ(NumTableFilesAtLevel(2), 1); // foo => v1 is now in level 2 file
|
|
708
|
+
Delete("foo");
|
|
709
|
+
Put("foo", "v2");
|
|
710
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");
|
|
711
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
|
712
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");
|
|
713
|
+
dbfull()->TEST_CompactRange(0, "", "z");
|
|
714
|
+
// DEL eliminated, but v1 remains because we aren't compacting that level
|
|
715
|
+
// (DEL can be eliminated because v2 hides v1).
|
|
716
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]");
|
|
717
|
+
dbfull()->TEST_CompactRange(1, "", "z");
|
|
718
|
+
// Merging L1 w/ L2, so we are the base level for "foo", so DEL is removed.
|
|
719
|
+
// (as is v1).
|
|
720
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]");
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
TEST(DBTest, DeletionMarkers2) {
|
|
724
|
+
Put("foo", "v1");
|
|
725
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
|
726
|
+
dbfull()->TEST_CompactRange(0, "", "z");
|
|
727
|
+
dbfull()->TEST_CompactRange(1, "", "z");
|
|
728
|
+
ASSERT_EQ(NumTableFilesAtLevel(2), 1); // foo => v1 is now in level 2 file
|
|
729
|
+
Delete("foo");
|
|
730
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
|
|
731
|
+
ASSERT_OK(dbfull()->TEST_CompactMemTable());
|
|
732
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
|
|
733
|
+
dbfull()->TEST_CompactRange(0, "", "z");
|
|
734
|
+
// DEL kept: L2 file overlaps
|
|
735
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
|
|
736
|
+
dbfull()->TEST_CompactRange(1, "", "z");
|
|
737
|
+
// Merging L1 w/ L2, so we are the base level for "foo", so DEL is removed.
|
|
738
|
+
// (as is v1).
|
|
739
|
+
ASSERT_EQ(AllEntriesFor("foo"), "[ ]");
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
TEST(DBTest, ComparatorCheck) {
|
|
743
|
+
class NewComparator : public Comparator {
|
|
744
|
+
public:
|
|
745
|
+
virtual const char* Name() const { return "leveldb.NewComparator"; }
|
|
746
|
+
virtual int Compare(const Slice& a, const Slice& b) const {
|
|
747
|
+
return BytewiseComparator()->Compare(a, b);
|
|
748
|
+
}
|
|
749
|
+
virtual void FindShortestSeparator(std::string* s, const Slice& l) const {
|
|
750
|
+
BytewiseComparator()->FindShortestSeparator(s, l);
|
|
751
|
+
}
|
|
752
|
+
virtual void FindShortSuccessor(std::string* key) const {
|
|
753
|
+
BytewiseComparator()->FindShortSuccessor(key);
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
NewComparator cmp;
|
|
757
|
+
Options new_options;
|
|
758
|
+
new_options.comparator = &cmp;
|
|
759
|
+
Status s = TryReopen(&new_options);
|
|
760
|
+
ASSERT_TRUE(!s.ok());
|
|
761
|
+
ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos)
|
|
762
|
+
<< s.ToString();
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
TEST(DBTest, DBOpen_Options) {
|
|
766
|
+
std::string dbname = test::TmpDir() + "/db_options_test";
|
|
767
|
+
DestroyDB(dbname, Options());
|
|
768
|
+
|
|
769
|
+
// Does not exist, and create_if_missing == false: error
|
|
770
|
+
DB* db = NULL;
|
|
771
|
+
Options opts;
|
|
772
|
+
opts.create_if_missing = false;
|
|
773
|
+
Status s = DB::Open(opts, dbname, &db);
|
|
774
|
+
ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL);
|
|
775
|
+
ASSERT_TRUE(db == NULL);
|
|
776
|
+
|
|
777
|
+
// Does not exist, and create_if_missing == true: OK
|
|
778
|
+
opts.create_if_missing = true;
|
|
779
|
+
s = DB::Open(opts, dbname, &db);
|
|
780
|
+
ASSERT_OK(s);
|
|
781
|
+
ASSERT_TRUE(db != NULL);
|
|
782
|
+
|
|
783
|
+
delete db;
|
|
784
|
+
db = NULL;
|
|
785
|
+
|
|
786
|
+
// Does exist, and error_if_exists == true: error
|
|
787
|
+
opts.create_if_missing = false;
|
|
788
|
+
opts.error_if_exists = true;
|
|
789
|
+
s = DB::Open(opts, dbname, &db);
|
|
790
|
+
ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL);
|
|
791
|
+
ASSERT_TRUE(db == NULL);
|
|
792
|
+
|
|
793
|
+
// Does exist, and error_if_exists == false: OK
|
|
794
|
+
opts.create_if_missing = true;
|
|
795
|
+
opts.error_if_exists = false;
|
|
796
|
+
s = DB::Open(opts, dbname, &db);
|
|
797
|
+
ASSERT_OK(s);
|
|
798
|
+
ASSERT_TRUE(db != NULL);
|
|
799
|
+
|
|
800
|
+
delete db;
|
|
801
|
+
db = NULL;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Multi-threaded test:
|
|
805
|
+
namespace {
|
|
806
|
+
|
|
807
|
+
static const int kNumThreads = 4;
|
|
808
|
+
static const int kTestSeconds = 10;
|
|
809
|
+
static const int kNumKeys = 1000;
|
|
810
|
+
|
|
811
|
+
struct MTState {
|
|
812
|
+
DBTest* test;
|
|
813
|
+
port::AtomicPointer stop;
|
|
814
|
+
port::AtomicPointer counter[kNumThreads];
|
|
815
|
+
port::AtomicPointer thread_done[kNumThreads];
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
struct MTThread {
|
|
819
|
+
MTState* state;
|
|
820
|
+
int id;
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
static void MTThreadBody(void* arg) {
|
|
824
|
+
MTThread* t = reinterpret_cast<MTThread*>(arg);
|
|
825
|
+
DB* db = t->state->test->db_;
|
|
826
|
+
uintptr_t counter = 0;
|
|
827
|
+
fprintf(stderr, "... starting thread %d\n", t->id);
|
|
828
|
+
Random rnd(1000 + t->id);
|
|
829
|
+
std::string value;
|
|
830
|
+
char valbuf[1500];
|
|
831
|
+
while (t->state->stop.Acquire_Load() == NULL) {
|
|
832
|
+
t->state->counter[t->id].Release_Store(reinterpret_cast<void*>(counter));
|
|
833
|
+
|
|
834
|
+
int key = rnd.Uniform(kNumKeys);
|
|
835
|
+
char keybuf[20];
|
|
836
|
+
snprintf(keybuf, sizeof(keybuf), "%016d", key);
|
|
837
|
+
|
|
838
|
+
if (rnd.OneIn(2)) {
|
|
839
|
+
// Write values of the form <key, my id, counter>.
|
|
840
|
+
// We add some padding for force compactions.
|
|
841
|
+
snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d",
|
|
842
|
+
key, t->id, static_cast<int>(counter));
|
|
843
|
+
ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf)));
|
|
844
|
+
} else {
|
|
845
|
+
// Read a value and verify that it matches the pattern written above.
|
|
846
|
+
Status s = db->Get(ReadOptions(), Slice(keybuf), &value);
|
|
847
|
+
if (s.IsNotFound()) {
|
|
848
|
+
// Key has not yet been written
|
|
849
|
+
} else {
|
|
850
|
+
// Check that the writer thread counter is >= the counter in the value
|
|
851
|
+
ASSERT_OK(s);
|
|
852
|
+
int k, w, c;
|
|
853
|
+
ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value;
|
|
854
|
+
ASSERT_EQ(k, key);
|
|
855
|
+
ASSERT_GE(w, 0);
|
|
856
|
+
ASSERT_LT(w, kNumThreads);
|
|
857
|
+
ASSERT_LE(c, reinterpret_cast<uintptr_t>(
|
|
858
|
+
t->state->counter[w].Acquire_Load()));
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
counter++;
|
|
862
|
+
}
|
|
863
|
+
t->state->thread_done[t->id].Release_Store(t);
|
|
864
|
+
fprintf(stderr, "... stopping thread %d after %d ops\n", t->id, int(counter));
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
TEST(DBTest, MultiThreaded) {
|
|
870
|
+
// Initialize state
|
|
871
|
+
MTState mt;
|
|
872
|
+
mt.test = this;
|
|
873
|
+
mt.stop.Release_Store(0);
|
|
874
|
+
for (int id = 0; id < kNumThreads; id++) {
|
|
875
|
+
mt.counter[id].Release_Store(0);
|
|
876
|
+
mt.thread_done[id].Release_Store(0);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Start threads
|
|
880
|
+
MTThread thread[kNumThreads];
|
|
881
|
+
for (int id = 0; id < kNumThreads; id++) {
|
|
882
|
+
thread[id].state = &mt;
|
|
883
|
+
thread[id].id = id;
|
|
884
|
+
env_->StartThread(MTThreadBody, &thread[id]);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Let them run for a while
|
|
888
|
+
env_->SleepForMicroseconds(kTestSeconds * 1000000);
|
|
889
|
+
|
|
890
|
+
// Stop the threads and wait for them to finish
|
|
891
|
+
mt.stop.Release_Store(&mt);
|
|
892
|
+
for (int id = 0; id < kNumThreads; id++) {
|
|
893
|
+
while (mt.thread_done[id].Acquire_Load() == NULL) {
|
|
894
|
+
env_->SleepForMicroseconds(100000);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
namespace {
|
|
900
|
+
typedef std::map<std::string, std::string> KVMap;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
class ModelDB: public DB {
|
|
904
|
+
public:
|
|
905
|
+
class ModelSnapshot : public Snapshot {
|
|
906
|
+
public:
|
|
907
|
+
KVMap map_;
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
explicit ModelDB(const Options& options): options_(options) { }
|
|
911
|
+
~ModelDB() { }
|
|
912
|
+
virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) {
|
|
913
|
+
return DB::Put(o, k, v);
|
|
914
|
+
}
|
|
915
|
+
virtual Status Delete(const WriteOptions& o, const Slice& key) {
|
|
916
|
+
return DB::Delete(o, key);
|
|
917
|
+
}
|
|
918
|
+
virtual Status Get(const ReadOptions& options,
|
|
919
|
+
const Slice& key, std::string* value) {
|
|
920
|
+
assert(false); // Not implemented
|
|
921
|
+
return Status::NotFound(key);
|
|
922
|
+
}
|
|
923
|
+
virtual Iterator* NewIterator(const ReadOptions& options) {
|
|
924
|
+
if (options.snapshot == NULL) {
|
|
925
|
+
KVMap* saved = new KVMap;
|
|
926
|
+
*saved = map_;
|
|
927
|
+
return new ModelIter(saved, true);
|
|
928
|
+
} else {
|
|
929
|
+
const KVMap* snapshot_state =
|
|
930
|
+
&(reinterpret_cast<const ModelSnapshot*>(options.snapshot)->map_);
|
|
931
|
+
return new ModelIter(snapshot_state, false);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
virtual const Snapshot* GetSnapshot() {
|
|
935
|
+
ModelSnapshot* snapshot = new ModelSnapshot;
|
|
936
|
+
snapshot->map_ = map_;
|
|
937
|
+
return snapshot;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
virtual void ReleaseSnapshot(const Snapshot* snapshot) {
|
|
941
|
+
delete reinterpret_cast<const ModelSnapshot*>(snapshot);
|
|
942
|
+
}
|
|
943
|
+
virtual Status Write(const WriteOptions& options, WriteBatch* batch) {
|
|
944
|
+
assert(options.post_write_snapshot == NULL); // Not supported
|
|
945
|
+
class Handler : public WriteBatch::Handler {
|
|
946
|
+
public:
|
|
947
|
+
KVMap* map_;
|
|
948
|
+
virtual void Put(const Slice& key, const Slice& value) {
|
|
949
|
+
(*map_)[key.ToString()] = value.ToString();
|
|
950
|
+
}
|
|
951
|
+
virtual void Delete(const Slice& key) {
|
|
952
|
+
map_->erase(key.ToString());
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
Handler handler;
|
|
956
|
+
handler.map_ = &map_;
|
|
957
|
+
return batch->Iterate(&handler);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
virtual bool GetProperty(const Slice& property, std::string* value) {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) {
|
|
964
|
+
for (int i = 0; i < n; i++) {
|
|
965
|
+
sizes[i] = 0;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
private:
|
|
969
|
+
class ModelIter: public Iterator {
|
|
970
|
+
public:
|
|
971
|
+
ModelIter(const KVMap* map, bool owned)
|
|
972
|
+
: map_(map), owned_(owned), iter_(map_->end()) {
|
|
973
|
+
}
|
|
974
|
+
~ModelIter() {
|
|
975
|
+
if (owned_) delete map_;
|
|
976
|
+
}
|
|
977
|
+
virtual bool Valid() const { return iter_ != map_->end(); }
|
|
978
|
+
virtual void SeekToFirst() { iter_ = map_->begin(); }
|
|
979
|
+
virtual void SeekToLast() {
|
|
980
|
+
if (map_->empty()) {
|
|
981
|
+
iter_ = map_->end();
|
|
982
|
+
} else {
|
|
983
|
+
iter_ = map_->find(map_->rbegin()->first);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
virtual void Seek(const Slice& k) {
|
|
987
|
+
iter_ = map_->lower_bound(k.ToString());
|
|
988
|
+
}
|
|
989
|
+
virtual void Next() { ++iter_; }
|
|
990
|
+
virtual void Prev() { --iter_; }
|
|
991
|
+
virtual Slice key() const { return iter_->first; }
|
|
992
|
+
virtual Slice value() const { return iter_->second; }
|
|
993
|
+
virtual Status status() const { return Status::OK(); }
|
|
994
|
+
private:
|
|
995
|
+
const KVMap* const map_;
|
|
996
|
+
const bool owned_; // Do we own map_
|
|
997
|
+
KVMap::const_iterator iter_;
|
|
998
|
+
};
|
|
999
|
+
const Options options_;
|
|
1000
|
+
KVMap map_;
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
static std::string RandomKey(Random* rnd) {
|
|
1004
|
+
int len = (rnd->OneIn(3)
|
|
1005
|
+
? 1 // Short sometimes to encourage collisions
|
|
1006
|
+
: (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10)));
|
|
1007
|
+
return test::RandomKey(rnd, len);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
static bool CompareIterators(int step,
|
|
1011
|
+
DB* model,
|
|
1012
|
+
DB* db,
|
|
1013
|
+
const Snapshot* model_snap,
|
|
1014
|
+
const Snapshot* db_snap) {
|
|
1015
|
+
ReadOptions options;
|
|
1016
|
+
options.snapshot = model_snap;
|
|
1017
|
+
Iterator* miter = model->NewIterator(options);
|
|
1018
|
+
options.snapshot = db_snap;
|
|
1019
|
+
Iterator* dbiter = db->NewIterator(options);
|
|
1020
|
+
bool ok = true;
|
|
1021
|
+
int count = 0;
|
|
1022
|
+
for (miter->SeekToFirst(), dbiter->SeekToFirst();
|
|
1023
|
+
ok && miter->Valid() && dbiter->Valid();
|
|
1024
|
+
miter->Next(), dbiter->Next()) {
|
|
1025
|
+
count++;
|
|
1026
|
+
if (miter->key().compare(dbiter->key()) != 0) {
|
|
1027
|
+
fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n",
|
|
1028
|
+
step,
|
|
1029
|
+
EscapeString(miter->key()).c_str(),
|
|
1030
|
+
EscapeString(dbiter->key()).c_str());
|
|
1031
|
+
ok = false;
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (miter->value().compare(dbiter->value()) != 0) {
|
|
1036
|
+
fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n",
|
|
1037
|
+
step,
|
|
1038
|
+
EscapeString(miter->key()).c_str(),
|
|
1039
|
+
EscapeString(miter->value()).c_str(),
|
|
1040
|
+
EscapeString(miter->value()).c_str());
|
|
1041
|
+
ok = false;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (ok) {
|
|
1046
|
+
if (miter->Valid() != dbiter->Valid()) {
|
|
1047
|
+
fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n",
|
|
1048
|
+
step, miter->Valid(), dbiter->Valid());
|
|
1049
|
+
ok = false;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
fprintf(stderr, "%d entries compared: ok=%d\n", count, ok);
|
|
1053
|
+
delete miter;
|
|
1054
|
+
delete dbiter;
|
|
1055
|
+
return ok;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
TEST(DBTest, Randomized) {
|
|
1059
|
+
Random rnd(test::RandomSeed());
|
|
1060
|
+
ModelDB model(last_options_);
|
|
1061
|
+
const int N = 10000;
|
|
1062
|
+
const Snapshot* model_snap = NULL;
|
|
1063
|
+
const Snapshot* db_snap = NULL;
|
|
1064
|
+
std::string k, v;
|
|
1065
|
+
for (int step = 0; step < N; step++) {
|
|
1066
|
+
if (step % 100 == 0) {
|
|
1067
|
+
fprintf(stderr, "Step %d of %d\n", step, N);
|
|
1068
|
+
}
|
|
1069
|
+
int p = rnd.Uniform(100);
|
|
1070
|
+
if (p < 45) { // Put
|
|
1071
|
+
k = RandomKey(&rnd);
|
|
1072
|
+
v = RandomString(&rnd,
|
|
1073
|
+
rnd.OneIn(20)
|
|
1074
|
+
? 100 + rnd.Uniform(100)
|
|
1075
|
+
: rnd.Uniform(8));
|
|
1076
|
+
ASSERT_OK(model.Put(WriteOptions(), k, v));
|
|
1077
|
+
ASSERT_OK(db_->Put(WriteOptions(), k, v));
|
|
1078
|
+
|
|
1079
|
+
} else if (p < 90) { // Delete
|
|
1080
|
+
k = RandomKey(&rnd);
|
|
1081
|
+
ASSERT_OK(model.Delete(WriteOptions(), k));
|
|
1082
|
+
ASSERT_OK(db_->Delete(WriteOptions(), k));
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
} else { // Multi-element batch
|
|
1086
|
+
WriteBatch b;
|
|
1087
|
+
const int num = rnd.Uniform(8);
|
|
1088
|
+
for (int i = 0; i < num; i++) {
|
|
1089
|
+
if (i == 0 || !rnd.OneIn(10)) {
|
|
1090
|
+
k = RandomKey(&rnd);
|
|
1091
|
+
} else {
|
|
1092
|
+
// Periodically re-use the same key from the previous iter, so
|
|
1093
|
+
// we have multiple entries in the write batch for the same key
|
|
1094
|
+
}
|
|
1095
|
+
if (rnd.OneIn(2)) {
|
|
1096
|
+
v = RandomString(&rnd, rnd.Uniform(10));
|
|
1097
|
+
b.Put(k, v);
|
|
1098
|
+
} else {
|
|
1099
|
+
b.Delete(k);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
ASSERT_OK(model.Write(WriteOptions(), &b));
|
|
1103
|
+
ASSERT_OK(db_->Write(WriteOptions(), &b));
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if ((step % 100) == 0) {
|
|
1107
|
+
ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
|
|
1108
|
+
ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap));
|
|
1109
|
+
// Save a snapshot from each DB this time that we'll use next
|
|
1110
|
+
// time we compare things, to make sure the current state is
|
|
1111
|
+
// preserved with the snapshot
|
|
1112
|
+
if (model_snap != NULL) model.ReleaseSnapshot(model_snap);
|
|
1113
|
+
if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);
|
|
1114
|
+
|
|
1115
|
+
Reopen();
|
|
1116
|
+
ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));
|
|
1117
|
+
|
|
1118
|
+
model_snap = model.GetSnapshot();
|
|
1119
|
+
db_snap = db_->GetSnapshot();
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
if (model_snap != NULL) model.ReleaseSnapshot(model_snap);
|
|
1123
|
+
if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
std::string MakeKey(unsigned int num) {
|
|
1127
|
+
char buf[30];
|
|
1128
|
+
snprintf(buf, sizeof(buf), "%016u", num);
|
|
1129
|
+
return std::string(buf);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
void BM_LogAndApply(int iters, int num_base_files) {
|
|
1133
|
+
std::string dbname = test::TmpDir() + "/leveldb_test_benchmark";
|
|
1134
|
+
DestroyDB(dbname, Options());
|
|
1135
|
+
|
|
1136
|
+
DB* db = NULL;
|
|
1137
|
+
Options opts;
|
|
1138
|
+
opts.create_if_missing = true;
|
|
1139
|
+
Status s = DB::Open(opts, dbname, &db);
|
|
1140
|
+
ASSERT_OK(s);
|
|
1141
|
+
ASSERT_TRUE(db != NULL);
|
|
1142
|
+
|
|
1143
|
+
delete db;
|
|
1144
|
+
db = NULL;
|
|
1145
|
+
|
|
1146
|
+
Env* env = Env::Default();
|
|
1147
|
+
|
|
1148
|
+
InternalKeyComparator cmp(BytewiseComparator());
|
|
1149
|
+
Options options;
|
|
1150
|
+
VersionSet vset(dbname, &options, NULL, &cmp);
|
|
1151
|
+
ASSERT_OK(vset.Recover());
|
|
1152
|
+
VersionEdit vbase;
|
|
1153
|
+
uint64_t fnum = 1;
|
|
1154
|
+
for (int i = 0; i < num_base_files; i++) {
|
|
1155
|
+
InternalKey start(MakeKey(2*fnum), 1, kTypeValue);
|
|
1156
|
+
InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion);
|
|
1157
|
+
vbase.AddFile(2, fnum++, 1 /* file size */, start, limit);
|
|
1158
|
+
}
|
|
1159
|
+
ASSERT_OK(vset.LogAndApply(&vbase));
|
|
1160
|
+
|
|
1161
|
+
uint64_t start_micros = env->NowMicros();
|
|
1162
|
+
|
|
1163
|
+
for (int i = 0; i < iters; i++) {
|
|
1164
|
+
VersionEdit vedit;
|
|
1165
|
+
vedit.DeleteFile(2, fnum);
|
|
1166
|
+
InternalKey start(MakeKey(2*fnum), 1, kTypeValue);
|
|
1167
|
+
InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion);
|
|
1168
|
+
vedit.AddFile(2, fnum++, 1 /* file size */, start, limit);
|
|
1169
|
+
vset.LogAndApply(&vedit);
|
|
1170
|
+
}
|
|
1171
|
+
uint64_t stop_micros = env->NowMicros();
|
|
1172
|
+
unsigned int us = stop_micros - start_micros;
|
|
1173
|
+
char buf[16];
|
|
1174
|
+
snprintf(buf, sizeof(buf), "%d", num_base_files);
|
|
1175
|
+
fprintf(stderr,
|
|
1176
|
+
"BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n",
|
|
1177
|
+
buf, iters, us, ((float)us) / iters);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
int main(int argc, char** argv) {
|
|
1183
|
+
if (argc > 1 && std::string(argv[1]) == "--benchmark") {
|
|
1184
|
+
leveldb::BM_LogAndApply(1000, 1);
|
|
1185
|
+
leveldb::BM_LogAndApply(1000, 100);
|
|
1186
|
+
leveldb::BM_LogAndApply(1000, 10000);
|
|
1187
|
+
leveldb::BM_LogAndApply(100, 100000);
|
|
1188
|
+
return 0;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return leveldb::test::RunAllTests();
|
|
1192
|
+
}
|