leveldb-ruby 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/README +17 -0
  2. data/ext/leveldb/extconf.rb +10 -0
  3. data/ext/leveldb/leveldb.cc +181 -0
  4. data/leveldb/Makefile +172 -0
  5. data/leveldb/db/builder.cc +90 -0
  6. data/leveldb/db/builder.h +36 -0
  7. data/leveldb/db/corruption_test.cc +354 -0
  8. data/leveldb/db/db_bench.cc +677 -0
  9. data/leveldb/db/db_impl.cc +1236 -0
  10. data/leveldb/db/db_impl.h +180 -0
  11. data/leveldb/db/db_iter.cc +298 -0
  12. data/leveldb/db/db_iter.h +26 -0
  13. data/leveldb/db/db_test.cc +1192 -0
  14. data/leveldb/db/dbformat.cc +87 -0
  15. data/leveldb/db/dbformat.h +165 -0
  16. data/leveldb/db/dbformat_test.cc +112 -0
  17. data/leveldb/db/filename.cc +135 -0
  18. data/leveldb/db/filename.h +80 -0
  19. data/leveldb/db/filename_test.cc +122 -0
  20. data/leveldb/db/log_format.h +35 -0
  21. data/leveldb/db/log_reader.cc +254 -0
  22. data/leveldb/db/log_reader.h +108 -0
  23. data/leveldb/db/log_test.cc +500 -0
  24. data/leveldb/db/log_writer.cc +103 -0
  25. data/leveldb/db/log_writer.h +48 -0
  26. data/leveldb/db/memtable.cc +108 -0
  27. data/leveldb/db/memtable.h +85 -0
  28. data/leveldb/db/repair.cc +384 -0
  29. data/leveldb/db/skiplist.h +378 -0
  30. data/leveldb/db/skiplist_test.cc +378 -0
  31. data/leveldb/db/snapshot.h +66 -0
  32. data/leveldb/db/table_cache.cc +95 -0
  33. data/leveldb/db/table_cache.h +50 -0
  34. data/leveldb/db/version_edit.cc +268 -0
  35. data/leveldb/db/version_edit.h +106 -0
  36. data/leveldb/db/version_edit_test.cc +46 -0
  37. data/leveldb/db/version_set.cc +1060 -0
  38. data/leveldb/db/version_set.h +306 -0
  39. data/leveldb/db/write_batch.cc +138 -0
  40. data/leveldb/db/write_batch_internal.h +45 -0
  41. data/leveldb/db/write_batch_test.cc +89 -0
  42. data/leveldb/include/leveldb/cache.h +99 -0
  43. data/leveldb/include/leveldb/comparator.h +63 -0
  44. data/leveldb/include/leveldb/db.h +148 -0
  45. data/leveldb/include/leveldb/env.h +302 -0
  46. data/leveldb/include/leveldb/iterator.h +100 -0
  47. data/leveldb/include/leveldb/options.h +198 -0
  48. data/leveldb/include/leveldb/slice.h +109 -0
  49. data/leveldb/include/leveldb/status.h +100 -0
  50. data/leveldb/include/leveldb/table.h +70 -0
  51. data/leveldb/include/leveldb/table_builder.h +91 -0
  52. data/leveldb/include/leveldb/write_batch.h +64 -0
  53. data/leveldb/port/port.h +23 -0
  54. data/leveldb/port/port_android.cc +64 -0
  55. data/leveldb/port/port_android.h +150 -0
  56. data/leveldb/port/port_chromium.cc +80 -0
  57. data/leveldb/port/port_chromium.h +97 -0
  58. data/leveldb/port/port_example.h +115 -0
  59. data/leveldb/port/port_osx.cc +50 -0
  60. data/leveldb/port/port_osx.h +125 -0
  61. data/leveldb/port/port_posix.cc +50 -0
  62. data/leveldb/port/port_posix.h +94 -0
  63. data/leveldb/port/sha1_portable.cc +298 -0
  64. data/leveldb/port/sha1_portable.h +25 -0
  65. data/leveldb/port/sha1_test.cc +39 -0
  66. data/leveldb/port/win/stdint.h +24 -0
  67. data/leveldb/table/block.cc +263 -0
  68. data/leveldb/table/block.h +43 -0
  69. data/leveldb/table/block_builder.cc +109 -0
  70. data/leveldb/table/block_builder.h +57 -0
  71. data/leveldb/table/format.cc +131 -0
  72. data/leveldb/table/format.h +103 -0
  73. data/leveldb/table/iterator.cc +67 -0
  74. data/leveldb/table/iterator_wrapper.h +63 -0
  75. data/leveldb/table/merger.cc +197 -0
  76. data/leveldb/table/merger.h +26 -0
  77. data/leveldb/table/table.cc +175 -0
  78. data/leveldb/table/table_builder.cc +227 -0
  79. data/leveldb/table/table_test.cc +845 -0
  80. data/leveldb/table/two_level_iterator.cc +182 -0
  81. data/leveldb/table/two_level_iterator.h +34 -0
  82. data/leveldb/util/arena.cc +68 -0
  83. data/leveldb/util/arena.h +68 -0
  84. data/leveldb/util/arena_test.cc +68 -0
  85. data/leveldb/util/cache.cc +255 -0
  86. data/leveldb/util/cache_test.cc +169 -0
  87. data/leveldb/util/coding.cc +194 -0
  88. data/leveldb/util/coding.h +104 -0
  89. data/leveldb/util/coding_test.cc +173 -0
  90. data/leveldb/util/comparator.cc +72 -0
  91. data/leveldb/util/crc32c.cc +332 -0
  92. data/leveldb/util/crc32c.h +45 -0
  93. data/leveldb/util/crc32c_test.cc +72 -0
  94. data/leveldb/util/env.cc +77 -0
  95. data/leveldb/util/env_chromium.cc +612 -0
  96. data/leveldb/util/env_posix.cc +606 -0
  97. data/leveldb/util/env_test.cc +102 -0
  98. data/leveldb/util/hash.cc +45 -0
  99. data/leveldb/util/hash.h +19 -0
  100. data/leveldb/util/histogram.cc +128 -0
  101. data/leveldb/util/histogram.h +41 -0
  102. data/leveldb/util/logging.cc +81 -0
  103. data/leveldb/util/logging.h +47 -0
  104. data/leveldb/util/mutexlock.h +39 -0
  105. data/leveldb/util/options.cc +28 -0
  106. data/leveldb/util/random.h +59 -0
  107. data/leveldb/util/status.cc +75 -0
  108. data/leveldb/util/testharness.cc +65 -0
  109. data/leveldb/util/testharness.h +129 -0
  110. data/leveldb/util/testutil.cc +51 -0
  111. data/leveldb/util/testutil.h +53 -0
  112. data/lib/leveldb.rb +36 -0
  113. 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
+ }