leveldb 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45ece05a2078c65923a11be951e9bd4ed6065d52
4
- data.tar.gz: 1e0067e76973f4562a84a8831cfb79e77289292a
3
+ metadata.gz: 8ac9eef1bb10dc5b82a7ea8cc6f4c2d64cc5d04c
4
+ data.tar.gz: 04748952f04ef49c0c5622712ade7f4b3fc211e8
5
5
  SHA512:
6
- metadata.gz: d82cbd5f00b07320b1f02d01964b0f46c4dcf494e65dac1156e18aff8079c8ebddef747cabe1266111f3c5f88d2bd58c5142149a24cc2f07201e337688b54180
7
- data.tar.gz: 00d47b0cf131dcf9edd6d2ddbf2fc4aaa64eab36179f0a5994fd0a3792067070b54e7fe79bfbdb06f3d9133f0e203c94b27320f269060ce978ef7bf9676eeabf
6
+ metadata.gz: 06f097cb8df8f2f5679dfced115ee73236caa4770aea85302d54c58dbf9d451982356e404cd3050ed8413df1bd7e4bd6379e5a7789ef2f5b81db48f216f40346
7
+ data.tar.gz: 2f846938f51b43632a5c9629ea618eccbcdabc42ccba33f63fe0e8a6ade6dbf026485c9461b8b99ab7cce1400135e15d1ba29311c58496963d90b99a9337dbf8
data/README.md CHANGED
@@ -36,8 +36,9 @@ embedded database. LevelDB is a persistent ordered map.
36
36
  $ brew install snappy
37
37
  $ git clone git://github.com/DAddYE/leveldb.git
38
38
  $ cd leveldb
39
- $ rake compile
40
- $ rake console
39
+ $ bundle install
40
+ $ bundle exec rake compile
41
+ $ bundle exec rake console
41
42
 
42
43
  ### Standard
43
44
 
@@ -130,6 +131,74 @@ db.read_property('leveldb.stats')
130
131
  db.stats
