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,36 @@
|
|
|
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_BUILDER_H_
|
|
6
|
+
#define STORAGE_LEVELDB_DB_BUILDER_H_
|
|
7
|
+
|
|
8
|
+
#include "leveldb/status.h"
|
|
9
|
+
|
|
10
|
+
namespace leveldb {
|
|
11
|
+
|
|
12
|
+
struct Options;
|
|
13
|
+
struct FileMetaData;
|
|
14
|
+
|
|
15
|
+
class Env;
|
|
16
|
+
class Iterator;
|
|
17
|
+
class TableCache;
|
|
18
|
+
class VersionEdit;
|
|
19
|
+
|
|
20
|
+
// Build a Table file from the contents of *iter. The generated file
|
|
21
|
+
// will be named according to meta->number. On success, the rest of
|
|
22
|
+
// *meta will be filled with metadata about the generated table, and
|
|
23
|
+
// the file information will be added to *edit. If no data is present
|
|
24
|
+
// in *iter, meta->file_size will be set to zero, and no Table file
|
|
25
|
+
// will be produced.
|
|
26
|
+
extern Status BuildTable(const std::string& dbname,
|
|
27
|
+
Env* env,
|
|
28
|
+
const Options& options,
|
|
29
|
+
TableCache* table_cache,
|
|
30
|
+
Iterator* iter,
|
|
31
|
+
FileMetaData* meta,
|
|
32
|
+
VersionEdit* edit);
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#endif // STORAGE_LEVELDB_DB_BUILDER_H_
|
|
@@ -0,0 +1,354 @@
|
|
|
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
|
+
|
|
7
|
+
#include <errno.h>
|
|
8
|
+
#include <fcntl.h>
|
|
9
|
+
#include <sys/stat.h>
|
|
10
|
+
#include <sys/types.h>
|
|
11
|
+
#include "leveldb/cache.h"
|
|
12
|
+
#include "leveldb/env.h"
|
|
13
|
+
#include "leveldb/table.h"
|
|
14
|
+
#include "leveldb/write_batch.h"
|
|
15
|
+
#include "db/db_impl.h"
|
|
16
|
+
#include "db/filename.h"
|
|
17
|
+
#include "db/log_format.h"
|
|
18
|
+
#include "db/version_set.h"
|
|
19
|
+
#include "util/logging.h"
|
|
20
|
+
#include "util/testharness.h"
|
|
21
|
+
#include "util/testutil.h"
|
|
22
|
+
|
|
23
|
+
namespace leveldb {
|
|
24
|
+
|
|
25
|
+
static const int kValueSize = 1000;
|
|
26
|
+
|
|
27
|
+
class CorruptionTest {
|
|
28
|
+
public:
|
|
29
|
+
test::ErrorEnv env_;
|
|
30
|
+
Random rnd_;
|
|
31
|
+
std::string dbname_;
|
|
32
|
+
Cache* tiny_cache_;
|
|
33
|
+
Options options_;
|
|
34
|
+
DB* db_;
|
|
35
|
+
|
|
36
|
+
CorruptionTest() : rnd_(test::RandomSeed()) {
|
|
37
|
+
tiny_cache_ = NewLRUCache(100);
|
|
38
|
+
options_.env = &env_;
|
|
39
|
+
dbname_ = test::TmpDir() + "/db_test";
|
|
40
|
+
DestroyDB(dbname_, options_);
|
|
41
|
+
|
|
42
|
+
db_ = NULL;
|
|
43
|
+
options_.create_if_missing = true;
|
|
44
|
+
Reopen();
|
|
45
|
+
options_.create_if_missing = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
~CorruptionTest() {
|
|
49
|
+
delete db_;
|
|
50
|
+
DestroyDB(dbname_, Options());
|
|
51
|
+
delete tiny_cache_;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Status TryReopen(Options* options = NULL) {
|
|
55
|
+
delete db_;
|
|
56
|
+
db_ = NULL;
|
|
57
|
+
Options opt = (options ? *options : options_);
|
|
58
|
+
opt.env = &env_;
|
|
59
|
+
opt.block_cache = tiny_cache_;
|
|
60
|
+
return DB::Open(opt, dbname_, &db_);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void Reopen(Options* options = NULL) {
|
|
64
|
+
ASSERT_OK(TryReopen(options));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void RepairDB() {
|
|
68
|
+
delete db_;
|
|
69
|
+
db_ = NULL;
|
|
70
|
+
ASSERT_OK(::leveldb::RepairDB(dbname_, options_));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void Build(int n) {
|
|
74
|
+
std::string key_space, value_space;
|
|
75
|
+
WriteBatch batch;
|
|
76
|
+
for (int i = 0; i < n; i++) {
|
|
77
|
+
//if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
|
|
78
|
+
Slice key = Key(i, &key_space);
|
|
79
|
+
batch.Clear();
|
|
80
|
+
batch.Put(key, Value(i, &value_space));
|
|
81
|
+
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void Check(int min_expected, int max_expected) {
|
|
86
|
+
int next_expected = 0;
|
|
87
|
+
int missed = 0;
|
|
88
|
+
int bad_keys = 0;
|
|
89
|
+
int bad_values = 0;
|
|
90
|
+
int correct = 0;
|
|
91
|
+
std::string value_space;
|
|
92
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
93
|
+
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
|
94
|
+
uint64_t key;
|
|
95
|
+
Slice in(iter->key());
|
|
96
|
+
if (!ConsumeDecimalNumber(&in, &key) ||
|
|
97
|
+
!in.empty() ||
|
|
98
|
+
key < next_expected) {
|
|
99
|
+
bad_keys++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
missed += (key - next_expected);
|
|
103
|
+
next_expected = key + 1;
|
|
104
|
+
if (iter->value() != Value(key, &value_space)) {
|
|
105
|
+
bad_values++;
|
|
106
|
+
} else {
|
|
107
|
+
correct++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
delete iter;
|
|
111
|
+
|
|
112
|
+
fprintf(stderr,
|
|
113
|
+
"expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
|
|
114
|
+
min_expected, max_expected, correct, bad_keys, bad_values, missed);
|
|
115
|
+
ASSERT_LE(min_expected, correct);
|
|
116
|
+
ASSERT_GE(max_expected, correct);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
|
120
|
+
// Pick file to corrupt
|
|
121
|
+
std::vector<std::string> filenames;
|
|
122
|
+
ASSERT_OK(env_.GetChildren(dbname_, &filenames));
|
|
123
|
+
uint64_t number;
|
|
124
|
+
FileType type;
|
|
125
|
+
std::vector<std::string> candidates;
|
|
126
|
+
for (int i = 0; i < filenames.size(); i++) {
|
|
127
|
+
if (ParseFileName(filenames[i], &number, &type) &&
|
|
128
|
+
type == filetype) {
|
|
129
|
+
candidates.push_back(dbname_ + "/" + filenames[i]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
ASSERT_TRUE(!candidates.empty()) << filetype;
|
|
133
|
+
std::string fname = candidates[rnd_.Uniform(candidates.size())];
|
|
134
|
+
|
|
135
|
+
struct stat sbuf;
|
|
136
|
+
if (stat(fname.c_str(), &sbuf) != 0) {
|
|
137
|
+
const char* msg = strerror(errno);
|
|
138
|
+
ASSERT_TRUE(false) << fname << ": " << msg;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (offset < 0) {
|
|
142
|
+
// Relative to end of file; make it absolute
|
|
143
|
+
if (-offset > sbuf.st_size) {
|
|
144
|
+
offset = 0;
|
|
145
|
+
} else {
|
|
146
|
+
offset = sbuf.st_size + offset;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (offset > sbuf.st_size) {
|
|
150
|
+
offset = sbuf.st_size;
|
|
151
|
+
}
|
|
152
|
+
if (offset + bytes_to_corrupt > sbuf.st_size) {
|
|
153
|
+
bytes_to_corrupt = sbuf.st_size - offset;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Do it
|
|
157
|
+
std::string contents;
|
|
158
|
+
Status s = ReadFileToString(Env::Default(), fname, &contents);
|
|
159
|
+
ASSERT_TRUE(s.ok()) << s.ToString();
|
|
160
|
+
for (int i = 0; i < bytes_to_corrupt; i++) {
|
|
161
|
+
contents[i + offset] ^= 0x80;
|
|
162
|
+
}
|
|
163
|
+
s = WriteStringToFile(Env::Default(), contents, fname);
|
|
164
|
+
ASSERT_TRUE(s.ok()) << s.ToString();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
int Property(const std::string& name) {
|
|
168
|
+
std::string property;
|
|
169
|
+
int result;
|
|
170
|
+
if (db_->GetProperty(name, &property) &&
|
|
171
|
+
sscanf(property.c_str(), "%d", &result) == 1) {
|
|
172
|
+
return result;
|
|
173
|
+
} else {
|
|
174
|
+
return -1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Return the ith key
|
|
179
|
+
Slice Key(int i, std::string* storage) {
|
|
180
|
+
char buf[100];
|
|
181
|
+
snprintf(buf, sizeof(buf), "%016d", i);
|
|
182
|
+
storage->assign(buf, strlen(buf));
|
|
183
|
+
return Slice(*storage);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Return the value to associate with the specified key
|
|
187
|
+
Slice Value(int k, std::string* storage) {
|
|
188
|
+
Random r(k);
|
|
189
|
+
return test::RandomString(&r, kValueSize, storage);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
TEST(CorruptionTest, Recovery) {
|
|
194
|
+
Build(100);
|
|
195
|
+
Check(100, 100);
|
|
196
|
+
Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
|
|
197
|
+
Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block
|
|
198
|
+
Reopen();
|
|
199
|
+
|
|
200
|
+
// The 64 records in the first two log blocks are completely lost.
|
|
201
|
+
Check(36, 36);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
TEST(CorruptionTest, RecoverWriteError) {
|
|
205
|
+
env_.writable_file_error_ = true;
|
|
206
|
+
Status s = TryReopen();
|
|
207
|
+
ASSERT_TRUE(!s.ok());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
TEST(CorruptionTest, NewFileErrorDuringWrite) {
|
|
211
|
+
// Do enough writing to force minor compaction
|
|
212
|
+
env_.writable_file_error_ = true;
|
|
213
|
+
const int num = 3 + (Options().write_buffer_size / kValueSize);
|
|
214
|
+
std::string value_storage;
|
|
215
|
+
Status s;
|
|
216
|
+
for (int i = 0; s.ok() && i < num; i++) {
|
|
217
|
+
WriteBatch batch;
|
|
218
|
+
batch.Put("a", Value(100, &value_storage));
|
|
219
|
+
s = db_->Write(WriteOptions(), &batch);
|
|
220
|
+
}
|
|
221
|
+
ASSERT_TRUE(!s.ok());
|
|
222
|
+
ASSERT_GE(env_.num_writable_file_errors_, 1);
|
|
223
|
+
env_.writable_file_error_ = false;
|
|
224
|
+
Reopen();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
TEST(CorruptionTest, TableFile) {
|
|
228
|
+
Build(100);
|
|
229
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
230
|
+
dbi->TEST_CompactMemTable();
|
|
231
|
+
dbi->TEST_CompactRange(0, "", "~");
|
|
232
|
+
dbi->TEST_CompactRange(1, "", "~");
|
|
233
|
+
|
|
234
|
+
Corrupt(kTableFile, 100, 1);
|
|
235
|
+
Check(99, 99);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
TEST(CorruptionTest, TableFileIndexData) {
|
|
239
|
+
Build(10000); // Enough to build multiple Tables
|
|
240
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
241
|
+
dbi->TEST_CompactMemTable();
|
|
242
|
+
dbi->TEST_CompactRange(0, "", "~");
|
|
243
|
+
dbi->TEST_CompactRange(1, "", "~");
|
|
244
|
+
|
|
245
|
+
Corrupt(kTableFile, -2000, 500);
|
|
246
|
+
Reopen();
|
|
247
|
+
Check(5000, 9999);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
TEST(CorruptionTest, MissingDescriptor) {
|
|
251
|
+
Build(1000);
|
|
252
|
+
RepairDB();
|
|
253
|
+
Reopen();
|
|
254
|
+
Check(1000, 1000);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
TEST(CorruptionTest, SequenceNumberRecovery) {
|
|
258
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
|
|
259
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
|
|
260
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
|
|
261
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
|
|
262
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
|
|
263
|
+
RepairDB();
|
|
264
|
+
Reopen();
|
|
265
|
+
std::string v;
|
|
266
|
+
ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
|
|
267
|
+
ASSERT_EQ("v5", v);
|
|
268
|
+
// Write something. If sequence number was not recovered properly,
|
|
269
|
+
// it will be hidden by an earlier write.
|
|
270
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
|
|
271
|
+
ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
|
|
272
|
+
ASSERT_EQ("v6", v);
|
|
273
|
+
Reopen();
|
|
274
|
+
ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
|
|
275
|
+
ASSERT_EQ("v6", v);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
TEST(CorruptionTest, CorruptedDescriptor) {
|
|
279
|
+
ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
|
|
280
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
281
|
+
dbi->TEST_CompactMemTable();
|
|
282
|
+
dbi->TEST_CompactRange(0, "", "~");
|
|
283
|
+
|
|
284
|
+
Corrupt(kDescriptorFile, 0, 1000);
|
|
285
|
+
Status s = TryReopen();
|
|
286
|
+
ASSERT_TRUE(!s.ok());
|
|
287
|
+
|
|
288
|
+
RepairDB();
|
|
289
|
+
Reopen();
|
|
290
|
+
std::string v;
|
|
291
|
+
ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
|
|
292
|
+
ASSERT_EQ("hello", v);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
TEST(CorruptionTest, CompactionInputError) {
|
|
296
|
+
Build(10);
|
|
297
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
298
|
+
dbi->TEST_CompactMemTable();
|
|
299
|
+
ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
|
|
300
|
+
|
|
301
|
+
Corrupt(kTableFile, 100, 1);
|
|
302
|
+
Check(9, 9);
|
|
303
|
+
|
|
304
|
+
// Force compactions by writing lots of values
|
|
305
|
+
Build(10000);
|
|
306
|
+
Check(10000, 10000);
|
|
307
|
+
dbi->TEST_CompactRange(0, "", "~");
|
|
308
|
+
ASSERT_EQ(0, Property("leveldb.num-files-at-level0"));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
TEST(CorruptionTest, CompactionInputErrorParanoid) {
|
|
312
|
+
Options options;
|
|
313
|
+
options.paranoid_checks = true;
|
|
314
|
+
options.write_buffer_size = 1048576;
|
|
315
|
+
Reopen(&options);
|
|
316
|
+
|
|
317
|
+
Build(10);
|
|
318
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
319
|
+
dbi->TEST_CompactMemTable();
|
|
320
|
+
ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
|
|
321
|
+
|
|
322
|
+
Corrupt(kTableFile, 100, 1);
|
|
323
|
+
Check(9, 9);
|
|
324
|
+
|
|
325
|
+
// Write must eventually fail because of corrupted table
|
|
326
|
+
Status s;
|
|
327
|
+
std::string tmp1, tmp2;
|
|
328
|
+
for (int i = 0; i < 10000 && s.ok(); i++) {
|
|
329
|
+
s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
|
|
330
|
+
}
|
|
331
|
+
ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
TEST(CorruptionTest, UnrelatedKeys) {
|
|
335
|
+
Build(10);
|
|
336
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
337
|
+
dbi->TEST_CompactMemTable();
|
|
338
|
+
Corrupt(kTableFile, 100, 1);
|
|
339
|
+
|
|
340
|
+
std::string tmp1, tmp2;
|
|
341
|
+
ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
|
|
342
|
+
std::string v;
|
|
343
|
+
ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
|
|
344
|
+
ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
|
|
345
|
+
dbi->TEST_CompactMemTable();
|
|
346
|
+
ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
|
|
347
|
+
ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
int main(int argc, char** argv) {
|
|
353
|
+
return leveldb::test::RunAllTests();
|
|
354
|
+
}
|
|
@@ -0,0 +1,677 @@
|
|
|
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 <sys/types.h>
|
|
6
|
+
#include <stdio.h>
|
|
7
|
+
#include <stdlib.h>
|
|
8
|
+
#include "db/db_impl.h"
|
|
9
|
+
#include "db/version_set.h"
|
|
10
|
+
#include "leveldb/cache.h"
|
|
11
|
+
#include "leveldb/db.h"
|
|
12
|
+
#include "leveldb/env.h"
|
|
13
|
+
#include "leveldb/write_batch.h"
|
|
14
|
+
#include "port/port.h"
|
|
15
|
+
#include "util/crc32c.h"
|
|
16
|
+
#include "util/histogram.h"
|
|
17
|
+
#include "util/random.h"
|
|
18
|
+
#include "util/testutil.h"
|
|
19
|
+
|
|
20
|
+
// Comma-separated list of operations to run in the specified order
|
|
21
|
+
// Actual benchmarks:
|
|
22
|
+
// fillseq -- write N values in sequential key order in async mode
|
|
23
|
+
// fillrandom -- write N values in random key order in async mode
|
|
24
|
+
// overwrite -- overwrite N values in random key order in async mode
|
|
25
|
+
// fillsync -- write N/100 values in random key order in sync mode
|
|
26
|
+
// fill100K -- write N/1000 100K values in random order in async mode
|
|
27
|
+
// readseq -- read N times sequentially
|
|
28
|
+
// readreverse -- read N times in reverse order
|
|
29
|
+
// readrandom -- read N times in random order
|
|
30
|
+
// readhot -- read N times in random order from 1% section of DB
|
|
31
|
+
// crc32c -- repeated crc32c of 4K of data
|
|
32
|
+
// acquireload -- load N*1000 times
|
|
33
|
+
// Meta operations:
|
|
34
|
+
// compact -- Compact the entire DB
|
|
35
|
+
// stats -- Print DB stats
|
|
36
|
+
// heapprofile -- Dump a heap profile (if supported by this port)
|
|
37
|
+
static const char* FLAGS_benchmarks =
|
|
38
|
+
"fillseq,"
|
|
39
|
+
"fillsync,"
|
|
40
|
+
"fillrandom,"
|
|
41
|
+
"overwrite,"
|
|
42
|
+
"readrandom,"
|
|
43
|
+
"readrandom," // Extra run to allow previous compactions to quiesce
|
|
44
|
+
"readseq,"
|
|
45
|
+
"readreverse,"
|
|
46
|
+
"compact,"
|
|
47
|
+
"readrandom,"
|
|
48
|
+
"readseq,"
|
|
49
|
+
"readreverse,"
|
|
50
|
+
"fill100K,"
|
|
51
|
+
"crc32c,"
|
|
52
|
+
"snappycomp,"
|
|
53
|
+
"snappyuncomp,"
|
|
54
|
+
"acquireload,"
|
|
55
|
+
;
|
|
56
|
+
|
|
57
|
+
// Number of key/values to place in database
|
|
58
|
+
static int FLAGS_num = 1000000;
|
|
59
|
+
|
|
60
|
+
// Number of read operations to do. If negative, do FLAGS_num reads.
|
|
61
|
+
static int FLAGS_reads = -1;
|
|
62
|
+
|
|
63
|
+
// Size of each value
|
|
64
|
+
static int FLAGS_value_size = 100;
|
|
65
|
+
|
|
66
|
+
// Arrange to generate values that shrink to this fraction of
|
|
67
|
+
// their original size after compression
|
|
68
|
+
static double FLAGS_compression_ratio = 0.5;
|
|
69
|
+
|
|
70
|
+
// Print histogram of operation timings
|
|
71
|
+
static bool FLAGS_histogram = false;
|
|
72
|
+
|
|
73
|
+
// Number of bytes to buffer in memtable before compacting
|
|
74
|
+
// (initialized to default value by "main")
|
|
75
|
+
static int FLAGS_write_buffer_size = 0;
|
|
76
|
+
|
|
77
|
+
// Number of bytes to use as a cache of uncompressed data.
|
|
78
|
+
// Negative means use default settings.
|
|
79
|
+
static int FLAGS_cache_size = -1;
|
|
80
|
+
|
|
81
|
+
// Maximum number of files to keep open at the same time (use default if == 0)
|
|
82
|
+
static int FLAGS_open_files = 0;
|
|
83
|
+
|
|
84
|
+
// If true, do not destroy the existing database. If you set this
|
|
85
|
+
// flag and also specify a benchmark that wants a fresh database, that
|
|
86
|
+
// benchmark will fail.
|
|
87
|
+
static bool FLAGS_use_existing_db = false;
|
|
88
|
+
|
|
89
|
+
namespace leveldb {
|
|
90
|
+
|
|
91
|
+
// Helper for quickly generating random data.
|
|
92
|
+
namespace {
|
|
93
|
+
class RandomGenerator {
|
|
94
|
+
private:
|
|
95
|
+
std::string data_;
|
|
96
|
+
int pos_;
|
|
97
|
+
|
|
98
|
+
public:
|
|
99
|
+
RandomGenerator() {
|
|
100
|
+
// We use a limited amount of data over and over again and ensure
|
|
101
|
+
// that it is larger than the compression window (32KB), and also
|
|
102
|
+
// large enough to serve all typical value sizes we want to write.
|
|
103
|
+
Random rnd(301);
|
|
104
|
+
std::string piece;
|
|
105
|
+
while (data_.size() < 1048576) {
|
|
106
|
+
// Add a short fragment that is as compressible as specified
|
|
107
|
+
// by FLAGS_compression_ratio.
|
|
108
|
+
test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
|
|
109
|
+
data_.append(piece);
|
|
110
|
+
}
|
|
111
|
+
pos_ = 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Slice Generate(int len) {
|
|
115
|
+
if (pos_ + len > data_.size()) {
|
|
116
|
+
pos_ = 0;
|
|
117
|
+
assert(len < data_.size());
|
|
118
|
+
}
|
|
119
|
+
pos_ += len;
|
|
120
|
+
return Slice(data_.data() + pos_ - len, len);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
static Slice TrimSpace(Slice s) {
|
|
125
|
+
int start = 0;
|
|
126
|
+
while (start < s.size() && isspace(s[start])) {
|
|
127
|
+
start++;
|
|
128
|
+
}
|
|
129
|
+
int limit = s.size();
|
|
130
|
+
while (limit > start && isspace(s[limit-1])) {
|
|
131
|
+
limit--;
|
|
132
|
+
}
|
|
133
|
+
return Slice(s.data() + start, limit - start);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class Benchmark {
|
|
139
|
+
private:
|
|
140
|
+
Cache* cache_;
|
|
141
|
+
DB* db_;
|
|
142
|
+
int num_;
|
|
143
|
+
int reads_;
|
|
144
|
+
int heap_counter_;
|
|
145
|
+
double start_;
|
|
146
|
+
double last_op_finish_;
|
|
147
|
+
int64_t bytes_;
|
|
148
|
+
std::string message_;
|
|
149
|
+
std::string post_message_;
|
|
150
|
+
Histogram hist_;
|
|
151
|
+
RandomGenerator gen_;
|
|
152
|
+
Random rand_;
|
|
153
|
+
|
|
154
|
+
// State kept for progress messages
|
|
155
|
+
int done_;
|
|
156
|
+
int next_report_; // When to report next
|
|
157
|
+
|
|
158
|
+
void PrintHeader() {
|
|
159
|
+
const int kKeySize = 16;
|
|
160
|
+
PrintEnvironment();
|
|
161
|
+
fprintf(stdout, "Keys: %d bytes each\n", kKeySize);
|
|
162
|
+
fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n",
|
|
163
|
+
FLAGS_value_size,
|
|
164
|
+
static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
|
|
165
|
+
fprintf(stdout, "Entries: %d\n", num_);
|
|
166
|
+
fprintf(stdout, "RawSize: %.1f MB (estimated)\n",
|
|
167
|
+
((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
|
|
168
|
+
/ 1048576.0));
|
|
169
|
+
fprintf(stdout, "FileSize: %.1f MB (estimated)\n",
|
|
170
|
+
(((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)
|
|
171
|
+
/ 1048576.0));
|
|
172
|
+
PrintWarnings();
|
|
173
|
+
fprintf(stdout, "------------------------------------------------\n");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
void PrintWarnings() {
|
|
177
|
+
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
|
178
|
+
fprintf(stdout,
|
|
179
|
+
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
|
|
180
|
+
);
|
|
181
|
+
#endif
|
|
182
|
+
#ifndef NDEBUG
|
|
183
|
+
fprintf(stdout,
|
|
184
|
+
"WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
|
|
185
|
+
#endif
|
|
186
|
+
|
|
187
|
+
// See if snappy is working by attempting to compress a compressible string
|
|
188
|
+
const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
|
|
189
|
+
std::string compressed;
|
|
190
|
+
if (!port::Snappy_Compress(text, sizeof(text), &compressed)) {
|
|
191
|
+
fprintf(stdout, "WARNING: Snappy compression is not enabled\n");
|
|
192
|
+
} else if (compressed.size() >= sizeof(text)) {
|
|
193
|
+
fprintf(stdout, "WARNING: Snappy compression is not effective\n");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void PrintEnvironment() {
|
|
198
|
+
fprintf(stderr, "LevelDB: version %d.%d\n",
|
|
199
|
+
kMajorVersion, kMinorVersion);
|
|
200
|
+
|
|
201
|
+
#if defined(__linux)
|
|
202
|
+
time_t now = time(NULL);
|
|
203
|
+
fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline
|
|
204
|
+
|
|
205
|
+
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
|
|
206
|
+
if (cpuinfo != NULL) {
|
|
207
|
+
char line[1000];
|
|
208
|
+
int num_cpus = 0;
|
|
209
|
+
std::string cpu_type;
|
|
210
|
+
std::string cache_size;
|
|
211
|
+
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
|
|
212
|
+
const char* sep = strchr(line, ':');
|
|
213
|
+
if (sep == NULL) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
Slice key = TrimSpace(Slice(line, sep - 1 - line));
|
|
217
|
+
Slice val = TrimSpace(Slice(sep + 1));
|
|
218
|
+
if (key == "model name") {
|
|
219
|
+
++num_cpus;
|
|
220
|
+
cpu_type = val.ToString();
|
|
221
|
+
} else if (key == "cache size") {
|
|
222
|
+
cache_size = val.ToString();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
fclose(cpuinfo);
|
|
226
|
+
fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str());
|
|
227
|
+
fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());
|
|
228
|
+
}
|
|
229
|
+
#endif
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
void Start() {
|
|
233
|
+
start_ = Env::Default()->NowMicros() * 1e-6;
|
|
234
|
+
bytes_ = 0;
|
|
235
|
+
message_.clear();
|
|
236
|
+
last_op_finish_ = start_;
|
|
237
|
+
hist_.Clear();
|
|
238
|
+
done_ = 0;
|
|
239
|
+
next_report_ = 100;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
void FinishedSingleOp() {
|
|
243
|
+
if (FLAGS_histogram) {
|
|
244
|
+
double now = Env::Default()->NowMicros() * 1e-6;
|
|
245
|
+
double micros = (now - last_op_finish_) * 1e6;
|
|
246
|
+
hist_.Add(micros);
|
|
247
|
+
if (micros > 20000) {
|
|
248
|
+
fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
|
|
249
|
+
fflush(stderr);
|
|
250
|
+
}
|
|
251
|
+
last_op_finish_ = now;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
done_++;
|
|
255
|
+
if (done_ >= next_report_) {
|
|
256
|
+
if (next_report_ < 1000) next_report_ += 100;
|
|
257
|
+
else if (next_report_ < 5000) next_report_ += 500;
|
|
258
|
+
else if (next_report_ < 10000) next_report_ += 1000;
|
|
259
|
+
else if (next_report_ < 50000) next_report_ += 5000;
|
|
260
|
+
else if (next_report_ < 100000) next_report_ += 10000;
|
|
261
|
+
else if (next_report_ < 500000) next_report_ += 50000;
|
|
262
|
+
else next_report_ += 100000;
|
|
263
|
+
fprintf(stderr, "... finished %d ops%30s\r", done_, "");
|
|
264
|
+
fflush(stderr);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
void Stop(const Slice& name) {
|
|
269
|
+
double finish = Env::Default()->NowMicros() * 1e-6;
|
|
270
|
+
|
|
271
|
+
// Pretend at least one op was done in case we are running a benchmark
|
|
272
|
+
// that does nto call FinishedSingleOp().
|
|
273
|
+
if (done_ < 1) done_ = 1;
|
|
274
|
+
|
|
275
|
+
if (bytes_ > 0) {
|
|
276
|
+
char rate[100];
|
|
277
|
+
snprintf(rate, sizeof(rate), "%6.1f MB/s",
|
|
278
|
+
(bytes_ / 1048576.0) / (finish - start_));
|
|
279
|
+
if (!message_.empty()) {
|
|
280
|
+
message_ = std::string(rate) + " " + message_;
|
|
281
|
+
} else {
|
|
282
|
+
message_ = rate;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
|
|
287
|
+
name.ToString().c_str(),
|
|
288
|
+
(finish - start_) * 1e6 / done_,
|
|
289
|
+
(message_.empty() ? "" : " "),
|
|
290
|
+
message_.c_str());
|
|
291
|
+
if (FLAGS_histogram) {
|
|
292
|
+
fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
|
|
293
|
+
}
|
|
294
|
+
fflush(stdout);
|
|
295
|
+
|
|
296
|
+
if (!post_message_.empty()) {
|
|
297
|
+
fprintf(stdout, "\n%s\n", post_message_.c_str());
|
|
298
|
+
post_message_.clear();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public:
|
|
303
|
+
enum Order {
|
|
304
|
+
SEQUENTIAL,
|
|
305
|
+
RANDOM
|
|
306
|
+
};
|
|
307
|
+
enum DBState {
|
|
308
|
+
FRESH,
|
|
309
|
+
EXISTING
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
Benchmark()
|
|
313
|
+
: cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL),
|
|
314
|
+
db_(NULL),
|
|
315
|
+
num_(FLAGS_num),
|
|
316
|
+
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
|
317
|
+
heap_counter_(0),
|
|
318
|
+
bytes_(0),
|
|
319
|
+
rand_(301) {
|
|
320
|
+
std::vector<std::string> files;
|
|
321
|
+
Env::Default()->GetChildren("/tmp/dbbench", &files);
|
|
322
|
+
for (int i = 0; i < files.size(); i++) {
|
|
323
|
+
if (Slice(files[i]).starts_with("heap-")) {
|
|
324
|
+
Env::Default()->DeleteFile("/tmp/dbbench/" + files[i]);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (!FLAGS_use_existing_db) {
|
|
328
|
+
DestroyDB("/tmp/dbbench", Options());
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
~Benchmark() {
|
|
333
|
+
delete db_;
|
|
334
|
+
delete cache_;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
void Run() {
|
|
338
|
+
PrintHeader();
|
|
339
|
+
Open();
|
|
340
|
+
|
|
341
|
+
const char* benchmarks = FLAGS_benchmarks;
|
|
342
|
+
while (benchmarks != NULL) {
|
|
343
|
+
const char* sep = strchr(benchmarks, ',');
|
|
344
|
+
Slice name;
|
|
345
|
+
if (sep == NULL) {
|
|
346
|
+
name = benchmarks;
|
|
347
|
+
benchmarks = NULL;
|
|
348
|
+
} else {
|
|
349
|
+
name = Slice(benchmarks, sep - benchmarks);
|
|
350
|
+
benchmarks = sep + 1;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
Start();
|
|
354
|
+
|
|
355
|
+
WriteOptions write_options;
|
|
356
|
+
bool known = true;
|
|
357
|
+
if (name == Slice("fillseq")) {
|
|
358
|
+
Write(write_options, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
|
|
359
|
+
} else if (name == Slice("fillbatch")) {
|
|
360
|
+
Write(write_options, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000);
|
|
361
|
+
} else if (name == Slice("fillrandom")) {
|
|
362
|
+
Write(write_options, RANDOM, FRESH, num_, FLAGS_value_size, 1);
|
|
363
|
+
} else if (name == Slice("overwrite")) {
|
|
364
|
+
Write(write_options, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
|
|
365
|
+
} else if (name == Slice("fillsync")) {
|
|
366
|
+
write_options.sync = true;
|
|
367
|
+
Write(write_options, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
|
|
368
|
+
} else if (name == Slice("fill100K")) {
|
|
369
|
+
Write(write_options, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
|
|
370
|
+
} else if (name == Slice("readseq")) {
|
|
371
|
+
ReadSequential();
|
|
372
|
+
} else if (name == Slice("readreverse")) {
|
|
373
|
+
ReadReverse();
|
|
374
|
+
} else if (name == Slice("readrandom")) {
|
|
375
|
+
ReadRandom();
|
|
376
|
+
} else if (name == Slice("readhot")) {
|
|
377
|
+
ReadHot();
|
|
378
|
+
} else if (name == Slice("readrandomsmall")) {
|
|
379
|
+
int n = reads_;
|
|
380
|
+
reads_ /= 1000;
|
|
381
|
+
ReadRandom();
|
|
382
|
+
reads_ = n;
|
|
383
|
+
} else if (name == Slice("compact")) {
|
|
384
|
+
Compact();
|
|
385
|
+
} else if (name == Slice("crc32c")) {
|
|
386
|
+
Crc32c(4096, "(4K per op)");
|
|
387
|
+
} else if (name == Slice("acquireload")) {
|
|
388
|
+
AcquireLoad();
|
|
389
|
+
} else if (name == Slice("snappycomp")) {
|
|
390
|
+
SnappyCompress();
|
|
391
|
+
} else if (name == Slice("snappyuncomp")) {
|
|
392
|
+
SnappyUncompress();
|
|
393
|
+
} else if (name == Slice("heapprofile")) {
|
|
394
|
+
HeapProfile();
|
|
395
|
+
} else if (name == Slice("stats")) {
|
|
396
|
+
PrintStats();
|
|
397
|
+
} else {
|
|
398
|
+
known = false;
|
|
399
|
+
if (name != Slice()) { // No error message for empty name
|
|
400
|
+
fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (known) {
|
|
404
|
+
Stop(name);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private:
|
|
410
|
+
void Crc32c(int size, const char* label) {
|
|
411
|
+
// Checksum about 500MB of data total
|
|
412
|
+
std::string data(size, 'x');
|
|
413
|
+
int64_t bytes = 0;
|
|
414
|
+
uint32_t crc = 0;
|
|
415
|
+
while (bytes < 500 * 1048576) {
|
|
416
|
+
crc = crc32c::Value(data.data(), size);
|
|
417
|
+
FinishedSingleOp();
|
|
418
|
+
bytes += size;
|
|
419
|
+
}
|
|
420
|
+
// Print so result is not dead
|
|
421
|
+
fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
|
|
422
|
+
|
|
423
|
+
bytes_ = bytes;
|
|
424
|
+
message_ = label;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
void AcquireLoad() {
|
|
428
|
+
int dummy;
|
|
429
|
+
port::AtomicPointer ap(&dummy);
|
|
430
|
+
int count = 0;
|
|
431
|
+
void *ptr = NULL;
|
|
432
|
+
message_ = "(each op is 1000 loads)";
|
|
433
|
+
while (count < 100000) {
|
|
434
|
+
for (int i = 0; i < 1000; i++) {
|
|
435
|
+
ptr = ap.Acquire_Load();
|
|
436
|
+
}
|
|
437
|
+
count++;
|
|
438
|
+
FinishedSingleOp();
|
|
439
|
+
}
|
|
440
|
+
if (ptr == NULL) exit(1); // Disable unused variable warning.
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
void SnappyCompress() {
|
|
444
|
+
Slice input = gen_.Generate(Options().block_size);
|
|
445
|
+
int64_t bytes = 0;
|
|
446
|
+
int64_t produced = 0;
|
|
447
|
+
bool ok = true;
|
|
448
|
+
std::string compressed;
|
|
449
|
+
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
|
450
|
+
ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
|
|
451
|
+
produced += compressed.size();
|
|
452
|
+
bytes += input.size();
|
|
453
|
+
FinishedSingleOp();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!ok) {
|
|
457
|
+
message_ = "(snappy failure)";
|
|
458
|
+
} else {
|
|
459
|
+
char buf[100];
|
|
460
|
+
snprintf(buf, sizeof(buf), "(output: %.1f%%)",
|
|
461
|
+
(produced * 100.0) / bytes);
|
|
462
|
+
message_ = buf;
|
|
463
|
+
bytes_ = bytes;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
void SnappyUncompress() {
|
|
468
|
+
Slice input = gen_.Generate(Options().block_size);
|
|
469
|
+
std::string compressed;
|
|
470
|
+
bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
|
|
471
|
+
int64_t bytes = 0;
|
|
472
|
+
std::string uncompressed;
|
|
473
|
+
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
|
474
|
+
ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),
|
|
475
|
+
&uncompressed);
|
|
476
|
+
bytes += uncompressed.size();
|
|
477
|
+
FinishedSingleOp();
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (!ok) {
|
|
481
|
+
message_ = "(snappy failure)";
|
|
482
|
+
} else {
|
|
483
|
+
bytes_ = bytes;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
void Open() {
|
|
488
|
+
assert(db_ == NULL);
|
|
489
|
+
Options options;
|
|
490
|
+
options.create_if_missing = !FLAGS_use_existing_db;
|
|
491
|
+
options.block_cache = cache_;
|
|
492
|
+
options.write_buffer_size = FLAGS_write_buffer_size;
|
|
493
|
+
Status s = DB::Open(options, "/tmp/dbbench", &db_);
|
|
494
|
+
if (!s.ok()) {
|
|
495
|
+
fprintf(stderr, "open error: %s\n", s.ToString().c_str());
|
|
496
|
+
exit(1);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
void Write(const WriteOptions& options, Order order, DBState state,
|
|
501
|
+
int num_entries, int value_size, int entries_per_batch) {
|
|
502
|
+
if (state == FRESH) {
|
|
503
|
+
if (FLAGS_use_existing_db) {
|
|
504
|
+
message_ = "skipping (--use_existing_db is true)";
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
delete db_;
|
|
508
|
+
db_ = NULL;
|
|
509
|
+
DestroyDB("/tmp/dbbench", Options());
|
|
510
|
+
Open();
|
|
511
|
+
Start(); // Do not count time taken to destroy/open
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (num_entries != num_) {
|
|
515
|
+
char msg[100];
|
|
516
|
+
snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
|
|
517
|
+
message_ = msg;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
WriteBatch batch;
|
|
521
|
+
Status s;
|
|
522
|
+
std::string val;
|
|
523
|
+
for (int i = 0; i < num_entries; i += entries_per_batch) {
|
|
524
|
+
batch.Clear();
|
|
525
|
+
for (int j = 0; j < entries_per_batch; j++) {
|
|
526
|
+
const int k = (order == SEQUENTIAL) ? i+j : (rand_.Next() % FLAGS_num);
|
|
527
|
+
char key[100];
|
|
528
|
+
snprintf(key, sizeof(key), "%016d", k);
|
|
529
|
+
batch.Put(key, gen_.Generate(value_size));
|
|
530
|
+
bytes_ += value_size + strlen(key);
|
|
531
|
+
FinishedSingleOp();
|
|
532
|
+
}
|
|
533
|
+
s = db_->Write(options, &batch);
|
|
534
|
+
if (!s.ok()) {
|
|
535
|
+
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
|
536
|
+
exit(1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
void ReadSequential() {
|
|
542
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
543
|
+
int i = 0;
|
|
544
|
+
for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {
|
|
545
|
+
bytes_ += iter->key().size() + iter->value().size();
|
|
546
|
+
FinishedSingleOp();
|
|
547
|
+
++i;
|
|
548
|
+
}
|
|
549
|
+
delete iter;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
void ReadReverse() {
|
|
553
|
+
Iterator* iter = db_->NewIterator(ReadOptions());
|
|
554
|
+
int i = 0;
|
|
555
|
+
for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {
|
|
556
|
+
bytes_ += iter->key().size() + iter->value().size();
|
|
557
|
+
FinishedSingleOp();
|
|
558
|
+
++i;
|
|
559
|
+
}
|
|
560
|
+
delete iter;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
void ReadRandom() {
|
|
564
|
+
ReadOptions options;
|
|
565
|
+
std::string value;
|
|
566
|
+
for (int i = 0; i < reads_; i++) {
|
|
567
|
+
char key[100];
|
|
568
|
+
const int k = rand_.Next() % FLAGS_num;
|
|
569
|
+
snprintf(key, sizeof(key), "%016d", k);
|
|
570
|
+
db_->Get(options, key, &value);
|
|
571
|
+
FinishedSingleOp();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
void ReadHot() {
|
|
576
|
+
ReadOptions options;
|
|
577
|
+
std::string value;
|
|
578
|
+
const int range = (FLAGS_num + 99) / 100;
|
|
579
|
+
for (int i = 0; i < reads_; i++) {
|
|
580
|
+
char key[100];
|
|
581
|
+
const int k = rand_.Next() % range;
|
|
582
|
+
snprintf(key, sizeof(key), "%016d", k);
|
|
583
|
+
db_->Get(options, key, &value);
|
|
584
|
+
FinishedSingleOp();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
void Compact() {
|
|
589
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
|
590
|
+
dbi->TEST_CompactMemTable();
|
|
591
|
+
int max_level_with_files = 1;
|
|
592
|
+
for (int level = 1; level < config::kNumLevels; level++) {
|
|
593
|
+
std::string property;
|
|
594
|
+
char name[100];
|
|
595
|
+
snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
|
|
596
|
+
if (db_->GetProperty(name, &property) && atoi(property.c_str()) > 0) {
|
|
597
|
+
max_level_with_files = level;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
for (int level = 0; level < max_level_with_files; level++) {
|
|
601
|
+
dbi->TEST_CompactRange(level, "", "~");
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
void PrintStats() {
|
|
606
|
+
std::string stats;
|
|
607
|
+
if (!db_->GetProperty("leveldb.stats", &stats)) {
|
|
608
|
+
message_ = "(failed)";
|
|
609
|
+
} else {
|
|
610
|
+
post_message_ = stats;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
static void WriteToFile(void* arg, const char* buf, int n) {
|
|
615
|
+
reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
void HeapProfile() {
|
|
619
|
+
char fname[100];
|
|
620
|
+
snprintf(fname, sizeof(fname), "/tmp/dbbench/heap-%04d", ++heap_counter_);
|
|
621
|
+
WritableFile* file;
|
|
622
|
+
Status s = Env::Default()->NewWritableFile(fname, &file);
|
|
623
|
+
if (!s.ok()) {
|
|
624
|
+
message_ = s.ToString();
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
bool ok = port::GetHeapProfile(WriteToFile, file);
|
|
628
|
+
delete file;
|
|
629
|
+
if (!ok) {
|
|
630
|
+
message_ = "not supported";
|
|
631
|
+
Env::Default()->DeleteFile(fname);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
int main(int argc, char** argv) {
|
|
639
|
+
FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
|
|
640
|
+
FLAGS_open_files = leveldb::Options().max_open_files;
|
|
641
|
+
|
|
642
|
+
for (int i = 1; i < argc; i++) {
|
|
643
|
+
double d;
|
|
644
|
+
int n;
|
|
645
|
+
char junk;
|
|
646
|
+
if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
|
|
647
|
+
FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
|
|
648
|
+
} else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
|
|
649
|
+
FLAGS_compression_ratio = d;
|
|
650
|
+
} else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
|
|
651
|
+
(n == 0 || n == 1)) {
|
|
652
|
+
FLAGS_histogram = n;
|
|
653
|
+
} else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
|
|
654
|
+
(n == 0 || n == 1)) {
|
|
655
|
+
FLAGS_use_existing_db = n;
|
|
656
|
+
} else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
|
|
657
|
+
FLAGS_num = n;
|
|
658
|
+
} else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
|
|
659
|
+
FLAGS_reads = n;
|
|
660
|
+
} else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
|
|
661
|
+
FLAGS_value_size = n;
|
|
662
|
+
} else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
|
|
663
|
+
FLAGS_write_buffer_size = n;
|
|
664
|
+
} else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
|
|
665
|
+
FLAGS_cache_size = n;
|
|
666
|
+
} else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) {
|
|
667
|
+
FLAGS_open_files = n;
|
|
668
|
+
} else {
|
|
669
|
+
fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
|
|
670
|
+
exit(1);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
leveldb::Benchmark benchmark;
|
|
675
|
+
benchmark.Run();
|
|
676
|
+
return 0;
|
|
677
|
+
}
|