@nxtedition/rocksdb 5.2.34 → 5.2.35

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.
package/binding.cc CHANGED
@@ -30,6 +30,23 @@ class NullLogger : public rocksdb::Logger {
30
30
  struct Database;
31
31
  struct Iterator;
32
32
 
33
+ #define NAPI_STATUS_RETURN(call) \
34
+ { \
35
+ const auto status = (call); \
36
+ if (status != napi_ok) { \
37
+ return status; \
38
+ } \
39
+ }
40
+
41
+ #define NAPI_PENDING_EXCEPTION() \
42
+ { \
43
+ bool result; \
44
+ NAPI_STATUS_THROWS(napi_is_exception_pending(env, &result)); \
45
+ if (result) { \
46
+ return nullptr; \
47
+ } \
48
+ }
49
+
33
50
  #define NAPI_DB_CONTEXT() \
34
51
  Database* database = nullptr; \
35
52
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
@@ -60,21 +77,15 @@ static bool IsObject(napi_env env, napi_value value) {
60
77
  return type == napi_object;
61
78
  }
62
79
 
63
- static napi_value CreateError(napi_env env, const std::string_view& str) {
64
- napi_value msg;
65
- napi_create_string_utf8(env, str.data(), str.size(), &msg);
66
- napi_value error;
67
- napi_create_error(env, nullptr, msg, &error);
68
- return error;
69
- }
70
-
71
- static napi_value CreateCodeError(napi_env env, const std::string_view& code, const std::string_view& msg) {
72
- napi_value codeValue;
73
- napi_create_string_utf8(env, code.data(), code.size(), &codeValue);
80
+ static napi_value CreateError(napi_env env, const std::optional<std::string_view>& code, const std::string_view& msg) {
81
+ napi_value codeValue = nullptr;
82
+ if (code) {
83
+ NAPI_STATUS_THROWS(napi_create_string_utf8(env, code->data(), code->size(), &codeValue));
84
+ }
74
85
  napi_value msgValue;
75
- napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue);
86
+ NAPI_STATUS_THROWS(napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue));
76
87
  napi_value error;
77
- napi_create_error(env, codeValue, msgValue, &error);
88
+ NAPI_STATUS_THROWS(napi_create_error(env, codeValue, msgValue, &error));
78
89
  return error;
79
90
  }
80
91
 
@@ -177,18 +188,10 @@ static std::optional<std::string> RangeOption(napi_env env, napi_value opts, con
177
188
  }
178
189
 
179
190
  static napi_status CallFunction(napi_env env, napi_value callback, const int argc, napi_value* argv) {
180
- napi_status status;
181
191
  napi_value global;
182
192
 
183
- status = napi_get_global(env, &global);
184
- if (status != napi_ok) {
185
- return status;
186
- }
187
-
188
- status = napi_call_function(env, global, callback, argc, argv, nullptr);
189
- if (status != napi_ok) {
190
- return status;
191
- }
193
+ NAPI_STATUS_RETURN(napi_get_global(env, &global));
194
+ NAPI_STATUS_RETURN(napi_call_function(env, global, callback, argc, argv, nullptr));
192
195
 
193
196
  return napi_ok;
194
197
  }
@@ -201,28 +204,35 @@ static napi_value ToError(napi_env env, const rocksdb::Status& status) {
201
204
  const auto msg = status.ToString();
202
205
 
203
206
  if (status.IsNotFound()) {
204
- return CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
207
+ return CreateError(env, "LEVEL_NOT_FOUND", msg);
205
208
  } else if (status.IsCorruption()) {
206
- return CreateCodeError(env, "LEVEL_CORRUPTION", msg);
209
+ return CreateError(env, "LEVEL_CORRUPTION", msg);
207
210
  } else if (status.IsIOError()) {
208
211
  if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
209
- return CreateCodeError(env, "LEVEL_LOCKED", msg);
212
+ return CreateError(env, "LEVEL_LOCKED", msg);
210
213
  } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
211
- return CreateCodeError(env, "LEVEL_LOCKED", msg);
214
+ return CreateError(env, "LEVEL_LOCKED", msg);
212
215
  } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
213
- return CreateCodeError(env, "LEVEL_LOCKED", msg);
216
+ return CreateError(env, "LEVEL_LOCKED", msg);
214
217
  } else {
215
- return CreateCodeError(env, "LEVEL_IO_ERROR", msg);
218
+ return CreateError(env, "LEVEL_IO_ERROR", msg);
216
219
  }
217
220
  }
218
221
 
219
- return CreateError(env, msg);
222
+ return CreateError(env, {}, msg);
220
223
  }
221
224
 
222
225
  template <typename T>