131
132
  ```
132
133
 
134
+ ## Benchmarks
135
+
136
+ _Preface_: those are only for general purpose, I know that [zedshaw](http://zedshaw.com/essays/programmer_stats.html)
137
+ will kill me for this, but ... on my mac:
138
+
139
+ Model Identifier: MacBookPro10,1
140
+ Processor Name: Intel Core i7
141
+ Processor Speed: 2.3 GHz
142
+ Number of Processors: 1
143
+ Total Number of Cores: 4
144
+ L2 Cache (per Core): 256 KB
145
+ L3 Cache: 6 MB
146
+ Memory: 8 GB
147
+
148
+ The benchmark code is in [benchmark/leveldb.rb](/benchmark/leveldb.rb)
149
+
150
+ Writing/Reading `100mb` of _very_ random data of `10kb` each:
151
+
152
+ ### Without compression:
153
+
154
+ user system total real
155
+ put 0.530000 0.310000 0.840000 ( 1.420387)
156
+ get 0.800000 0.460000 1.260000 ( 2.626631)
157
+
158
+ Level Files Size(MB) Time(sec) Read(MB) Write(MB)
159
+ --------------------------------------------------
160
+ 0 1 0 0 0 0
161
+ 2 50 98 0 0 0
162
+ 3 1 2 0 0 0
163
+
164
+ ### With compression:
165
+
166
+ user system total real
167
+ put 0.850000 0.320000 1.170000 ( 1.721609)
168
+ get 1.160000 0.480000 1.640000 ( 2.703543)
169
+
170
+ Level Files Size(MB) Time(sec) Read(MB) Write(MB)
171
+ --------------------------------------------------
172
+ 0 1 0 0 0 0
173
+ 1 5 10 0 0 0
174
+ 2 45 90 0 0 0
175
+
176
+ **NOTE**: as you can see `snappy` can't compress that kind of _very very_
177
+ random data, but I was not interested to bench snappy (as a compressor) but
178
+ only to see how (eventually) much _slower_ will be using it. As you can see,
179
+ only a _few_ and on normal _data_ the db size will be much much better!
180
+
181
+ ### With batch:
182
+
183
+ user system total real
184
+ put 0.260000 0.170000 0.430000 ( 0.433407)
185
+
186
+ Level Files Size(MB) Time(sec) Read(MB) Write(MB)
187
+ --------------------------------------------------
188
+ 0 1 100 1 0 100
189
+
190
+
191
+ ## Difference between a c++ pure ruby impl?
192
+
193
+ This, again, only for general purpose, but I want to compare the `c++` implementation
194
+ of [leveldb-ruby](https://github.com/wmorgan/leveldb-ruby) with this that use ffi.
195
+
196
+ I'm aware that this lib is 1 year older, but for those who cares, the basic bench:
197
+
198
+ user system total real
199
+ put 0.440000 0.300000 0.740000 ( 1.363188)
200
+ get 0.440000 0.440000 1.460000 ( 2.407274)
201
+
133
202
  ## Todo
134
203
 
135
204
  1. Add pluggable serializers
data/ext/leveldb/Makefile CHANGED
@@ -31,6 +31,7 @@ TESTHARNESS = ./util/testharness.o $(TESTUTIL)
31
31
 
32
32
  TESTS = \
33
33
  arena_test \
34
+ autocompact_test \
34
35
  bloom_test \
35
36
  c_test \
36
37
  cache_test \
@@ -70,7 +71,7 @@ SHARED = $(SHARED1)
70
71
  else
71
72
  # Update db.h if you change these.
72
73
  SHARED_MAJOR = 1
73
- SHARED_MINOR = 12
74
+ SHARED_MINOR = 13
74
75
  SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
75
76
  SHARED2 = $(SHARED1).$(SHARED_MAJOR)
76
77
  SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
@@ -114,6 +115,9 @@ leveldbutil: db/leveldb_main.o $(LIBOBJECTS)
114
115
  arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
115
116
  $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
116
117
 
118
+ autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS)
119
+ $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
120
+
117
121
  bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
118
122
  $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
119
123
 
@@ -0,0 +1,118 @@
1
+ // Copyright (c) 2013 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 "leveldb/cache.h"
8
+ #include "util/testharness.h"
9
+ #include "util/testutil.h"
10
+
11
+ namespace leveldb {
12
+
13
+ class AutoCompactTest {
14
+ public:
15
+ std::string dbname_;
16
+ Cache* tiny_cache_;
17
+ Options options_;
18
+ DB* db_;
19
+
20
+ AutoCompactTest() {
21
+ dbname_ = test::TmpDir() + "/autocompact_test";
22
+ tiny_cache_ = NewLRUCache(100);
23
+ options_.block_cache = tiny_cache_;
24
+ DestroyDB(dbname_, options_);
25
+ options_.create_if_missing = true;
26
+ options_.compression = kNoCompression;
27
+ ASSERT_OK(DB::Open(options_, dbname_, &db_));
28
+ }
29
+
30
+ ~AutoCompactTest() {
31
+ delete db_;
32
+ DestroyDB(dbname_, Options());
33
+ delete tiny_cache_;
34
+ }
35
+
36
+ std::string Key(int i) {
37
+ char buf[100];
38
+ snprintf(buf, sizeof(buf), "key%06d", i);
39
+ return std::string(buf);
40
+ }
41
+
42
+ uint64_t Size(const Slice& start, const Slice& limit) {
43
+ Range r(start, limit);
44
+ uint64_t size;
45
+ db_->GetApproximateSizes(&r, 1, &size);
46
+ return size;
47
+ }
48
+
49
+ void DoReads(int n);
50
+ };
51
+
52
+ static const int kValueSize = 200 * 1024;
53
+ static const int kTotalSize = 100 * 1024 * 1024;
54
+ static const int kCount = kTotalSize / kValueSize;
55
+
56
+ // Read through the first n keys repeatedly and check that they get
57
+ // compacted (verified by checking the size of the key space).
58
+ void AutoCompactTest::DoReads(int n) {
59
+ std::string value(kValueSize, 'x');
60
+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
61
+
62
+ // Fill database
63
+ for (int i = 0; i < kCount; i++) {
64
+ ASSERT_OK(db_->Put(WriteOptions(), Key(i), value));
65
+ }
66
+ ASSERT_OK(dbi->TEST_CompactMemTable());
67
+
68
+ // Delete everything
69
+ for (int i = 0; i < kCount; i++) {
70
+ ASSERT_OK(db_->Delete(WriteOptions(), Key(i)));
71
+ }
72
+ ASSERT_OK(dbi->TEST_CompactMemTable());
73
+
74
+ // Get initial measurement of the space we will be reading.
75
+ const int64_t initial_size = Size(Key(0), Key(n));
76
+ const int64_t initial_other_size = Size(Key(n), Key(kCount));
77
+
78
+ // Read until size drops significantly.
79
+ std::string limit_key = Key(n);
80
+ for (int read = 0; true; read++) {
81
+ ASSERT_LT(read, 100) << "Taking too long to compact";
82
+ Iterator* iter = db_->NewIterator(ReadOptions());
83
+ for (iter->SeekToFirst();
84
+ iter->Valid() && iter->key().ToString() < limit_key;
85
+ iter->Next()) {
86
+ // Drop data
87
+ }
88
+ delete iter;
89
+ // Wait a little bit to allow any triggered compactions to complete.
90
+ Env::Default()->SleepForMicroseconds(1000000);
91
+ uint64_t size = Size(Key(0), Key(n));
92
+ fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n",
93
+ read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0);
94
+ if (size <= initial_size/10) {
95
+ break;
96
+ }
97
+ }
98
+
99
+ // Verify that the size of the key space not touched by the reads
100
+ // is pretty much unchanged.
101
+ const int64_t final_other_size = Size(Key(n), Key(kCount));
102
+ ASSERT_LE(final_other_size, initial_other_size + 1048576);
103
+ ASSERT_GE(final_other_size, initial_other_size/5 - 1048576);
104
+ }
105
+
106
+ TEST(AutoCompactTest, ReadAll) {
107
+ DoReads(kCount);
108
+ }
109
+
110
+ TEST(AutoCompactTest, ReadHalf) {
111
+ DoReads(kCount/2);
112
+ }
113
+
114
+ } // namespace leveldb
115
+
116
+ int main(int argc, char** argv) {
117
+ return leveldb::test::RunAllTests();
118
+ }
@@ -35,6 +35,7 @@ class CorruptionTest {
35
35
  CorruptionTest() {
36
36
  tiny_cache_ = NewLRUCache(100);
37
37
  options_.env = &env_;
38
+ options_.block_cache = tiny_cache_;
38
39
  dbname_ = test::TmpDir() + "/db_test";
39
40
  DestroyDB(dbname_, options_);
40
41
 
@@ -50,17 +51,14 @@ class CorruptionTest {
50
51
  delete tiny_cache_;
51
52
  }
52
53
 
53
- Status TryReopen(Options* options = NULL) {
54
+ Status TryReopen() {
54
55
  delete db_;
55
56
  db_ = NULL;
56
- Options opt = (options ? *options : options_);
57
- opt.env = &env_;
58
- opt.block_cache = tiny_cache_;
59
- return DB::Open(opt, dbname_, &db_);
57
+ return DB::Open(options_, dbname_, &db_);
60
58
  }
61
59
 
62
- void Reopen(Options* options = NULL) {
63
- ASSERT_OK(TryReopen(options));
60
+ void Reopen() {
61
+ ASSERT_OK(TryReopen());
64
62
  }
65
63
 
66
64
  void RepairDB() {
@@ -92,6 +90,10 @@ class CorruptionTest {
92
90
  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
93
91
  uint64_t key;
94
92
  Slice in(iter->key());
93
+ if (in == "" || in == "~") {
94
+ // Ignore boundary keys.
95
+ continue;
96
+ }
95
97
  if (!ConsumeDecimalNumber(&in, &key) ||
96
98
  !in.empty() ||
97
99
  key < next_expected) {
@@ -233,7 +235,7 @@ TEST(CorruptionTest, TableFile) {
233
235
  dbi->TEST_CompactRange(1, NULL, NULL);
234
236
 
235
237
  Corrupt(kTableFile, 100, 1);
236
- Check(99, 99);
238
+ Check(90, 99);
237
239
  }
238
240
 
239
241
  TEST(CorruptionTest, TableFileIndexData) {
@@ -299,7 +301,7 @@ TEST(CorruptionTest, CompactionInputError) {
299
301
  ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));
300
302
 
301
303
  Corrupt(kTableFile, 100, 1);
302
- Check(9, 9);
304
+ Check(5, 9);
303
305
 
304
306
  // Force compactions by writing lots of values
305
307
  Build(10000);
@@ -307,32 +309,23 @@ TEST(CorruptionTest, CompactionInputError) {
307
309
  }
308
310
 
309
311
  TEST(CorruptionTest, CompactionInputErrorParanoid) {
310
- Options options;
311
- options.paranoid_checks = true;
312
- options.write_buffer_size = 1048576;
313
- Reopen(&options);
312
+ options_.paranoid_checks = true;
313
+ options_.write_buffer_size = 512 << 10;
314
+ Reopen();
314
315
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
315
316
 
316
- // Fill levels >= 1 so memtable compaction outputs to level 1
317
- for (int level = 1; level < config::kNumLevels; level++) {
318
- dbi->Put(WriteOptions(), "", "begin");
319
- dbi->Put(WriteOptions(), "~", "end");
317
+ // Make multiple inputs so we need to compact.
318
+ for (int i = 0; i < 2; i++) {
319
+ Build(10);
320
320
  dbi->TEST_CompactMemTable();
321
+ Corrupt(kTableFile, 100, 1);
322
+ env_.SleepForMicroseconds(100000);
321
323
  }
324
+ dbi->CompactRange(NULL, NULL);
322
325
 
323
- Build(10);
324
- dbi->TEST_CompactMemTable();
325
- ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
326
-
327
- Corrupt(kTableFile, 100, 1);
328
- Check(9, 9);
329
-
330
- // Write must eventually fail because of corrupted table
331
- Status s;
326
+ // Write must fail because of corrupted table
332
327
  std::string tmp1, tmp2;
333
- for (int i = 0; i < 10000 && s.ok(); i++) {
334
- s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
335
- }
328
+ Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2));
336
329
  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
337
330
  }
338
331
 
@@ -113,14 +113,14 @@ Options SanitizeOptions(const std::string& dbname,
113
113
  return result;
114
114
  }
115
115
 
116
- DBImpl::DBImpl(const Options& options, const std::string& dbname)
117
- : env_(options.env),
118
- internal_comparator_(options.comparator),
119
- internal_filter_policy_(options.filter_policy),
120
- options_(SanitizeOptions(
121
- dbname, &internal_comparator_, &internal_filter_policy_, options)),
122
- owns_info_log_(options_.info_log != options.info_log),
123
- owns_cache_(options_.block_cache != options.block_cache),
116
+ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
117
+ : env_(raw_options.env),
118
+ internal_comparator_(raw_options.comparator),
119
+ internal_filter_policy_(raw_options.filter_policy),
120
+ options_(SanitizeOptions(dbname, &internal_comparator_,
121
+ &internal_filter_policy_, raw_options)),
122
+ owns_info_log_(options_.info_log != raw_options.info_log),
123
+ owns_cache_(options_.block_cache != raw_options.block_cache),
124
124
  dbname_(dbname),
125
125
  db_lock_(NULL),
126
126
  shutting_down_(NULL),
@@ -130,6 +130,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
130
130
  logfile_(NULL),
131
131
  logfile_number_(0),
132
132
  log_(NULL),
133
+ seed_(0),
133
134
  tmp_batch_(new WriteBatch),
134
135
  bg_compaction_scheduled_(false),
135
136
  manual_compaction_(NULL),
@@ -138,7 +139,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
138
139
  has_imm_.Release_Store(NULL);
139
140
 
140
141
  // Reserve ten files or so for other uses and give the rest to TableCache.
141
- const int table_cache_size = options.max_open_files - kNumNonTableCacheFiles;
142
+ const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles;
142
143
  table_cache_ = new TableCache(dbname_, &options_, table_cache_size);
143
144
 
144
145
  versions_ = new VersionSet(dbname_, &options_, table_cache_,
@@ -1027,7 +1028,8 @@ static void CleanupIteratorState(void* arg1, void* arg2) {
1027
1028
  } // namespace
1028
1029
 
1029
1030
  Iterator* DBImpl::NewInternalIterator(const ReadOptions& options,
1030
- SequenceNumber* latest_snapshot) {
1031
+ SequenceNumber* latest_snapshot,
1032
+ uint32_t* seed) {
1031
1033
  IterState* cleanup = new IterState;
1032
1034
  mutex_.Lock();
1033
1035
  *latest_snapshot = versions_->LastSequence();
@@ -1051,13 +1053,15 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options,
1051
1053
  cleanup->version = versions_->current();
1052
1054
  internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL);
1053
1055
 
1056
+ *seed = ++seed_;
1054
1057
  mutex_.Unlock();
1055
1058
  return internal_iter;
1056
1059
  }
1057
1060
 
1058
1061
  Iterator* DBImpl::TEST_NewInternalIterator() {
1059
1062
  SequenceNumber ignored;
1060
- return NewInternalIterator(ReadOptions(), &ignored);
1063
+ uint32_t ignored_seed;
1064
+ return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed);
1061
1065
  }
1062
1066
 
1063
1067
  int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() {
@@ -1114,12 +1118,21 @@ Status DBImpl::Get(const ReadOptions& options,
1114
1118
 
1115
1119
  Iterator* DBImpl::NewIterator(const ReadOptions& options) {
1116
1120
  SequenceNumber latest_snapshot;
1117
- Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot);
1121
+ uint32_t seed;
1122
+ Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed);
1118
1123
  return NewDBIterator(
1119
- &dbname_, env_, user_comparator(), internal_iter,
1124
+ this, user_comparator(), iter,
1120
1125
  (options.snapshot != NULL
1121
1126
  ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_
1122
- : latest_snapshot));
1127
+ : latest_snapshot),
1128
+ seed);
1129
+ }
1130
+
1131
+ void DBImpl::RecordReadSample(Slice key) {
1132
+ MutexLock l(&mutex_);
1133
+ if (versions_->current()->RecordReadSample(key)) {
1134
+ MaybeScheduleCompaction();
1135
+ }
1123
1136
  }
1124
1137
 
1125
1138
  const Snapshot* DBImpl::GetSnapshot() {
@@ -59,13 +59,19 @@ class DBImpl : public DB {
59
59
  // file at a level >= 1.
60
60
  int64_t TEST_MaxNextLevelOverlappingBytes();
61
61
 
62
+ // Record a sample of bytes read at the specified internal key.
63
+ // Samples are taken approximately once every config::kReadBytesPeriod
64
+ // bytes.
65
+ void RecordReadSample(Slice key);
66
+
62
67
  private:
63
68
  friend class DB;
64
69
  struct CompactionState;
65
70
  struct Writer;
66
71
 
67
72
  Iterator* NewInternalIterator(const ReadOptions&,
68
- SequenceNumber* latest_snapshot);
73
+ SequenceNumber* latest_snapshot,
74
+ uint32_t* seed);
69
75
 
70
76
  Status NewDB();
71
77
 
@@ -135,6 +141,7 @@ class DBImpl : public DB {
135
141
  WritableFile* logfile_;
136
142
  uint64_t logfile_number_;
137
143
  log::Writer* log_;
144
+ uint32_t seed_; // For sampling.
138
145
 
139
146
  // Queue of writers.
140
147
  std::deque<Writer*> writers_;
@@ -5,12 +5,14 @@
5
5
  #include "db/db_iter.h"
6
6
 
7
7
  #include "db/filename.h"
8
+ #include "db/db_impl.h"
8
9
  #include "db/dbformat.h"
9
10
  #include "leveldb/env.h"
10
11
  #include "leveldb/iterator.h"
11
12
  #include "port/port.h"
12
13
  #include "util/logging.h"
13
14
  #include "util/mutexlock.h"
15
+ #include "util/random.h"
14
16
 
15
17
  namespace leveldb {
16
18
 
@@ -46,15 +48,16 @@ class DBIter: public Iterator {
46
48
  kReverse
47
49
  };
48
50
 
49
- DBIter(const std::string* dbname, Env* env,
50
- const Comparator* cmp, Iterator* iter, SequenceNumber s)
51
- : dbname_(dbname),
52
- env_(env),
51
+ DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s,
52
+ uint32_t seed)
53
+ : db_(db),
53
54
  user_comparator_(cmp),
54
55
  iter_(iter),
55
56
  sequence_(s),
56
57
  direction_(kForward),
57
- valid_(false) {
58
+ valid_(false),
59
+ rnd_(seed),
60
+ bytes_counter_(RandomPeriod()) {
58
61
  }
59
62
  virtual ~DBIter() {
60
63
  delete iter_;
@@ -100,8 +103,12 @@ class DBIter: public Iterator {
100
103
  }
101
104
  }
102
105
 
103
- const std::string* const dbname_;
104
- Env* const env_;
106
+ // Pick next gap with average value of config::kReadBytesPeriod.
107
+ ssize_t RandomPeriod() {
108
+ return rnd_.Uniform(2*config::kReadBytesPeriod);
109
+ }
110
+
111
+ DBImpl* db_;
105
112
  const Comparator* const user_comparator_;
106
113
  Iterator* const iter_;
107
114
  SequenceNumber const sequence_;
@@ -112,13 +119,23 @@ class DBIter: public Iterator {
112
119
  Direction direction_;
113
120
  bool valid_;
114
121
 
122
+ Random rnd_;
123
+ ssize_t bytes_counter_;
124
+
115
125
  // No copying allowed
116
126
  DBIter(const DBIter&);
117
127
  void operator=(const DBIter&);
118
128
  };
119
129
 
120
130
  inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
121
- if (!ParseInternalKey(iter_->key(), ikey)) {
131
+ Slice k = iter_->key();
132
+ ssize_t n = k.size() + iter_->value().size();
133
+ bytes_counter_ -= n;
134
+ while (bytes_counter_ < 0) {
135
+ bytes_counter_ += RandomPeriod();
136
+ db_->RecordReadSample(k);
137
+ }
138
+ if (!ParseInternalKey(k, ikey)) {
122
139
  status_ = Status::Corruption("corrupted internal key in DBIter");
123
140
  return false;
124
141
  } else {
@@ -288,12 +305,12 @@ void DBIter::SeekToLast() {
288
305
  } // anonymous namespace
289
306
 
290
307
  Iterator* NewDBIterator(
291
- const std::string* dbname,
292
- Env* env,
308
+ DBImpl* db,
293
309
  const Comparator* user_key_comparator,
294
310
  Iterator* internal_iter,
295
- const SequenceNumber& sequence) {
296
- return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence);
311
+ SequenceNumber sequence,
312
+ uint32_t seed) {
313
+ return new DBIter(db, user_key_comparator, internal_iter, sequence, seed);
297
314
  }
298
315
 
299
316
  } // namespace leveldb
@@ -11,15 +11,17 @@
11
11
 
12
12
  namespace leveldb {
13
13
 
14
+ class DBImpl;
15
+
14
16
  // Return a new iterator that converts internal keys (yielded by
15
17
  // "*internal_iter") that were live at the specified "sequence" number
16
18
  // into appropriate user keys.
17
19
  extern Iterator* NewDBIterator(
18
- const std::string* dbname,
19
- Env* env,
20
+ DBImpl* db,
20
21
  const Comparator* user_key_comparator,
21
22
  Iterator* internal_iter,
22
- const SequenceNumber& sequence);
23
+ SequenceNumber sequence,
24
+ uint32_t seed);
23
25
 
24
26
  } // namespace leveldb
25
27
 
@@ -38,6 +38,9 @@ static const int kL0_StopWritesTrigger = 12;
38
38
  // space if the same key space is being repeatedly overwritten.
39
39
  static const int kMaxMemCompactLevel = 2;
40
40
 
41
+ // Approximate gap in bytes between samples of data read during iteration.
42
+ static const int kReadBytesPeriod = 1048576;
43
+
41
44
  } // namespace config
42
45
 
43
46
  class InternalKey;
@@ -289,6 +289,51 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) {
289
289
  return a->number > b->number;
290
290
  }
291
291
 
292
+ void Version::ForEachOverlapping(Slice user_key, Slice internal_key,
293
+ void* arg,
294
+ bool (*func)(void*, int, FileMetaData*)) {
295
+ // TODO(sanjay): Change Version::Get() to use this function.
296
+ const Comparator* ucmp = vset_->icmp_.user_comparator();
297
+
298
+ // Search level-0 in order from newest to oldest.
299
+ std::vector<FileMetaData*> tmp;
300
+ tmp.reserve(files_[0].size());
301
+ for (uint32_t i = 0; i < files_[0].size(); i++) {
302
+ FileMetaData* f = files_[0][i];
303
+ if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&
304
+ ucmp->Compare(user_key, f->largest.user_key()) <= 0) {
305
+ tmp.push_back(f);
306
+ }
307
+ }
308
+ if (!tmp.empty()) {
309
+ std::sort(tmp.begin(), tmp.end(), NewestFirst);
310
+ for (uint32_t i = 0; i < tmp.size(); i++) {
311
+ if (!(*func)(arg, 0, tmp[i])) {
312
+ return;
313
+ }
314
+ }
315
+ }
316
+
317
+ // Search other levels.
318
+ for (int level = 1; level < config::kNumLevels; level++) {
319
+ size_t num_files = files_[level].size();
320
+ if (num_files == 0) continue;
321
+
322
+ // Binary search to find earliest index whose largest key >= internal_key.
323
+ uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key);
324
+ if (index < num_files) {
325
+ FileMetaData* f = files_[level][index];
326
+ if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) {
327
+ // All of "f" is past any data for user_key
328
+ } else {
329
+ if (!(*func)(arg, level, f)) {
330
+ return;
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+
292
337
  Status Version::Get(const ReadOptions& options,
293
338
  const LookupKey& k,
294
339
  std::string* value,
@@ -401,6 +446,44 @@ bool Version::UpdateStats(const GetStats& stats) {
401
446
  return false;
402
447
  }
403
448
 
449
+ bool Version::RecordReadSample(Slice internal_key) {
450
+ ParsedInternalKey ikey;
451
+ if (!ParseInternalKey(internal_key, &ikey)) {
452
+ return false;
453
+ }
454
+
455
+ struct State {
456
+ GetStats stats; // Holds first matching file
457
+ int matches;
458
+
459
+ static bool Match(void* arg, int level, FileMetaData* f) {
460
+ State* state = reinterpret_cast<State*>(arg);
461
+ state->matches++;
462
+ if (state->matches == 1) {
463
+ // Remember first match.
464
+ state->stats.seek_file = f;
465
+ state->stats.seek_file_level = level;
466
+ }
467
+ // We can stop iterating once we have a second match.
468
+ return state->matches < 2;
469
+ }
470
+ };
471
+
472
+ State state;
473
+ state.matches = 0;
474
+ ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match);
475
+
476
+ // Must have at least two matches since we want to merge across
477
+ // files. But what if we have a single file that contains many
478
+ // overwrites and deletions? Should we have another mechanism for
479
+ // finding such files?
480
+ if (state.matches >= 2) {
481
+ // 1MB cost is about 1 seek (see comment in Builder::Apply).
482
+ return UpdateStats(state.stats);
483
+ }
484
+ return false;
485
+ }
486
+
404
487
  void Version::Ref() {
405
488
  ++refs_;
406
489
  }
@@ -435,10 +518,13 @@ int Version::PickLevelForMemTableOutput(
435
518
  if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) {
436
519
  break;
437
520
  }
438
- GetOverlappingInputs(level + 2, &start, &limit, &overlaps);
439
- const int64_t sum = TotalFileSize(overlaps);
440
- if (sum > kMaxGrandParentOverlapBytes) {
441
- break;
521
+ if (level + 2 < config::kNumLevels) {
522
+ // Check that file does not overlap too many grandparent bytes.
523
+ GetOverlappingInputs(level + 2, &start, &limit, &overlaps);
524
+ const int64_t sum = TotalFileSize(overlaps);
525
+ if (sum > kMaxGrandParentOverlapBytes) {
526
+ break;
527
+ }
442
528
  }
443
529
  level++;
444
530
  }
@@ -452,6 +538,8 @@ void Version::GetOverlappingInputs(
452
538
  const InternalKey* begin,
453
539
  const InternalKey* end,
454
540
  std::vector<FileMetaData*>* inputs) {
541
+ assert(level >= 0);
542
+ assert(level < config::kNumLevels);
455
543
  inputs->clear();
456
544
  Slice user_begin, user_end;
457
545
  if (begin != NULL) {
@@ -78,6 +78,12 @@ class Version {
78
78
  // REQUIRES: lock is held
79
79
  bool UpdateStats(const GetStats& stats);
80
80
 
81
+ // Record a sample of bytes read at the specified internal key.
82
+ // Samples are taken approximately once every config::kReadBytesPeriod
83
+ // bytes. Returns true if a new compaction may need to be triggered.
84
+ // REQUIRES: lock is held
85
+ bool RecordReadSample(Slice key);
86
+
81
87
  // Reference count management (so Versions do not disappear out from
82
88
  // under live iterators)
83
89
  void Ref();
@@ -114,6 +120,15 @@ class Version {
114
120
  class LevelFileNumIterator;
115
121
  Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const;
116
122
 
123
+ // Call func(arg, level, f) for every file that overlaps user_key in
124
+ // order from newest to oldest. If an invocation of func returns
125
+ // false, makes no more calls.
126
+ //
127
+ // REQUIRES: user portion of internal_key == user_key.
128
+ void ForEachOverlapping(Slice user_key, Slice internal_key,
129
+ void* arg,
130
+ bool (*func)(void*, int, FileMetaData*));
131
+
117
132
  VersionSet* vset_; // VersionSet to which this Version belongs
118
133
  Version* next_; // Next version in linked list
119
134
  Version* prev_; // Previous version in linked list
@@ -14,7 +14,7 @@ namespace leveldb {
14
14
 
15
15
  // Update Makefile if you change these
16
16
  static const int kMajorVersion = 1;
17
- static const int kMinorVersion = 12;
17
+ static const int kMinorVersion = 13;
18
18
 
19
19
  struct Options;
20
20
  struct ReadOptions;
@@ -319,8 +319,39 @@ class PosixMmapFile : public WritableFile {
319
319
  return Status::OK();
320
320
  }
321
321
 
322
- virtual Status Sync() {
322
+ Status SyncDirIfManifest() {
323
+ const char* f = filename_.c_str();
324
+ const char* sep = strrchr(f, '/');
325
+ Slice basename;
326
+ std::string dir;
327
+ if (sep == NULL) {
328
+ dir = ".";
329
+ basename = f;
330
+ } else {
331
+ dir = std::string(f, sep - f);
332
+ basename = sep + 1;
333
+ }
323
334
  Status s;
335
+ if (basename.starts_with("MANIFEST")) {
336
+ int fd = open(dir.c_str(), O_RDONLY);
337
+ if (fd < 0) {
338
+ s = IOError(dir, errno);
339
+ } else {
340
+ if (fsync(fd) < 0) {
341
+ s = IOError(dir, errno);
342
+ }
343
+ close(fd);
344
+ }
345
+ }
346
+ return s;
347
+ }
348
+
349
+ virtual Status Sync() {
350
+ // Ensure new files referred to by the manifest are in the filesystem.
351
+ Status s = SyncDirIfManifest();
352
+ if (!s.ok()) {
353
+ return s;
354
+ }
324
355
 
325
356
  if (pending_sync_) {
326
357
  // Some unmapped data was not synced
@@ -16,7 +16,12 @@ class Random {
16
16
  private:
17
17
  uint32_t seed_;
18
18
  public:
19
- explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { }
19
+ explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) {
20
+ // Avoid bad seeds.
21
+ if (seed_ == 0 || seed_ == 2147483647L) {
22
+ seed_ = 1;
23
+ }
24
+ }
20
25
  uint32_t Next() {
21
26
  static const uint32_t M = 2147483647L; // 2^31-1
22
27
  static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0
data/lib/leveldb/db.rb CHANGED
@@ -11,7 +11,7 @@ module LevelDB
11
11
  class KeyError < StandardError; end
12
12
  class ClosedError < StandardError; end
13
13
 
14
- attr_reader :path
14
+ attr_reader :path, :options
15
15
  @@mutex = Mutex.new
16
16
 
17
17
  DEFAULT = {
@@ -20,7 +20,7 @@ module LevelDB
20
20
  paranoid_checks: false,
21
21
  write_buffer_size: 4 << 20,
22
22
  block_size: 4096,
23
- max_open_files: 1000,
23
+ max_open_files: 200,
24
24
  block_cache_size: 8 * (2 << 20),
25
25
  block_restart_interval: 16,
26
26
  compression: false,
@@ -29,27 +29,35 @@ module LevelDB
29
29
  }
30
30
 
31
31
  def initialize(path, options={})
32
+ new!(path, options)
33
+ end
34
+
35
+ def new!(path, options={})
32
36
  @_db_opts = C.options_create
33
37
  @_write_opts = C.writeoptions_create
34
38
  @_read_opts = C.readoptions_create
35
39
  @_read_len = C.value('size_t')
36
40
 
37
- options = DEFAULT.merge(options)
41
+ @options = DEFAULT.merge(options)
38
42
 
39
- @_cache = C.cache_create_lru(options[:block_cache_size])
43
+ @_cache = C.cache_create_lru(@options[:block_cache_size])
40
44
 
41
- C.readoptions_set_verify_checksums(@_read_opts, options[:verify_checksums] ? 1 : 0)
42
- C.readoptions_set_fill_cache(@_read_opts, options[:fill_cache] ? 1 : 0)
45
+ C.readoptions_set_verify_checksums(@_read_opts, @options[:verify_checksums] ? 1 : 0)
46
+ C.readoptions_set_fill_cache(@_read_opts, @options[:fill_cache] ? 1 : 0)
43
47
 
44
- C.options_set_create_if_missing(@_db_opts, options[:create_if_missing] ? 1 : 0)
45
- C.options_set_error_if_exists(@_db_opts, options[:error_if_exists] ? 1 : 0)
46
- C.options_set_paranoid_checks(@_db_opts, options[:paranoid_checks] ? 1 : 0)
47
- C.options_set_write_buffer_size(@_db_opts, options[:write_buffer_size])
48
- C.options_set_block_size(@_db_opts, options[:block_size])
48
+ C.options_set_create_if_missing(@_db_opts, @options[:create_if_missing] ? 1 : 0)
49
+ C.options_set_error_if_exists(@_db_opts, @options[:error_if_exists] ? 1 : 0)
50
+ C.options_set_paranoid_checks(@_db_opts, @options[:paranoid_checks] ? 1 : 0)
51
+ C.options_set_write_buffer_size(@_db_opts, @options[:write_buffer_size])
52
+ C.options_set_block_size(@_db_opts, @options[:block_size])
49
53
  C.options_set_cache(@_db_opts, @_cache)
50
- C.options_set_max_open_files(@_db_opts, options[:max_open_files])
51
- C.options_set_block_restart_interval(@_db_opts, options[:block_restart_interval])
52
- C.options_set_compression(@_db_opts, options[:compression] ? 1 : 0)
54
+ C.options_set_max_open_files(@_db_opts, @options[:max_open_files])
55
+ C.options_set_block_restart_interval(@_db_opts, @options[:block_restart_interval])
56
+ C.options_set_compression(@_db_opts, @options[:compression] ? 1 : 0)
57
+
58
+ if @options[:bloom_filter_bits_per_key]
59
+ C.options_set_filter_policy(@_db_opts, C.filterpolicy_create_bloom(@options[:bloom_filter_bits_per_key]))
60
+ end
53
61
 
54
62
  @_db_opts.free = @_write_opts.free = @_read_opts.free = C[:options_destroy]
55
63
 
@@ -63,6 +71,14 @@ module LevelDB
63
71
 
64
72
  raise Error, error_message if errors?
65
73
  end
74
+ private :new!
75
+
76
+ def reopen
77
+ close unless closed?
78
+ @@mutex.synchronize { @_closed = false }
79
+ new!(@path, @options)
80
+ end
81
+ alias reopen! reopen
66
82
 
67
83
  def []=(key, val)
68
84
  raise ClosedError if closed?
@@ -81,12 +97,13 @@ module LevelDB
81
97
  def [](key)
82
98
  raise ClosedError if closed?
83
99
 
84
- key = key.to_s
85
- val = C.get(@_db, @_read_opts, key, key.size, @_read_len, @_err)
100
+ key = key.to_s
101
+ val = C.get(@_db, @_read_opts, key, key.size, @_read_len, @_err)
102
+ val.free = C[:free]
86
103
 
87
104
  raise Error, error_message if errors?
88
105
 
89
- @_read_len.value == 0 ? nil : val.to_s(@_read_len.value)
106
+ @_read_len.value == 0 ? nil : val.to_s(@_read_len.value).clone
90
107
  end
91
108
  alias get []
92
109
 
@@ -189,6 +206,11 @@ module LevelDB
189
206
  true
190
207
  end
191
208
 
209
+ def destroy!
210
+ close && destroy && reopen
211
+ end
212
+ alias clear! destroy!
213
+
192
214
  def read_property(name)
193
215
  raise ClosedError if closed?
194
216
 
@@ -1,3 +1,3 @@
1
1
  module LevelDB
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leveldb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - DAddYE
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-25 00:00:00.000000000 Z
11
+ date: 2013-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fiddler-rb
@@ -90,6 +90,7 @@ extra_rdoc_files: []
90
90
  files:
91
91
  - ext/Rakefile
92
92
  - ext/leveldb/db/c_test.c
93
+ - ext/leveldb/db/autocompact_test.cc
93
94
  - ext/leveldb/db/builder.cc
94
95
  - ext/leveldb/db/c.cc
95
96
  - ext/leveldb/db/corruption_test.cc
@@ -244,7 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
244
245
  version: '0'
245
246
  requirements: []
246
247
  rubyforge_project:
247
- rubygems_version: 2.0.3
248
+ rubygems_version: 2.1.2
248
249
  signing_key:
249
250
  specification_version: 4
250
251
  summary: LevelDB for Ruby