223
- napi_status Convert(napi_env env, const T& s, bool asBuffer, napi_value& result) {
226
+ static void Finalize(napi_env env, void* data, void* hint) {
227
+ if (hint) {
228
+ delete reinterpret_cast<T*>(hint);
229
+ }
230
+ }
231
+
232
+ napi_status Convert(napi_env env, std::string s, bool asBuffer, napi_value& result) {
224
233
  if (asBuffer) {
225
- return napi_create_buffer_copy(env, s.size(), s.data(), nullptr, &result);
234
+ auto ptr = new std::string(std::move(s));
235
+ return napi_create_external_buffer(env, ptr->size(), ptr->data(), Finalize<std::string>, ptr, &result);
226
236
  } else {
227
237
  return napi_create_string_utf8(env, s.data(), s.size(), &result);
228
238
  }
@@ -231,7 +241,7 @@ napi_status Convert(napi_env env, const T& s, bool asBuffer, napi_value& result)
231
241
  struct NapiSlice : public rocksdb::Slice {
232
242
  NapiSlice(napi_env env, napi_value from) {
233
243
  if (IsString(env, from)) {
234
- napi_get_value_string_utf8(env, from, nullptr, 0, &size_);
244
+ NAPI_STATUS_THROWS_VOID(napi_get_value_string_utf8(env, from, nullptr, 0, &size_));
235
245
  char* data;
236
246
  if (size_ + 1 < stack_.size()) {
237
247
  data = stack_.data();
@@ -240,17 +250,17 @@ struct NapiSlice : public rocksdb::Slice {
240
250
  data = heap_.get();
241
251
  }
242
252
  data[size_] = 0;
243
- napi_get_value_string_utf8(env, from, data, size_ + 1, &size_);
253
+ NAPI_STATUS_THROWS_VOID(napi_get_value_string_utf8(env, from, data, size_ + 1, &size_));
244
254
  data_ = data;
245
255
  } else if (IsBuffer(env, from)) {
246
256
  void* data;
247
- napi_get_buffer_info(env, from, &data, &size_);
257
+ NAPI_STATUS_THROWS_VOID(napi_get_buffer_info(env, from, &data, &size_));
248
258
  data_ = static_cast<char*>(data);
249
259
  }
250
260
  }
251
261
 
252
262
  std::unique_ptr<char[]> heap_;
253
- std::array<char, 8192> stack_;
263
+ std::array<char, 1024> stack_;
254
264
  };
255
265
 
256
266
  /**
@@ -263,13 +273,12 @@ struct NapiSlice : public rocksdb::Slice {
263
273
  * - Destroy (main thread): do cleanup regardless of success
264
274
  */
265
275
  struct Worker {
266
- Worker(napi_env env, Database* database, napi_value callback, const std::string& resourceName)
267
- : database_(database) {
276
+ Worker(napi_env env, Database* database, napi_value callback, const std::string& resourceName) : database_(database) {
268
277
  NAPI_STATUS_THROWS_VOID(napi_create_reference(env, callback, 1, &callbackRef_));
269
278
  napi_value asyncResourceName;
270
279
  NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName.data(), resourceName.size(), &asyncResourceName));
271
- NAPI_STATUS_THROWS_VOID(napi_create_async_work(env, callback, asyncResourceName, Worker::Execute,
272
- Worker::Complete, this, &asyncWork_));
280
+ NAPI_STATUS_THROWS_VOID(
281
+ napi_create_async_work(env, callback, asyncResourceName, Worker::Execute, Worker::Complete, this, &asyncWork_));
273
282
  }
274
283
 
275
284
  virtual ~Worker() {}
@@ -282,6 +291,8 @@ struct Worker {
282
291
  static void Complete(napi_env env, napi_status status, void* data) {
283
292
  auto self = reinterpret_cast<Worker*>(data);
284
293
 
294
+ // TODO (fix): napi status handling...
295
+
285
296
  napi_value callback;
286
297
  napi_get_reference_value(env, self->callbackRef_, &callback);
287
298
 
@@ -301,13 +312,15 @@ struct Worker {
301
312
 
302
313
  virtual rocksdb::Status Execute(Database& database) = 0;
303
314
 
304
- virtual void OnOk(napi_env env, napi_value callback) {
315
+ virtual napi_status OnOk(napi_env env, napi_value callback) {
305
316
  napi_value argv;
306
- napi_get_null(env, &argv);
307
- CallFunction(env, callback, 1, &argv);
317
+ NAPI_STATUS_RETURN(napi_get_null(env, &argv));
318
+ return CallFunction(env, callback, 1, &argv);
308
319
  }
309
320
 
310
- virtual void OnError(napi_env env, napi_value callback, napi_value err) { CallFunction(env, callback, 1, &err); }
321
+ virtual napi_status OnError(napi_env env, napi_value callback, napi_value err) {
322
+ return CallFunction(env, callback, 1, &err);
323
+ }
311
324
 
312
325
  virtual void Destroy(napi_env env) {}
313
326
 
@@ -322,20 +335,6 @@ struct Worker {
322
335
  };
323
336
 
324
337
  struct Database {
325
- rocksdb::Status Open(const rocksdb::Options& options, const bool readOnly, const char* location) {
326
- if (readOnly) {
327
- rocksdb::DB* db = nullptr;
328
- const auto status = rocksdb::DB::OpenForReadOnly(options, location, &db);
329
- db_.reset(db);
330
- return status;
331
- } else {
332
- rocksdb::DB* db = nullptr;
333
- const auto status = rocksdb::DB::Open(options, location, &db);
334
- db_.reset(db);
335
- return status;
336
- }
337
- }
338
-
339
338
  void AttachIterator(napi_env env, Iterator* iterator) {
340
339
  iterators_.insert(iterator);
341
340
  IncrementPriorityWork(env);
@@ -346,10 +345,10 @@ struct Database {
346
345
  DecrementPriorityWork(env);
347
346
  }
348
347
 
349
- void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, prioritRef_, &priorityWork_); }
348
+ void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, priorityRef_, &priorityWork_); }
350
349
 
351
350
  void DecrementPriorityWork(napi_env env) {
352
- napi_reference_unref(env, prioritRef_, &priorityWork_);
351
+ napi_reference_unref(env, priorityRef_, &priorityWork_);
353
352
 
354
353
  if (priorityWork_ == 0 && pendingCloseWorker_) {
355
354
  pendingCloseWorker_->Queue(env);
@@ -362,7 +361,7 @@ struct Database {
362
361
  std::unique_ptr<rocksdb::DB> db_;
363
362
  Worker* pendingCloseWorker_;
364
363
  std::set<Iterator*> iterators_;
365
- napi_ref prioritRef_;
364
+ napi_ref priorityRef_;
366
365
 
367
366
  private:
368
367
  uint32_t priorityWork_ = 0;
@@ -378,33 +377,40 @@ struct BaseIterator {
378
377
  const int limit,
379
378
  const bool fillCache)
380
379
  : database_(database),
381
- lt_(!lte ? lt : *lte + '\0'),
382
- gte_(gte ? gte : (gt ? std::optional<std::string>(*gt + '\0') : std::nullopt)),
383
380
  snapshot_(database_->db_->GetSnapshot(),
384
381
  [this](const rocksdb::Snapshot* ptr) { database_->db_->ReleaseSnapshot(ptr); }),
385
- iterator_(database->db_->NewIterator([&] {
386
- rocksdb::ReadOptions options;
387
- if (lt_) {
388
- upper_bound_ = rocksdb::Slice(lt_->data(), lt_->size());
389
- options.iterate_upper_bound = &upper_bound_;
390
- }
391
- if (gte_) {
392
- lower_bound_ = rocksdb::Slice(gte_->data(), gte_->size());
393
- options.iterate_lower_bound = &lower_bound_;
394
- }
395
- options.fill_cache = fillCache;
396
- options.snapshot = snapshot_.get();
397
- return options;
398
- }())),
399
382
  reverse_(reverse),
400
- limit_(limit) {}
383
+ limit_(limit),
384
+ fillCache_(fillCache) {
385
+ if (lte) {
386
+ upper_bound_ = std::make_unique<rocksdb::PinnableSlice>();
387
+ *upper_bound_->GetSelf() = std::move(*lte) + '\0';
388
+ upper_bound_->PinSelf();
389
+ } else if (lt) {
390
+ upper_bound_ = std::make_unique<rocksdb::PinnableSlice>();
391
+ *upper_bound_->GetSelf() = std::move(*lt);
392
+ upper_bound_->PinSelf();
393
+ }
394
+
395
+ if (gte) {
396
+ lower_bound_ = std::make_unique<rocksdb::PinnableSlice>();
397
+ *lower_bound_->GetSelf() = std::move(*gte);
398
+ lower_bound_->PinSelf();
399
+ } else if (gt) {
400
+ lower_bound_ = std::make_unique<rocksdb::PinnableSlice>();
401
+ *lower_bound_->GetSelf() = std::move(*gt) + '\0';
402
+ lower_bound_->PinSelf();
403
+ }
404
+ }
401
405
 
402
406
  virtual ~BaseIterator() { assert(!iterator_); }
403
407
 
404
- bool DidSeek() const { return didSeek_; }
408
+ bool DidSeek() const { return iterator_ != nullptr; }
405
409
 
406
410
  void SeekToRange() {
407
- didSeek_ = true;
411
+ if (!iterator_) {
412
+ Init();
413
+ }
408
414
 
409
415
  if (reverse_) {
410
416
  iterator_->SeekToLast();
@@ -413,10 +419,12 @@ struct BaseIterator {
413
419
  }
414
420
  }
415
421
 
416
- void Seek(const std::string& target) {
417
- didSeek_ = true;
422
+ void Seek(const rocksdb::Slice& target) {
423
+ if (!iterator_) {
424
+ Init();
425
+ }
418
426
 
419
- if ((lt_ && target.compare(*lt_) >= 0) || (gte_ && target.compare(*gte_) < 0)) {
427
+ if ((upper_bound_ && target.compare(*upper_bound_) >= 0) || (lower_bound_ && target.compare(*lower_bound_) < 0)) {
420
428
  // TODO (fix): Why is this required? Seek should handle it?
421
429
  // https://github.com/facebook/rocksdb/issues/9904
422
430
  iterator_->SeekToLast();
@@ -455,16 +463,28 @@ struct BaseIterator {
455
463
  Database* database_;
456
464
 
457
465
  private:
458
- const std::optional<std::string> lt_;
459
- const std::optional<std::string> gte_;
460
- rocksdb::Slice lower_bound_;
461
- rocksdb::Slice upper_bound_;
466
+ void Init() {
467
+ rocksdb::ReadOptions options;
468
+ if (upper_bound_) {
469
+ options.iterate_upper_bound = &*upper_bound_;
470
+ }
471
+ if (lower_bound_) {
472
+ options.iterate_lower_bound = &*lower_bound_;
473
+ }
474
+ options.fill_cache = fillCache_;
475
+ options.snapshot = snapshot_.get();
476
+
477
+ iterator_.reset(database_->db_->NewIterator(options));
478
+ }
479
+
480
+ std::unique_ptr<rocksdb::PinnableSlice> lower_bound_;
481
+ std::unique_ptr<rocksdb::PinnableSlice> upper_bound_;
462
482
  std::shared_ptr<const rocksdb::Snapshot> snapshot_;
463
483
  std::unique_ptr<rocksdb::Iterator> iterator_;
464
- bool didSeek_ = false;
465
484
  const bool reverse_;
466
485
  const int limit_;
467
486
  int count_ = 0;
487
+ const bool fillCache_;
468
488
  };
469
489
 
470
490
  struct Iterator final : public BaseIterator {
@@ -543,8 +563,8 @@ static void FinalizeDatabase(napi_env env, void* data, void* hint) {
543
563
  if (data) {
544
564
  auto database = reinterpret_cast<Database*>(data);
545
565
  napi_remove_env_cleanup_hook(env, env_cleanup_hook, database);
546
- if (database->prioritRef_)
547
- napi_delete_reference(env, database->prioritRef_);
566
+ if (database->priorityRef_)
567
+ napi_delete_reference(env, database->priorityRef_);
548
568
  delete database;
549
569
  }
550
570
  }
@@ -556,7 +576,7 @@ NAPI_METHOD(db_init) {
556
576
  napi_value result;
557
577
  NAPI_STATUS_THROWS(napi_create_external(env, database, FinalizeDatabase, nullptr, &result));
558
578
 
559
- NAPI_STATUS_THROWS(napi_create_reference(env, result, 0, &database->prioritRef_));
579
+ NAPI_STATUS_THROWS(napi_create_reference(env, result, 0, &database->priorityRef_));
560
580
 
561
581
  return result;
562
582
  }
@@ -566,71 +586,22 @@ struct OpenWorker final : public Worker {
566
586
  Database* database,
567
587
  napi_value callback,
568
588
  const std::string& location,
569
- const bool createIfMissing,
570
- const bool errorIfExists,
571
- const bool compression,
572
- const uint32_t writeBufferSize,
573
- const uint32_t blockSize,
574
- const uint32_t maxOpenFiles,
575
- const uint32_t blockRestartInterval,
576
- const uint32_t maxFileSize,
577
- const uint32_t cacheSize,
578
- const std::string& infoLogLevel,
589
+ rocksdb::Options options,
579
590
  const bool readOnly)
580
- : Worker(env, database, callback, "leveldown.db.open"), readOnly_(readOnly), location_(location) {
581
- options_.create_if_missing = createIfMissing;
582
- options_.error_if_exists = errorIfExists;
583
- options_.compression = compression ? rocksdb::kSnappyCompression : rocksdb::kNoCompression;
584
- options_.write_buffer_size = writeBufferSize;
585
- options_.max_open_files = maxOpenFiles;
586
- options_.max_log_file_size = maxFileSize;
587
- options_.use_adaptive_mutex = true;
588
-
589
- if (infoLogLevel.size() > 0) {
590
- rocksdb::InfoLogLevel lvl = {};
591
-
592
- if (infoLogLevel == "debug")
593
- lvl = rocksdb::InfoLogLevel::DEBUG_LEVEL;
594
- else if (infoLogLevel == "info")
595
- lvl = rocksdb::InfoLogLevel::INFO_LEVEL;
596
- else if (infoLogLevel == "warn")
597
- lvl = rocksdb::InfoLogLevel::WARN_LEVEL;
598
- else if (infoLogLevel == "error")
599
- lvl = rocksdb::InfoLogLevel::ERROR_LEVEL;
600
- else if (infoLogLevel == "fatal")
601
- lvl = rocksdb::InfoLogLevel::FATAL_LEVEL;
602
- else if (infoLogLevel == "header")
603
- lvl = rocksdb::InfoLogLevel::HEADER_LEVEL;
604
- else
605
- napi_throw_error(env, nullptr, "invalid log level");
606
-
607
- options_.info_log_level = lvl;
608
- } else {
609
- // In some places RocksDB checks this option to see if it should prepare
610
- // debug information (ahead of logging), so set it to the highest level.
611
- options_.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
612
- options_.info_log.reset(new NullLogger());
613
- }
614
-
615
- rocksdb::BlockBasedTableOptions tableOptions;
616
-
617
- if (cacheSize) {
618
- tableOptions.block_cache = rocksdb::NewLRUCache(cacheSize);
619
- } else {
620
- tableOptions.no_block_cache = true;
621
- }
622
-
623
- tableOptions.block_size = blockSize;
624
- tableOptions.block_restart_interval = blockRestartInterval;
625
- tableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10));
626
- tableOptions.format_version = 5;
627
- tableOptions.checksum = rocksdb::kxxHash64;
628
-
629
- options_.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tableOptions));
591
+ : Worker(env, database, callback, "leveldown.db.open"),
592
+ options_(options),
593
+ readOnly_(readOnly),
594
+ location_(location) {}
595
+
596
+ rocksdb::Status Execute(Database& database) override {
597
+ rocksdb::DB* db;
598
+ const auto status = readOnly_
599
+ ? rocksdb::DB::OpenForReadOnly(options_, location_, &db)
600
+ : rocksdb::DB::Open(options_, location_, &db);
601
+ database.db_.reset(db);
602
+ return status;
630
603
  }
631
604
 
632
- rocksdb::Status Execute(Database& database) override { return database.Open(options_, readOnly_, location_.c_str()); }
633
-
634
605
  rocksdb::Options options_;
635
606
  const bool readOnly_;
636
607
  const std::string location_;
@@ -640,27 +611,71 @@ NAPI_METHOD(db_open) {
640
611
  NAPI_ARGV(4);
641
612
  NAPI_DB_CONTEXT();
642
613
 
614
+ rocksdb::Options options;
615
+
616
+ options.IncreaseParallelism(Uint32Property(env, argv[2], "parallelism", 4));
617
+
643
618
  const auto location = ToString(env, argv[1]);
644
- const auto options = argv[2];
645
- const auto createIfMissing = BooleanProperty(env, options, "createIfMissing", true);
646
- const auto errorIfExists = BooleanProperty(env, options, "errorIfExists", false);
647
- const auto compression = BooleanProperty(env, options, "compression", true);
648
- const auto readOnly = BooleanProperty(env, options, "readOnly", false);
619
+ options.create_if_missing = BooleanProperty(env, argv[2], "createIfMissing", true);
620
+ options.error_if_exists = BooleanProperty(env, argv[2], "errorIfExists", false);
621
+ options.compression =
622
+ BooleanProperty(env, argv[2], "compression", true) ? rocksdb::kSnappyCompression : rocksdb::kNoCompression;
623
+ options.max_open_files = Uint32Property(env, argv[2], "maxOpenFiles", 1000);
624
+ options.max_log_file_size = Uint32Property(env, argv[2], "maxFileSize", 2 << 20);
625
+ options.write_buffer_size = Uint32Property(env, argv[2], "writeBufferSize", 4 << 20);
626
+ options.use_adaptive_mutex = true;
627
+
628
+ const auto infoLogLevel = StringProperty(env, argv[2], "infoLogLevel");
629
+ if (infoLogLevel.size() > 0) {
630
+ rocksdb::InfoLogLevel lvl = {};
631
+
632
+ if (infoLogLevel == "debug")
633
+ lvl = rocksdb::InfoLogLevel::DEBUG_LEVEL;
634
+ else if (infoLogLevel == "info")
635
+ lvl = rocksdb::InfoLogLevel::INFO_LEVEL;
636
+ else if (infoLogLevel == "warn")
637
+ lvl = rocksdb::InfoLogLevel::WARN_LEVEL;
638
+ else if (infoLogLevel == "error")
639
+ lvl = rocksdb::InfoLogLevel::ERROR_LEVEL;
640
+ else if (infoLogLevel == "fatal")
641
+ lvl = rocksdb::InfoLogLevel::FATAL_LEVEL;
642
+ else if (infoLogLevel == "header")
643
+ lvl = rocksdb::InfoLogLevel::HEADER_LEVEL;
644
+ else
645
+ napi_throw_error(env, nullptr, "invalid log level");
646
+
647
+ options.info_log_level = lvl;
648
+ } else {
649
+ // In some places RocksDB checks this option to see if it should prepare
650
+ // debug information (ahead of logging), so set it to the highest level.
651
+ options.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
652
+ options.info_log.reset(new NullLogger());
653
+ }
654
+
655
+ const auto readOnly = BooleanProperty(env, argv[2], "readOnly", false);
656
+ const auto cacheSize = Uint32Property(env, argv[2], "cacheSize", 8 << 20);
657
+
658
+ rocksdb::BlockBasedTableOptions tableOptions;
659
+
660
+ if (cacheSize) {
661
+ tableOptions.block_cache = rocksdb::NewLRUCache(cacheSize);
662
+ } else {
663
+ tableOptions.no_block_cache = true;
664
+ }
649
665
 
650
- const auto infoLogLevel = StringProperty(env, options, "infoLogLevel");
666
+ tableOptions.block_size = Uint32Property(env, argv[2], "blockSize", 4096);
667
+ tableOptions.block_restart_interval = Uint32Property(env, argv[2], "blockRestartInterval", 16);
668
+ tableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10));
669
+ tableOptions.format_version = 5;
670
+ tableOptions.checksum = rocksdb::kxxHash64;
651
671
 
652
- const auto cacheSize = Uint32Property(env, options, "cacheSize", 8 << 20);
653
- const auto writeBufferSize = Uint32Property(env, options, "writeBufferSize", 4 << 20);
654
- const auto blockSize = Uint32Property(env, options, "blockSize", 4096);
655
- const auto maxOpenFiles = Uint32Property(env, options, "maxOpenFiles", 1000);
656
- const auto blockRestartInterval = Uint32Property(env, options, "blockRestartInterval", 16);
657
- const auto maxFileSize = Uint32Property(env, options, "maxFileSize", 2 << 20);
672
+ options.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tableOptions));
658
673
 
659
674
  const auto callback = argv[3];
660
675
 
661
- auto worker =
662
- new OpenWorker(env, database, callback, location, createIfMissing, errorIfExists, compression, writeBufferSize,
663
- blockSize, maxOpenFiles, blockRestartInterval, maxFileSize, cacheSize, infoLogLevel, readOnly);
676
+ NAPI_PENDING_EXCEPTION();
677
+
678
+ auto worker = new OpenWorker(env, database, callback, location, options, readOnly);
664
679
  worker->Queue(env);
665
680
 
666
681
  return 0;
@@ -698,11 +713,13 @@ NAPI_METHOD(db_put) {
698
713
  NAPI_ARGV(4);
699
714
  NAPI_DB_CONTEXT();
700
715
 
701
- const auto key = ToString(env, argv[1]);
702
- const auto value = ToString(env, argv[2]);
716
+ const auto key = NapiSlice(env, argv[1]);
717
+ const auto val = NapiSlice(env, argv[2]);
718
+
719
+ NAPI_PENDING_EXCEPTION();
703
720
 
704
721
  rocksdb::WriteOptions options;
705
- return ToError(env, database->db_->Put(options, key, value));
722
+ return ToError(env, database->db_->Put(options, key, val));
706
723
  }
707
724
 
708
725
  struct GetWorker final : public Worker {
@@ -726,17 +743,19 @@ struct GetWorker final : public Worker {
726
743
  options.fill_cache = fillCache_;
727
744
  options.snapshot = snapshot_.get();
728
745
 
729
- auto status = database.db_->Get(options, database.db_->DefaultColumnFamily(), key_, &value_);
746
+ auto status = database.db_->Get(options, key_, &value_);
747
+
748
+ key_.clear();
730
749
  snapshot_ = nullptr;
731
750
 
732
751
  return status;
733
752
  }
734
753
 
735
- void OnOk(napi_env env, napi_value callback) override {
754
+ napi_status OnOk(napi_env env, napi_value callback) override {
736
755
  napi_value argv[2];
737
- napi_get_null(env, &argv[0]);
738
- Convert(env, std::move(value_), asBuffer_, argv[1]);
739
- CallFunction(env, callback, 2, argv);
756
+ NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
757
+ NAPI_STATUS_RETURN(Convert(env, std::move(value_), asBuffer_, argv[1]));
758
+ return CallFunction(env, callback, 2, argv);
740
759
  }
741
760
 
742
761
  void Destroy(napi_env env) override {
@@ -745,8 +764,8 @@ struct GetWorker final : public Worker {
745
764
  }
746
765
 
747
766
  private:
748
- const std::string key_;
749
- rocksdb::PinnableSlice value_;
767
+ std::string key_;
768
+ std::string value_;
750
769
  const bool asBuffer_;
751
770
  const bool fillCache_;
752
771
  std::shared_ptr<const rocksdb::Snapshot> snapshot_;
@@ -762,6 +781,8 @@ NAPI_METHOD(db_get) {
762
781
  const auto fillCache = BooleanProperty(env, options, "fillCache", true);
763
782
  const auto callback = argv[3];
764
783
 
784
+ NAPI_PENDING_EXCEPTION();
785
+
765
786
  auto worker = new GetWorker(env, database, callback, key, asBuffer, fillCache);
766
787
  worker->Queue(env);
767
788
 
@@ -790,6 +811,8 @@ struct GetManyWorker final : public Worker {
790
811
  options.snapshot = snapshot_.get();
791
812
 
792
813
  status_ = database.db_->MultiGet(options, std::vector<rocksdb::Slice>(keys_.begin(), keys_.end()), &values_);
814
+
815
+ keys_.clear();
793
816
  snapshot_ = nullptr;
794
817
 
795
818
  for (auto status : status_) {
@@ -801,26 +824,29 @@ struct GetManyWorker final : public Worker {
801
824
  return rocksdb::Status::OK();
802
825
  }
803
826
 
804
- void OnOk(napi_env env, napi_value callback) override {
827
+ napi_status OnOk(napi_env env, napi_value callback) override {
805
828
  const auto size = values_.size();
806
829
 
807
830
  napi_value array;
808
- napi_create_array_with_length(env, size, &array);
831
+ NAPI_STATUS_RETURN(napi_create_array_with_length(env, size, &array));
809
832
 
810
833
  for (size_t idx = 0; idx < size; idx++) {
811
834
  napi_value element;
812
835
  if (status_[idx].ok()) {
813
- Convert(env, values_[idx], valueAsBuffer_, element);
836
+ NAPI_STATUS_RETURN(Convert(env, std::move(values_[idx]), valueAsBuffer_, element));
814
837
  } else {
815
- napi_get_undefined(env, &element);
838
+ NAPI_STATUS_RETURN(napi_get_undefined(env, &element));
816
839
  }
817
- napi_set_element(env, array, static_cast<uint32_t>(idx), element);
840
+ NAPI_STATUS_RETURN(napi_set_element(env, array, static_cast<uint32_t>(idx), element));
818
841
  }
819
842
 
843
+ values_.clear();
844
+ status_.clear();
845
+
820
846
  napi_value argv[2];
821
- napi_get_null(env, &argv[0]);
847
+ NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
822
848
  argv[1] = array;
823
- CallFunction(env, callback, 2, argv);
849
+ return CallFunction(env, callback, 2, argv);
824
850
  }
825
851
 
826
852
  void Destroy(napi_env env) override {
@@ -829,7 +855,7 @@ struct GetManyWorker final : public Worker {
829
855
  }
830
856
 
831
857
  private:
832
- const std::vector<std::string> keys_;
858
+ std::vector<std::string> keys_;
833
859
  std::vector<std::string> values_;
834
860
  std::vector<rocksdb::Status> status_;
835
861
  const bool valueAsBuffer_;
@@ -861,6 +887,8 @@ NAPI_METHOD(db_get_many) {
861
887
  const bool fillCache = BooleanProperty(env, options, "fillCache", true);
862
888
  const auto callback = argv[3];
863
889
 
890
+ NAPI_PENDING_EXCEPTION();
891
+
864
892
  auto worker = new GetManyWorker(env, database, keys, callback, asBuffer, fillCache);
865
893
  worker->Queue(env);
866
894
 
@@ -871,7 +899,9 @@ NAPI_METHOD(db_del) {
871
899
  NAPI_ARGV(3);
872
900
  NAPI_DB_CONTEXT();
873
901
 
874
- const auto key = ToString(env, argv[1]);
902
+ const auto key = NapiSlice(env, argv[1]);
903
+
904
+ NAPI_PENDING_EXCEPTION();
875
905
 
876
906
  rocksdb::WriteOptions options;
877
907
  return ToError(env, database->db_->Delete(options, key));
@@ -889,6 +919,8 @@ NAPI_METHOD(db_clear) {
889
919
  const auto gt = RangeOption(env, argv[1], "gt");
890
920
  const auto gte = RangeOption(env, argv[1], "gte");
891
921
 
922
+ NAPI_PENDING_EXCEPTION();
923
+
892
924
  // TODO (perf): Use DeleteRange.
893
925
 
894
926
  BaseIterator it(database, reverse, lt, lte, gt, gte, limit, false);
@@ -934,23 +966,19 @@ NAPI_METHOD(db_get_property) {
934
966
  NAPI_ARGV(2);
935
967
  NAPI_DB_CONTEXT();
936
968
 
937
- const auto property = ToString(env, argv[1]);
969
+ const auto property = NapiSlice(env, argv[1]);
970
+
971
+ NAPI_PENDING_EXCEPTION();
938
972
 
939
973
  std::string value;
940
974
  database->db_->GetProperty(property, &value);
941
975
 
942
976
  napi_value result;
943
- napi_create_string_utf8(env, value.data(), value.size(), &result);
977
+ NAPI_STATUS_THROWS(napi_create_string_utf8(env, value.data(), value.size(), &result));
944
978
 
945
979
  return result;
946
980
  }
947
981
 
948
- static void FinalizeIterator(napi_env env, void* data, void* hint) {
949
- if (data) {
950
- delete reinterpret_cast<Iterator*>(data);
951
- }
952
- }
953
-
954
982
  NAPI_METHOD(iterator_init) {
955
983
  NAPI_ARGV(2);
956
984
  NAPI_DB_CONTEXT();
@@ -970,15 +998,17 @@ NAPI_METHOD(iterator_init) {
970
998
  const auto gt = RangeOption(env, options, "gt");
971
999
  const auto gte = RangeOption(env, options, "gte");
972
1000
 
973
- auto iterator = new Iterator(database, reverse, keys, values, limit, lt, lte, gt, gte, fillCache, keyAsBuffer,
1001
+ NAPI_PENDING_EXCEPTION();
1002
+
1003
+ auto iterator = std::make_unique<Iterator>(database, reverse, keys, values, limit, lt, lte, gt, gte, fillCache, keyAsBuffer,
974
1004
  valueAsBuffer, highWaterMarkBytes);
975
- napi_value result;
976
1005
 
977
- NAPI_STATUS_THROWS(napi_create_external(env, iterator, FinalizeIterator, nullptr, &result));
1006
+ napi_value result;
1007
+ NAPI_STATUS_THROWS(napi_create_external(env, iterator.get(), Finalize<Iterator>, iterator.get(), &result));
978
1008
 
979
1009
  // Prevent GC of JS object before the iterator is closed (explicitly or on
980
1010
  // db close) and keep track of non-closed iterators to end them on db close.
981
- iterator->Attach(env, result);
1011
+ iterator.release()->Attach(env, result);
982
1012
 
983
1013
  return result;
984
1014
  }
@@ -987,39 +1017,22 @@ NAPI_METHOD(iterator_seek) {
987
1017
  NAPI_ARGV(2);
988
1018
  NAPI_ITERATOR_CONTEXT();
989
1019
 
990
- const auto target = ToString(env, argv[1]);
1020
+ const auto target = NapiSlice(env, argv[1]);
1021
+
1022
+ NAPI_PENDING_EXCEPTION();
1023
+
991
1024
  iterator->first_ = true;
992
1025
  iterator->Seek(target);
993
1026
 
994
1027
  return 0;
995
1028
  }
996
1029
 
997
- struct CloseIteratorWorker final : public Worker {
998
- CloseIteratorWorker(napi_env env, Iterator* iterator, napi_value callback)
999
- : Worker(env, iterator->database_, callback, "leveldown.iterator.end"), iterator_(iterator) {}
1000
-
1001
- rocksdb::Status Execute(Database& database) override {
1002
- iterator_->Close();
1003
- return rocksdb::Status::OK();
1004
- }
1005
-
1006
- void Destroy(napi_env env) override {
1007
- iterator_->Detach(env);
1008
- Worker::Destroy(env);
1009
- }
1010
-
1011
- private:
1012
- Iterator* iterator_;
1013
- };
1014
-
1015
1030
  NAPI_METHOD(iterator_close) {
1016
- NAPI_ARGV(2);
1031
+ NAPI_ARGV(1);
1017
1032
  NAPI_ITERATOR_CONTEXT();
1018
1033
 
1019
- const auto callback = argv[1];
1020
-
1021
- auto worker = new CloseIteratorWorker(env, iterator, callback);
1022
- worker->Queue(env);
1034
+ iterator->Detach(env);
1035
+ iterator->Close();
1023
1036
 
1024
1037
  return 0;
1025
1038
  }
@@ -1033,9 +1046,6 @@ struct NextWorker final : public Worker {
1033
1046
  iterator_->SeekToRange();
1034
1047
  }
1035
1048
 
1036
- // Limit the size of the cache to prevent starving the event loop
1037
- // in JS-land while we're recursively calling process.nextTick().
1038
-
1039
1049
  cache_.reserve(size_ * 2);
1040
1050
  size_t bytesRead = 0;
1041
1051
 
@@ -1075,29 +1085,29 @@ struct NextWorker final : public Worker {
1075
1085
  return iterator_->Status();
1076
1086
  }
1077
1087
 
1078
- void OnOk(napi_env env, napi_value callback) override {
1088
+ napi_status OnOk(napi_env env, napi_value callback) override {
1079
1089
  const auto size = cache_.size();
1080
1090
  napi_value result;
1081
- napi_create_array_with_length(env, size, &result);
1091
+ NAPI_STATUS_RETURN(napi_create_array_with_length(env, size, &result));
1082
1092
 
1083
1093
  for (size_t idx = 0; idx < cache_.size(); idx += 2) {
1084
1094
  napi_value key;
1085
1095
  napi_value val;
1086
1096
 
1087
- Convert(env, cache_[idx + 0], iterator_->keyAsBuffer_, key);
1088
- Convert(env, cache_[idx + 1], iterator_->valueAsBuffer_, val);
1097
+ NAPI_STATUS_RETURN(Convert(env, std::move(cache_[idx + 0]), iterator_->keyAsBuffer_, key));
1098
+ NAPI_STATUS_RETURN(Convert(env, std::move(cache_[idx + 1]), iterator_->valueAsBuffer_, val));
1089
1099
 
1090
- napi_set_element(env, result, static_cast<int>(idx + 0), key);
1091
- napi_set_element(env, result, static_cast<int>(idx + 1), val);
1100
+ NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(idx + 0), key));
1101
+ NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(idx + 1), val));
1092
1102
  }
1093
1103
 
1094
1104
  cache_.clear();
1095
1105
 
1096
1106
  napi_value argv[3];
1097
- napi_get_null(env, &argv[0]);
1107
+ NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1098
1108
  argv[1] = result;
1099
- napi_get_boolean(env, !finished_, &argv[2]);
1100
- CallFunction(env, callback, 3, argv);
1109
+ NAPI_STATUS_RETURN(napi_get_boolean(env, !finished_, &argv[2]));
1110
+ return CallFunction(env, callback, 3, argv);
1101
1111
  }
1102
1112
 
1103
1113
  private:
@@ -1113,8 +1123,6 @@ NAPI_METHOD(iterator_nextv) {
1113
1123
 
1114
1124
  uint32_t size;
1115
1125
  NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &size));
1116
- if (size == 0)
1117
- size = 1;
1118
1126
 
1119
1127
  const auto callback = argv[2];
1120
1128
 
@@ -1164,16 +1172,12 @@ NAPI_METHOD(batch_do) {
1164
1172
  }
1165
1173
  }
1166
1174
 
1175
+ NAPI_PENDING_EXCEPTION();
1176
+
1167
1177
  rocksdb::WriteOptions options;
1168
1178
  return ToError(env, database->db_->Write(options, &batch));
1169
1179
  }
1170
1180
 
1171
- static void FinalizeBatch(napi_env env, void* data, void* hint) {
1172
- if (data) {
1173
- delete reinterpret_cast<rocksdb::WriteBatch*>(data);
1174
- }
1175
- }
1176
-
1177
1181
  NAPI_METHOD(batch_init) {
1178
1182
  NAPI_ARGV(1);
1179
1183
  NAPI_DB_CONTEXT();
@@ -1181,7 +1185,7 @@ NAPI_METHOD(batch_init) {
1181
1185
  auto batch = new rocksdb::WriteBatch();
1182
1186
 
1183
1187
  napi_value result;
1184
- NAPI_STATUS_THROWS(napi_create_external(env, batch, FinalizeBatch, nullptr, &result));
1188
+ NAPI_STATUS_THROWS(napi_create_external(env, batch, Finalize<rocksdb::WriteBatch>, batch, &result));
1185
1189
  return result;
1186
1190
  }
1187
1191
 
@@ -1190,9 +1194,11 @@ NAPI_METHOD(batch_put) {
1190
1194
  NAPI_BATCH_CONTEXT();
1191
1195
 
1192
1196
  const auto key = NapiSlice(env, argv[1]);
1193
- const auto value = NapiSlice(env, argv[2]);
1197
+ const auto val = NapiSlice(env, argv[2]);
1198
+
1199
+ NAPI_PENDING_EXCEPTION();
1194
1200
 
1195
- batch->Put(key, value);
1201
+ batch->Put(key, val);
1196
1202
 
1197
1203
  return 0;
1198
1204
  }
@@ -1203,6 +1209,8 @@ NAPI_METHOD(batch_del) {
1203
1209
 
1204
1210
  const auto key = NapiSlice(env, argv[1]);
1205
1211
 
1212
+ NAPI_PENDING_EXCEPTION();
1213
+
1206
1214
  batch->Delete(key);
1207
1215
 
1208
1216
  return 0;
package/iterator.js CHANGED
@@ -103,7 +103,7 @@ class Iterator extends AbstractIterator {
103
103
  this[kCache] = empty
104
104
  this[kCallback] = null
105
105
 
106
- binding.iterator_close(this[kContext], callback)
106
+ process.nextTick(callback, binding.iterator_close(this[kContext]))
107
107
  }
108
108
 
109
109
  _end (callback) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/rocksdb",
3
- "version": "5.2.34",
3
+ "version": "5.2.35",
4
4
  "description": "A low-level Node.js RocksDB binding",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
Binary file
Binary file