@nxtedition/rocksdb 6.0.3 → 7.0.0-alpha.3
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 +576 -155
- package/chained-batch.js +6 -6
- package/index.js +132 -5
- package/iterator.js +1 -0
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.node +0 -0
package/binding.cc
CHANGED
|
@@ -30,6 +30,7 @@ class NullLogger : public rocksdb::Logger {
|
|
|
30
30
|
|
|
31
31
|
struct Database;
|
|
32
32
|
struct Iterator;
|
|
33
|
+
struct Updates;
|
|
33
34
|
|
|
34
35
|
#define NAPI_STATUS_RETURN(call) \
|
|
35
36
|
{ \
|
|
@@ -39,17 +40,14 @@ struct Iterator;
|
|
|
39
40
|
} \
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
#define
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
#define NAPI_BATCH_CONTEXT() \
|
|
51
|
-
rocksdb::WriteBatch* batch = nullptr; \
|
|
52
|
-
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&batch));
|
|
43
|
+
#define ROCKS_STATUS_THROWS(call) \
|
|
44
|
+
{ \
|
|
45
|
+
const auto status = (call); \
|
|
46
|
+
if (!status.ok()) { \
|
|
47
|
+
napi_throw(env, ToError(env, status)); \
|
|
48
|
+
return NULL; \
|
|
49
|
+
} \
|
|
50
|
+
}
|
|
53
51
|
|
|
54
52
|
static bool IsString(napi_env env, napi_value value) {
|
|
55
53
|
napi_valuetype type;
|
|
@@ -111,6 +109,20 @@ static bool EncodingIsBuffer(napi_env env, napi_value obj, const std::string_vie
|
|
|
111
109
|
return false;
|
|
112
110
|
}
|
|
113
111
|
|
|
112
|
+
template <typename T>
|
|
113
|
+
static std::optional<T*> ExternalValueProperty(napi_env env, napi_value obj, const std::string_view& key) {
|
|
114
|
+
if (HasProperty(env, obj, key.data())) {
|
|
115
|
+
const auto value = GetProperty(env, obj, key.data());
|
|
116
|
+
|
|
117
|
+
T* result;
|
|
118
|
+
if (napi_get_value_external(env, value, reinterpret_cast<void**>(&result)) == napi_ok) {
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
|
|
114
126
|
static std::optional<uint32_t> Uint32Property(napi_env env, napi_value obj, const std::string_view& key) {
|
|
115
127
|
if (HasProperty(env, obj, key.data())) {
|
|
116
128
|
const auto value = GetProperty(env, obj, key.data());
|
|
@@ -133,6 +145,18 @@ static std::optional<int> Int32Property(napi_env env, napi_value obj, const std:
|
|
|
133
145
|
return {};
|
|
134
146
|
}
|
|
135
147
|
|
|
148
|
+
static std::optional<int64_t> Int64Property(napi_env env, napi_value obj, const std::string_view& key) {
|
|
149
|
+
if (HasProperty(env, obj, key.data())) {
|
|
150
|
+
const auto value = GetProperty(env, obj, key.data());
|
|
151
|
+
int64_t result;
|
|
152
|
+
bool lossless;
|
|
153
|
+
napi_get_value_bigint_int64(env, value, &result, &lossless);
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
static std::string ToString(napi_env env, napi_value from, const std::string& defaultValue = "") {
|
|
137
161
|
if (IsString(env, from)) {
|
|
138
162
|
size_t length = 0;
|
|
@@ -210,6 +234,18 @@ napi_status Convert(napi_env env, rocksdb::PinnableSlice&& s, bool asBuffer, nap
|
|
|
210
234
|
}
|
|
211
235
|
}
|
|
212
236
|
|
|
237
|
+
napi_status Convert(napi_env env, std::optional<std::string>&& s, bool asBuffer, napi_value& result) {
|
|
238
|
+
if (!s) {
|
|
239
|
+
return napi_get_null(env, &result);
|
|
240
|
+
} else if (asBuffer) {
|
|
241
|
+
auto ptr = new std::string(std::move(*s));
|
|
242
|
+
return napi_create_external_buffer(env, ptr->size(), const_cast<char*>(ptr->data()), Finalize<std::string>, ptr,
|
|
243
|
+
&result);
|
|
244
|
+
} else {
|
|
245
|
+
return napi_create_string_utf8(env, s->data(), s->size(), &result);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
213
249
|
struct NapiSlice : public rocksdb::Slice {
|
|
214
250
|
std::unique_ptr<char[]> heap_;
|
|
215
251
|
std::array<char, 128> stack_;
|
|
@@ -318,6 +354,16 @@ struct Database {
|
|
|
318
354
|
DecrementPriorityWork(env);
|
|
319
355
|
}
|
|
320
356
|
|
|
357
|
+
void AttachUpdates(napi_env env, Updates* update) {
|
|
358
|
+
updates_.insert(update);
|
|
359
|
+
IncrementPriorityWork(env);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
void DetachUpdates(napi_env env, Updates* update) {
|
|
363
|
+
updates_.erase(update);
|
|
364
|
+
DecrementPriorityWork(env);
|
|
365
|
+
}
|
|
366
|
+
|
|
321
367
|
void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, priorityRef_, &priorityWork_); }
|
|
322
368
|
|
|
323
369
|
void DecrementPriorityWork(napi_env env) {
|
|
@@ -334,6 +380,8 @@ struct Database {
|
|
|
334
380
|
std::unique_ptr<rocksdb::DB> db_;
|
|
335
381
|
Worker* pendingCloseWorker_;
|
|
336
382
|
std::set<Iterator*> iterators_;
|
|
383
|
+
std::set<Updates*> updates_;
|
|
384
|
+
std::vector<rocksdb::ColumnFamilyHandle*> columns_;
|
|
337
385
|
napi_ref priorityRef_;
|
|
338
386
|
|
|
339
387
|
private:
|
|
@@ -342,16 +390,18 @@ struct Database {
|
|
|
342
390
|
|
|
343
391
|
struct BaseIterator {
|
|
344
392
|
BaseIterator(Database* database,
|
|
393
|
+
rocksdb::ColumnFamilyHandle* column,
|
|
345
394
|
const bool reverse,
|
|
346
395
|
const std::optional<std::string>& lt,
|
|
347
396
|
const std::optional<std::string>& lte,
|
|
348
397
|
const std::optional<std::string>& gt,
|
|
349
398
|
const std::optional<std::string>& gte,
|
|
350
399
|
const int limit,
|
|
351
|
-
const bool fillCache
|
|
400
|
+
const bool fillCache,
|
|
401
|
+
std::shared_ptr<const rocksdb::Snapshot> snapshot)
|
|
352
402
|
: database_(database),
|
|
353
|
-
|
|
354
|
-
|
|
403
|
+
column_(column),
|
|
404
|
+
snapshot_(snapshot),
|
|
355
405
|
reverse_(reverse),
|
|
356
406
|
limit_(limit),
|
|
357
407
|
fillCache_(fillCache) {
|
|
@@ -434,6 +484,8 @@ struct BaseIterator {
|
|
|
434
484
|
rocksdb::Status Status() const { return iterator_->status(); }
|
|
435
485
|
|
|
436
486
|
Database* database_;
|
|
487
|
+
rocksdb::ColumnFamilyHandle* column_;
|
|
488
|
+
std::shared_ptr<const rocksdb::Snapshot> snapshot_;
|
|
437
489
|
|
|
438
490
|
private:
|
|
439
491
|
void Init() {
|
|
@@ -448,12 +500,11 @@ struct BaseIterator {
|
|
|
448
500
|
options.snapshot = snapshot_.get();
|
|
449
501
|
options.async_io = true;
|
|
450
502
|
|
|
451
|
-
iterator_.reset(database_->db_->NewIterator(options));
|
|
503
|
+
iterator_.reset(database_->db_->NewIterator(options, column_));
|
|
452
504
|
}
|
|
453
505
|
|
|
454
506
|
std::optional<rocksdb::PinnableSlice> lower_bound_;
|
|
455
507
|
std::optional<rocksdb::PinnableSlice> upper_bound_;
|
|
456
|
-
std::shared_ptr<const rocksdb::Snapshot> snapshot_;
|
|
457
508
|
std::unique_ptr<rocksdb::Iterator> iterator_;
|
|
458
509
|
const bool reverse_;
|
|
459
510
|
const int limit_;
|
|
@@ -463,6 +514,7 @@ struct BaseIterator {
|
|
|
463
514
|
|
|
464
515
|
struct Iterator final : public BaseIterator {
|
|
465
516
|
Iterator(Database* database,
|
|
517
|
+
rocksdb::ColumnFamilyHandle* column,
|
|
466
518
|
const bool reverse,
|
|
467
519
|
const bool keys,
|
|
468
520
|
const bool values,
|
|
@@ -474,8 +526,9 @@ struct Iterator final : public BaseIterator {
|
|
|
474
526
|
const bool fillCache,
|
|
475
527
|
const bool keyAsBuffer,
|
|
476
528
|
const bool valueAsBuffer,
|
|
477
|
-
const uint32_t highWaterMarkBytes
|
|
478
|
-
|
|
529
|
+
const uint32_t highWaterMarkBytes,
|
|
530
|
+
std::shared_ptr<const rocksdb::Snapshot> snapshot)
|
|
531
|
+
: BaseIterator(database, column, reverse, lt, lte, gt, gte, limit, fillCache, snapshot),
|
|
479
532
|
keys_(keys),
|
|
480
533
|
values_(values),
|
|
481
534
|
keyAsBuffer_(keyAsBuffer),
|
|
@@ -505,6 +558,39 @@ struct Iterator final : public BaseIterator {
|
|
|
505
558
|
napi_ref ref_ = nullptr;
|
|
506
559
|
};
|
|
507
560
|
|
|
561
|
+
struct Updates {
|
|
562
|
+
Updates(Database* database, const bool keyAsBuffer, const bool valueAsBuffer, int64_t seqNumber)
|
|
563
|
+
: database_(database), keyAsBuffer_(keyAsBuffer), valueAsBuffer_(valueAsBuffer), seqNumber_(seqNumber) {}
|
|
564
|
+
|
|
565
|
+
void Close() { iterator_.reset(); }
|
|
566
|
+
|
|
567
|
+
void Attach(napi_env env, napi_value context) {
|
|
568
|
+
napi_create_reference(env, context, 1, &ref_);
|
|
569
|
+
database_->AttachUpdates(env, this);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
void Detach(napi_env env) {
|
|
573
|
+
database_->DetachUpdates(env, this);
|
|
574
|
+
if (ref_) {
|
|
575
|
+
napi_delete_reference(env, ref_);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
Database* database_;
|
|
580
|
+
const bool keyAsBuffer_;
|
|
581
|
+
const bool valueAsBuffer_;
|
|
582
|
+
int64_t seqNumber_;
|
|
583
|
+
std::unique_ptr<rocksdb::TransactionLogIterator> iterator_;
|
|
584
|
+
|
|
585
|
+
private:
|
|
586
|
+
napi_ref ref_ = nullptr;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
static rocksdb::ColumnFamilyHandle* GetColumnFamily(Database* database, napi_env env, napi_value options) {
|
|
590
|
+
auto column_opt = ExternalValueProperty<rocksdb::ColumnFamilyHandle>(env, options, "column");
|
|
591
|
+
return column_opt ? *column_opt : database->db_->DefaultColumnFamily();
|
|
592
|
+
}
|
|
593
|
+
|
|
508
594
|
/**
|
|
509
595
|
* Hook for when the environment exits. This hook will be called after
|
|
510
596
|
* already-scheduled napi_async_work items have finished, which gives us
|
|
@@ -521,11 +607,20 @@ static void env_cleanup_hook(void* arg) {
|
|
|
521
607
|
// following code must be a safe noop if called before db_open() or after
|
|
522
608
|
// db_close().
|
|
523
609
|
if (database && database->db_) {
|
|
524
|
-
// TODO: does not do `napi_delete_reference(env, iterator->ref_)`. Problem?
|
|
525
610
|
for (auto it : database->iterators_) {
|
|
611
|
+
// TODO: does not do `napi_delete_reference`. Problem?
|
|
612
|
+
it->Close();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
for (auto it : database->updates_) {
|
|
616
|
+
// TODO: does not do `napi_delete_reference`. Problem?
|
|
526
617
|
it->Close();
|
|
527
618
|
}
|
|
528
619
|
|
|
620
|
+
for (auto it : database->columns_) {
|
|
621
|
+
database->db_->DestroyColumnFamilyHandle(it);
|
|
622
|
+
}
|
|
623
|
+
|
|
529
624
|
// Having closed the iterators (and released snapshots) we can safely close.
|
|
530
625
|
database->db_->Close();
|
|
531
626
|
}
|
|
@@ -558,55 +653,154 @@ struct OpenWorker final : public Worker {
|
|
|
558
653
|
Database* database,
|
|
559
654
|
napi_value callback,
|
|
560
655
|
const std::string& location,
|
|
561
|
-
rocksdb::Options options,
|
|
562
|
-
|
|
656
|
+
const rocksdb::Options& options,
|
|
657
|
+
std::vector<rocksdb::ColumnFamilyDescriptor> column_families)
|
|
563
658
|
: Worker(env, database, callback, "leveldown.db.open"),
|
|
564
659
|
options_(options),
|
|
565
|
-
|
|
566
|
-
|
|
660
|
+
location_(location),
|
|
661
|
+
column_families_(std::move(column_families)) {}
|
|
567
662
|
|
|
568
663
|
rocksdb::Status Execute(Database& database) override {
|
|
569
664
|
rocksdb::DB* db = nullptr;
|
|
570
|
-
const auto status =
|
|
571
|
-
|
|
665
|
+
const auto status = column_families_.empty()
|
|
666
|
+
? rocksdb::DB::Open(options_, location_, &db)
|
|
667
|
+
: rocksdb::DB::Open(options_, location_, column_families_, &database.columns_, &db);
|
|
572
668
|
database.db_.reset(db);
|
|
573
669
|
return status;
|
|
574
670
|
}
|
|
575
671
|
|
|
672
|
+
napi_status OnOk(napi_env env, napi_value callback) override {
|
|
673
|
+
const auto size = database_->columns_.size();
|
|
674
|
+
napi_value result;
|
|
675
|
+
NAPI_STATUS_RETURN(napi_create_object(env, &result));
|
|
676
|
+
|
|
677
|
+
for (size_t n = 0; n < size; ++n) {
|
|
678
|
+
napi_value column;
|
|
679
|
+
NAPI_STATUS_RETURN(napi_create_external(env, database_->columns_[n], nullptr, nullptr, &column));
|
|
680
|
+
NAPI_STATUS_RETURN(napi_set_named_property(env, result, column_families_[n].name.c_str(), column));
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
napi_value argv[2];
|
|
684
|
+
NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
|
|
685
|
+
argv[1] = result;
|
|
686
|
+
return CallFunction(env, callback, 2, argv);
|
|
687
|
+
}
|
|
688
|
+
|
|
576
689
|
rocksdb::Options options_;
|
|
577
|
-
const bool readOnly_;
|
|
578
690
|
const std::string location_;
|
|
691
|
+
std::vector<rocksdb::ColumnFamilyDescriptor> column_families_;
|
|
579
692
|
};
|
|
580
693
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
694
|
+
napi_status InitOptions(napi_env env, auto& options, auto options2) {
|
|
695
|
+
const auto memtable_memory_budget = Uint32Property(env, options2, "memtableMemoryBudget").value_or(256 * 1024 * 1024);
|
|
696
|
+
|
|
697
|
+
const auto compaction = StringProperty(env, options2, "compaction").value_or("level");
|
|
698
|
+
|
|
699
|
+
if (compaction == "universal") {
|
|
700
|
+
options.write_buffer_size = static_cast<size_t>(memtable_memory_budget / 4);
|
|
701
|
+
// merge two memtables when flushing to L0
|
|
702
|
+
options.min_write_buffer_number_to_merge = 2;
|
|
703
|
+
// this means we'll use 50% extra memory in the worst case, but will reduce
|
|
704
|
+
// write stalls.
|
|
705
|
+
options.max_write_buffer_number = 6;
|
|
706
|
+
// universal style compaction
|
|
707
|
+
options.compaction_style = rocksdb::kCompactionStyleUniversal;
|
|
708
|
+
options.compaction_options_universal.compression_size_percent = 80;
|
|
709
|
+
} else {
|
|
710
|
+
// merge two memtables when flushing to L0
|
|
711
|
+
options.min_write_buffer_number_to_merge = 2;
|
|
712
|
+
// this means we'll use 50% extra memory in the worst case, but will reduce
|
|
713
|
+
// write stalls.
|
|
714
|
+
options.max_write_buffer_number = 6;
|
|
715
|
+
// start flushing L0->L1 as soon as possible. each file on level0 is
|
|
716
|
+
// (memtable_memory_budget / 2). This will flush level 0 when it's bigger than
|
|
717
|
+
// memtable_memory_budget.
|
|
718
|
+
options.level0_file_num_compaction_trigger = 2;
|
|
719
|
+
// doesn't really matter much, but we don't want to create too many files
|
|
720
|
+
options.target_file_size_base = memtable_memory_budget / 8;
|
|
721
|
+
// make Level1 size equal to Level0 size, so that L0->L1 compactions are fast
|
|
722
|
+
options.max_bytes_for_level_base = memtable_memory_budget;
|
|
723
|
+
|
|
724
|
+
// level style compaction
|
|
725
|
+
options.compaction_style = rocksdb::kCompactionStyleLevel;
|
|
726
|
+
|
|
727
|
+
// TODO (perf): only compress levels >= 2
|
|
728
|
+
}
|
|
589
729
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
options.error_if_exists = BooleanProperty(env, argv[2], "errorIfExists").value_or(false);
|
|
593
|
-
options.compression = BooleanProperty(env, argv[2], "compression").value_or((true)) ? rocksdb::kZSTD
|
|
594
|
-
: rocksdb::kNoCompression;
|
|
730
|
+
options.compression =
|
|
731
|
+
BooleanProperty(env, options2, "compression").value_or((true)) ? rocksdb::kZSTD : rocksdb::kNoCompression;
|
|
595
732
|
if (options.compression == rocksdb::kZSTD) {
|
|
596
733
|
options.compression_opts.max_dict_bytes = 16 * 1024;
|
|
597
734
|
options.compression_opts.zstd_max_train_bytes = 16 * 1024 * 100;
|
|
598
735
|
// options.compression_opts.parallel_threads
|
|
599
736
|
}
|
|
600
737
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
738
|
+
const auto cacheSize = Uint32Property(env, options2, "cacheSize").value_or(8 << 20);
|
|
739
|
+
|
|
740
|
+
rocksdb::BlockBasedTableOptions tableOptions;
|
|
741
|
+
|
|
742
|
+
if (cacheSize) {
|
|
743
|
+
tableOptions.block_cache = rocksdb::NewLRUCache(cacheSize);
|
|
744
|
+
tableOptions.cache_index_and_filter_blocks =
|
|
745
|
+
BooleanProperty(env, options2, "cacheIndexAndFilterBlocks").value_or(true);
|
|
746
|
+
} else {
|
|
747
|
+
tableOptions.no_block_cache = true;
|
|
748
|
+
tableOptions.cache_index_and_filter_blocks = false;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const auto optimize = StringProperty(env, options2, "optimize").value_or("");
|
|
752
|
+
|
|
753
|
+
if (optimize == "point-lookup") {
|
|
754
|
+
tableOptions.data_block_index_type = rocksdb::BlockBasedTableOptions::kDataBlockBinaryAndHash;
|
|
755
|
+
tableOptions.data_block_hash_table_util_ratio = 0.75;
|
|
756
|
+
tableOptions.filter_policy.reset(rocksdb::NewRibbonFilterPolicy(10, 1));
|
|
757
|
+
|
|
758
|
+
options.memtable_prefix_bloom_size_ratio = 0.02;
|
|
759
|
+
options.memtable_whole_key_filtering = true;
|
|
760
|
+
} else if (optimize == "range-lookup") {
|
|
761
|
+
// TODO?
|
|
762
|
+
} else {
|
|
763
|
+
tableOptions.filter_policy.reset(rocksdb::NewRibbonFilterPolicy(10));
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
tableOptions.block_size = Uint32Property(env, options2, "blockSize").value_or(4096);
|
|
767
|
+
tableOptions.block_restart_interval = Uint32Property(env, options2, "blockRestartInterval").value_or(16);
|
|
768
|
+
tableOptions.format_version = 5;
|
|
769
|
+
tableOptions.checksum = rocksdb::kXXH3;
|
|
770
|
+
tableOptions.optimize_filters_for_memory = BooleanProperty(env, options2, "optimizeFiltersForMemory").value_or(true);
|
|
771
|
+
|
|
772
|
+
options.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tableOptions));
|
|
773
|
+
|
|
774
|
+
return napi_ok;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
NAPI_METHOD(db_open) {
|
|
778
|
+
NAPI_ARGV(4);
|
|
779
|
+
|
|
780
|
+
Database* database;
|
|
781
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
782
|
+
|
|
783
|
+
const auto location = ToString(env, argv[1]);
|
|
784
|
+
const auto options = argv[2];
|
|
785
|
+
const auto callback = argv[3];
|
|
786
|
+
|
|
787
|
+
rocksdb::Options dbOptions;
|
|
788
|
+
|
|
789
|
+
dbOptions.IncreaseParallelism(
|
|
790
|
+
Uint32Property(env, options, "parallelism").value_or(std::thread::hardware_concurrency() / 2));
|
|
605
791
|
|
|
606
|
-
|
|
607
|
-
|
|
792
|
+
dbOptions.create_if_missing = BooleanProperty(env, options, "createIfMissing").value_or(true);
|
|
793
|
+
dbOptions.error_if_exists = BooleanProperty(env, options, "errorIfExists").value_or(false);
|
|
794
|
+
dbOptions.avoid_unnecessary_blocking_io = true;
|
|
795
|
+
dbOptions.use_adaptive_mutex = BooleanProperty(env, options, "useAdaptiveMutex").value_or(true);
|
|
796
|
+
dbOptions.enable_pipelined_write = BooleanProperty(env, options, "enablePipelinedWrite").value_or(true);
|
|
797
|
+
dbOptions.max_background_jobs =
|
|
798
|
+
Uint32Property(env, options, "maxBackgroundJobs").value_or(std::thread::hardware_concurrency() / 4);
|
|
799
|
+
dbOptions.WAL_ttl_seconds = Uint32Property(env, options, "walTTL").value_or(0) / 1e3;
|
|
800
|
+
dbOptions.WAL_size_limit_MB = Uint32Property(env, options, "walSizeLimit").value_or(0) / 1e6;
|
|
801
|
+
dbOptions.create_missing_column_families = true;
|
|
608
802
|
|
|
609
|
-
const auto infoLogLevel = StringProperty(env,
|
|
803
|
+
const auto infoLogLevel = StringProperty(env, options, "infoLogLevel").value_or("");
|
|
610
804
|
if (infoLogLevel.size() > 0) {
|
|
611
805
|
rocksdb::InfoLogLevel lvl = {};
|
|
612
806
|
|
|
@@ -625,40 +819,43 @@ NAPI_METHOD(db_open) {
|
|
|
625
819
|
else
|
|
626
820
|
napi_throw_error(env, nullptr, "invalid log level");
|
|
627
821
|
|
|
628
|
-
|
|
822
|
+
dbOptions.info_log_level = lvl;
|
|
629
823
|
} else {
|
|
630
824
|
// In some places RocksDB checks this option to see if it should prepare
|
|
631
825
|
// debug information (ahead of logging), so set it to the highest level.
|
|
632
|
-
|
|
633
|
-
|
|
826
|
+
dbOptions.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
|
|
827
|
+
dbOptions.info_log.reset(new NullLogger());
|
|
634
828
|
}
|
|
635
829
|
|
|
636
|
-
|
|
637
|
-
const auto cacheSize = Uint32Property(env, argv[2], "cacheSize").value_or(8 << 20);
|
|
830
|
+
NAPI_STATUS_THROWS(InitOptions(env, dbOptions, options));
|
|
638
831
|
|
|
639
|
-
rocksdb::
|
|
832
|
+
std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
|
|
833
|
+
|
|
834
|
+
if (HasProperty(env, options, "columns")) {
|
|
835
|
+
napi_value columns;
|
|
836
|
+
NAPI_STATUS_THROWS(napi_get_named_property(env, options, "columns", &columns));
|
|
640
837
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
tableOptions.cache_index_and_filter_blocks =
|
|
644
|
-
BooleanProperty(env, argv[2], "cacheIndexAndFilterBlocks").value_or(true);
|
|
645
|
-
} else {
|
|
646
|
-
tableOptions.no_block_cache = true;
|
|
647
|
-
tableOptions.cache_index_and_filter_blocks = false;
|
|
648
|
-
}
|
|
838
|
+
napi_value keys;
|
|
839
|
+
NAPI_STATUS_THROWS(napi_get_property_names(env, columns, &keys));
|
|
649
840
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
tableOptions.filter_policy.reset(rocksdb::NewRibbonFilterPolicy(10));
|
|
653
|
-
tableOptions.format_version = 5;
|
|
654
|
-
tableOptions.checksum = rocksdb::kXXH3;
|
|
655
|
-
tableOptions.optimize_filters_for_memory = BooleanProperty(env, argv[2], "optimizeFiltersForMemory").value_or(true);
|
|
841
|
+
uint32_t len;
|
|
842
|
+
NAPI_STATUS_THROWS(napi_get_array_length(env, keys, &len));
|
|
656
843
|
|
|
657
|
-
|
|
844
|
+
for (uint32_t n = 0; n < len; ++n) {
|
|
845
|
+
napi_value key;
|
|
846
|
+
NAPI_STATUS_THROWS(napi_get_element(env, keys, n, &key));
|
|
847
|
+
|
|
848
|
+
napi_value column;
|
|
849
|
+
NAPI_STATUS_THROWS(napi_get_property(env, columns, key, &column));
|
|
658
850
|
|
|
659
|
-
|
|
851
|
+
rocksdb::ColumnFamilyOptions columnOptions;
|
|
852
|
+
NAPI_STATUS_THROWS(InitOptions(env, columnOptions, column));
|
|
660
853
|
|
|
661
|
-
|
|
854
|
+
column_families.emplace_back(ToString(env, key), columnOptions);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
auto worker = new OpenWorker(env, database, callback, location, dbOptions, column_families);
|
|
662
859
|
worker->Queue(env);
|
|
663
860
|
|
|
664
861
|
return 0;
|
|
@@ -668,7 +865,13 @@ struct CloseWorker final : public Worker {
|
|
|
668
865
|
CloseWorker(napi_env env, Database* database, napi_value callback)
|
|
669
866
|
: Worker(env, database, callback, "leveldown.db.close") {}
|
|
670
867
|
|
|
671
|
-
rocksdb::Status Execute(Database& database) override {
|
|
868
|
+
rocksdb::Status Execute(Database& database) override {
|
|
869
|
+
for (auto it : database.columns_) {
|
|
870
|
+
database.db_->DestroyColumnFamilyHandle(it);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
return database.db_->Close();
|
|
874
|
+
}
|
|
672
875
|
};
|
|
673
876
|
|
|
674
877
|
napi_value noop_callback(napi_env env, napi_callback_info info) {
|
|
@@ -677,7 +880,9 @@ napi_value noop_callback(napi_env env, napi_callback_info info) {
|
|
|
677
880
|
|
|
678
881
|
NAPI_METHOD(db_close) {
|
|
679
882
|
NAPI_ARGV(2);
|
|
680
|
-
|
|
883
|
+
|
|
884
|
+
Database* database;
|
|
885
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
681
886
|
|
|
682
887
|
const auto callback = argv[1];
|
|
683
888
|
|
|
@@ -692,9 +897,147 @@ NAPI_METHOD(db_close) {
|
|
|
692
897
|
return 0;
|
|
693
898
|
}
|
|
694
899
|
|
|
900
|
+
struct UpdateNextWorker final : public rocksdb::WriteBatch::Handler, public Worker {
|
|
901
|
+
UpdateNextWorker(napi_env env, Updates* updates, napi_value callback)
|
|
902
|
+
: Worker(env, updates->database_, callback, "rocks_level.db.get"), updates_(updates) {
|
|
903
|
+
database_->IncrementPriorityWork(env);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
rocksdb::Status Execute(Database& database) override {
|
|
907
|
+
rocksdb::TransactionLogIterator::ReadOptions options;
|
|
908
|
+
|
|
909
|
+
if (!updates_->iterator_) {
|
|
910
|
+
const auto status = database_->db_->GetUpdatesSince(updates_->seqNumber_, &updates_->iterator_, options);
|
|
911
|
+
if (!status.ok()) {
|
|
912
|
+
return status;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (!updates_->iterator_->Valid()) {
|
|
917
|
+
return rocksdb::Status::OK();
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
auto batch = updates_->iterator_->GetBatch();
|
|
921
|
+
|
|
922
|
+
updates_->seqNumber_ = batch.sequence;
|
|
923
|
+
|
|
924
|
+
const auto status = batch.writeBatchPtr->Iterate(this);
|
|
925
|
+
if (!status.ok()) {
|
|
926
|
+
return status;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
updates_->iterator_->Next();
|
|
930
|
+
|
|
931
|
+
return rocksdb::Status::OK();
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
napi_status OnOk(napi_env env, napi_value callback) override {
|
|
935
|
+
const auto size = cache_.size();
|
|
936
|
+
napi_value result;
|
|
937
|
+
|
|
938
|
+
if (cache_.size()) {
|
|
939
|
+
NAPI_STATUS_RETURN(napi_create_array_with_length(env, size, &result));
|
|
940
|
+
|
|
941
|
+
for (size_t idx = 0; idx < cache_.size(); idx += 2) {
|
|
942
|
+
napi_value key;
|
|
943
|
+
napi_value val;
|
|
944
|
+
|
|
945
|
+
NAPI_STATUS_RETURN(Convert(env, std::move(cache_[idx + 0]), updates_->keyAsBuffer_, key));
|
|
946
|
+
NAPI_STATUS_RETURN(Convert(env, std::move(cache_[idx + 1]), updates_->valueAsBuffer_, val));
|
|
947
|
+
|
|
948
|
+
NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(idx + 0), key));
|
|
949
|
+
NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(idx + 1), val));
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
cache_.clear();
|
|
953
|
+
} else {
|
|
954
|
+
NAPI_STATUS_RETURN(napi_get_null(env, &result));
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
napi_value argv[3];
|
|
958
|
+
NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
|
|
959
|
+
argv[1] = result;
|
|
960
|
+
NAPI_STATUS_RETURN(napi_create_bigint_int64(env, updates_->seqNumber_, &argv[2]));
|
|
961
|
+
return CallFunction(env, callback, 3, argv);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
void Destroy(napi_env env) override {
|
|
965
|
+
database_->DecrementPriorityWork(env);
|
|
966
|
+
Worker::Destroy(env);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
void Put(const rocksdb::Slice& key, const rocksdb::Slice& value) override {
|
|
970
|
+
cache_.emplace_back(key.ToString());
|
|
971
|
+
cache_.emplace_back(value.ToString());
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
void Delete(const rocksdb::Slice& key) override {
|
|
975
|
+
cache_.emplace_back(key.ToString());
|
|
976
|
+
cache_.emplace_back(std::nullopt);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
bool Continue() override { return true; }
|
|
980
|
+
|
|
981
|
+
private:
|
|
982
|
+
std::vector<std::optional<std::string>> cache_;
|
|
983
|
+
Updates* updates_;
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
NAPI_METHOD(updates_init) {
|
|
987
|
+
NAPI_ARGV(2);
|
|
988
|
+
|
|
989
|
+
Database* database;
|
|
990
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
991
|
+
|
|
992
|
+
const auto options = argv[1];
|
|
993
|
+
|
|
994
|
+
const bool keyAsBuffer = EncodingIsBuffer(env, options, "keyEncoding");
|
|
995
|
+
const bool valueAsBuffer = EncodingIsBuffer(env, options, "valueEncoding");
|
|
996
|
+
const auto seqNumber = Int64Property(env, options, "since").value_or(database->db_->GetLatestSequenceNumber());
|
|
997
|
+
|
|
998
|
+
auto updates = std::make_unique<Updates>(database, keyAsBuffer, valueAsBuffer, seqNumber);
|
|
999
|
+
|
|
1000
|
+
napi_value result;
|
|
1001
|
+
NAPI_STATUS_THROWS(napi_create_external(env, updates.get(), Finalize<Updates>, updates.get(), &result));
|
|
1002
|
+
|
|
1003
|
+
// Prevent GC of JS object before the iterator is closed (explicitly or on
|
|
1004
|
+
// db close) and keep track of non-closed iterators to end them on db close.
|
|
1005
|
+
updates.release()->Attach(env, result);
|
|
1006
|
+
|
|
1007
|
+
return result;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
NAPI_METHOD(updates_next) {
|
|
1011
|
+
NAPI_ARGV(2);
|
|
1012
|
+
|
|
1013
|
+
Updates* updates;
|
|
1014
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&updates));
|
|
1015
|
+
|
|
1016
|
+
const auto callback = argv[1];
|
|
1017
|
+
|
|
1018
|
+
auto worker = new UpdateNextWorker(env, updates, callback);
|
|
1019
|
+
worker->Queue(env);
|
|
1020
|
+
|
|
1021
|
+
return 0;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
NAPI_METHOD(updates_close) {
|
|
1025
|
+
NAPI_ARGV(1);
|
|
1026
|
+
|
|
1027
|
+
Updates* updates;
|
|
1028
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&updates));
|
|
1029
|
+
|
|
1030
|
+
updates->Detach(env);
|
|
1031
|
+
updates->Close();
|
|
1032
|
+
|
|
1033
|
+
return 0;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
695
1036
|
NAPI_METHOD(db_put) {
|
|
696
1037
|
NAPI_ARGV(4);
|
|
697
|
-
|
|
1038
|
+
|
|
1039
|
+
Database* database;
|
|
1040
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
698
1041
|
|
|
699
1042
|
NapiSlice key;
|
|
700
1043
|
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[1], key));
|
|
@@ -702,18 +1045,20 @@ NAPI_METHOD(db_put) {
|
|
|
702
1045
|
NapiSlice val;
|
|
703
1046
|
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[2], val));
|
|
704
1047
|
|
|
705
|
-
rocksdb::WriteOptions
|
|
706
|
-
return ToError(env, database->db_->Put(
|
|
1048
|
+
rocksdb::WriteOptions writeOptions;
|
|
1049
|
+
return ToError(env, database->db_->Put(writeOptions, key, val));
|
|
707
1050
|
}
|
|
708
1051
|
|
|
709
1052
|
struct GetWorker final : public Worker {
|
|
710
1053
|
GetWorker(napi_env env,
|
|
711
1054
|
Database* database,
|
|
1055
|
+
rocksdb::ColumnFamilyHandle* column,
|
|
712
1056
|
napi_value callback,
|
|
713
1057
|
const std::string& key,
|
|
714
1058
|
const bool asBuffer,
|
|
715
1059
|
const bool fillCache)
|
|
716
1060
|
: Worker(env, database, callback, "rocks_level.db.get"),
|
|
1061
|
+
column_(column),
|
|
717
1062
|
key_(key),
|
|
718
1063
|
asBuffer_(asBuffer),
|
|
719
1064
|
fillCache_(fillCache),
|
|
@@ -723,11 +1068,11 @@ struct GetWorker final : public Worker {
|
|
|
723
1068
|
}
|
|
724
1069
|
|
|
725
1070
|
rocksdb::Status Execute(Database& database) override {
|
|
726
|
-
rocksdb::ReadOptions
|
|
727
|
-
|
|
728
|
-
|
|
1071
|
+
rocksdb::ReadOptions readOptions;
|
|
1072
|
+
readOptions.fill_cache = fillCache_;
|
|
1073
|
+
readOptions.snapshot = snapshot_.get();
|
|
729
1074
|
|
|
730
|
-
auto status = database.db_->Get(
|
|
1075
|
+
auto status = database.db_->Get(readOptions, column_, key_, &value_);
|
|
731
1076
|
|
|
732
1077
|
key_.clear();
|
|
733
1078
|
snapshot_ = nullptr;
|
|
@@ -748,6 +1093,7 @@ struct GetWorker final : public Worker {
|
|
|
748
1093
|
}
|
|
749
1094
|
|
|
750
1095
|
private:
|
|
1096
|
+
rocksdb::ColumnFamilyHandle* column_;
|
|
751
1097
|
std::string key_;
|
|
752
1098
|
rocksdb::PinnableSlice value_;
|
|
753
1099
|
const bool asBuffer_;
|
|
@@ -757,15 +1103,18 @@ struct GetWorker final : public Worker {
|
|
|
757
1103
|
|
|
758
1104
|
NAPI_METHOD(db_get) {
|
|
759
1105
|
NAPI_ARGV(4);
|
|
760
|
-
|
|
1106
|
+
|
|
1107
|
+
Database* database;
|
|
1108
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
761
1109
|
|
|
762
1110
|
const auto key = ToString(env, argv[1]);
|
|
763
1111
|
const auto options = argv[2];
|
|
764
1112
|
const auto asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
|
|
765
1113
|
const auto fillCache = BooleanProperty(env, options, "fillCache").value_or(true);
|
|
1114
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
766
1115
|
const auto callback = argv[3];
|
|
767
1116
|
|
|
768
|
-
auto worker = new GetWorker(env, database, callback, key, asBuffer, fillCache);
|
|
1117
|
+
auto worker = new GetWorker(env, database, column, callback, key, asBuffer, fillCache);
|
|
769
1118
|
worker->Queue(env);
|
|
770
1119
|
|
|
771
1120
|
return 0;
|
|
@@ -774,24 +1123,25 @@ NAPI_METHOD(db_get) {
|
|
|
774
1123
|
struct GetManyWorker final : public Worker {
|
|
775
1124
|
GetManyWorker(napi_env env,
|
|
776
1125
|
Database* database,
|
|
1126
|
+
rocksdb::ColumnFamilyHandle* column,
|
|
777
1127
|
std::vector<std::string> keys,
|
|
778
1128
|
napi_value callback,
|
|
779
1129
|
const bool valueAsBuffer,
|
|
780
1130
|
const bool fillCache)
|
|
781
1131
|
: Worker(env, database, callback, "leveldown.get.many"),
|
|
1132
|
+
column_(column),
|
|
782
1133
|
keys_(std::move(keys)),
|
|
783
1134
|
valueAsBuffer_(valueAsBuffer),
|
|
784
1135
|
fillCache_(fillCache),
|
|
785
|
-
snapshot_(database_->db_->GetSnapshot(),
|
|
786
|
-
[this](const rocksdb::Snapshot* ptr) { database_->db_->ReleaseSnapshot(ptr); }) {
|
|
1136
|
+
snapshot_(database_->db_->GetSnapshot(), [=](const auto ptr) { database_->db_->ReleaseSnapshot(ptr); }) {
|
|
787
1137
|
database_->IncrementPriorityWork(env);
|
|
788
1138
|
}
|
|
789
1139
|
|
|
790
1140
|
rocksdb::Status Execute(Database& database) override {
|
|
791
|
-
rocksdb::ReadOptions
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1141
|
+
rocksdb::ReadOptions readOptions;
|
|
1142
|
+
readOptions.fill_cache = fillCache_;
|
|
1143
|
+
readOptions.snapshot = snapshot_.get();
|
|
1144
|
+
readOptions.async_io = true;
|
|
795
1145
|
|
|
796
1146
|
const auto numKeys = keys_.size();
|
|
797
1147
|
|
|
@@ -804,8 +1154,7 @@ struct GetManyWorker final : public Worker {
|
|
|
804
1154
|
statuses_.resize(numKeys);
|
|
805
1155
|
values_.resize(numKeys);
|
|
806
1156
|
|
|
807
|
-
database.db_->MultiGet(
|
|
808
|
-
statuses_.data());
|
|
1157
|
+
database.db_->MultiGet(readOptions, column_, numKeys, keys.data(), values_.data(), statuses_.data());
|
|
809
1158
|
|
|
810
1159
|
keys_.clear();
|
|
811
1160
|
snapshot_ = nullptr;
|
|
@@ -850,6 +1199,7 @@ struct GetManyWorker final : public Worker {
|
|
|
850
1199
|
}
|
|
851
1200
|
|
|
852
1201
|
private:
|
|
1202
|
+
rocksdb::ColumnFamilyHandle* column_;
|
|
853
1203
|
std::vector<std::string> keys_;
|
|
854
1204
|
std::vector<rocksdb::PinnableSlice> values_;
|
|
855
1205
|
std::vector<rocksdb::Status> statuses_;
|
|
@@ -860,7 +1210,9 @@ struct GetManyWorker final : public Worker {
|
|
|
860
1210
|
|
|
861
1211
|
NAPI_METHOD(db_get_many) {
|
|
862
1212
|
NAPI_ARGV(4);
|
|
863
|
-
|
|
1213
|
+
|
|
1214
|
+
Database* database;
|
|
1215
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
864
1216
|
|
|
865
1217
|
std::vector<std::string> keys;
|
|
866
1218
|
{
|
|
@@ -880,9 +1232,10 @@ NAPI_METHOD(db_get_many) {
|
|
|
880
1232
|
const auto options = argv[2];
|
|
881
1233
|
const bool asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
|
|
882
1234
|
const bool fillCache = BooleanProperty(env, options, "fillCache").value_or(true);
|
|
1235
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
883
1236
|
const auto callback = argv[3];
|
|
884
1237
|
|
|
885
|
-
auto worker = new GetManyWorker(env, database, std::move(keys), callback, asBuffer, fillCache);
|
|
1238
|
+
auto worker = new GetManyWorker(env, database, column, std::move(keys), callback, asBuffer, fillCache);
|
|
886
1239
|
worker->Queue(env);
|
|
887
1240
|
|
|
888
1241
|
return 0;
|
|
@@ -890,27 +1243,36 @@ NAPI_METHOD(db_get_many) {
|
|
|
890
1243
|
|
|
891
1244
|
NAPI_METHOD(db_del) {
|
|
892
1245
|
NAPI_ARGV(3);
|
|
893
|
-
|
|
1246
|
+
|
|
1247
|
+
Database* database;
|
|
1248
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
894
1249
|
|
|
895
1250
|
NapiSlice key;
|
|
896
1251
|
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[1], key));
|
|
897
1252
|
|
|
898
|
-
|
|
899
|
-
|
|
1253
|
+
const auto options = argv[2];
|
|
1254
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
1255
|
+
|
|
1256
|
+
rocksdb::WriteOptions writeOptions;
|
|
1257
|
+
return ToError(env, database->db_->Delete(writeOptions, column, key));
|
|
900
1258
|
}
|
|
901
1259
|
|
|
902
1260
|
NAPI_METHOD(db_clear) {
|
|
903
1261
|
NAPI_ARGV(2);
|
|
904
|
-
NAPI_DB_CONTEXT();
|
|
905
1262
|
|
|
906
|
-
|
|
907
|
-
|
|
1263
|
+
Database* database;
|
|
1264
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1265
|
+
|
|
1266
|
+
const auto options = argv[1];
|
|
1267
|
+
const auto reverse = BooleanProperty(env, options, "reverse").value_or(false);
|
|
1268
|
+
const auto limit = Int32Property(env, options, "limit").value_or(-1);
|
|
1269
|
+
|
|
1270
|
+
const auto lt = StringProperty(env, options, "lt");
|
|
1271
|
+
const auto lte = StringProperty(env, options, "lte");
|
|
1272
|
+
const auto gt = StringProperty(env, options, "gt");
|
|
1273
|
+
const auto gte = StringProperty(env, options, "gte");
|
|
1274
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
908
1275
|
|
|
909
|
-
const auto lt = StringProperty(env, argv[1], "lt");
|
|
910
|
-
const auto lte = StringProperty(env, argv[1], "lte");
|
|
911
|
-
const auto gt = StringProperty(env, argv[1], "gt");
|
|
912
|
-
const auto gte = StringProperty(env, argv[1], "gte");
|
|
913
|
-
|
|
914
1276
|
if (!reverse && limit == -1 && (lte || lt)) {
|
|
915
1277
|
rocksdb::Slice begin;
|
|
916
1278
|
if (gte) {
|
|
@@ -932,13 +1294,15 @@ NAPI_METHOD(db_clear) {
|
|
|
932
1294
|
return ToError(env, rocksdb::Status::OK());
|
|
933
1295
|
}
|
|
934
1296
|
|
|
935
|
-
rocksdb::WriteOptions
|
|
936
|
-
const auto status = database->db_->DeleteRange(
|
|
1297
|
+
rocksdb::WriteOptions writeOptions;
|
|
1298
|
+
const auto status = database->db_->DeleteRange(writeOptions, column, begin, end);
|
|
937
1299
|
return ToError(env, status);
|
|
938
1300
|
} else {
|
|
939
1301
|
// TODO (perf): Use DeleteRange.
|
|
940
1302
|
|
|
941
|
-
|
|
1303
|
+
std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db_->GetSnapshot(),
|
|
1304
|
+
[=](const auto ptr) { database->db_->ReleaseSnapshot(ptr); });
|
|
1305
|
+
BaseIterator it(database, column, reverse, lt, lte, gt, gte, limit, false, snapshot);
|
|
942
1306
|
|
|
943
1307
|
it.SeekToRange();
|
|
944
1308
|
|
|
@@ -946,7 +1310,7 @@ NAPI_METHOD(db_clear) {
|
|
|
946
1310
|
const uint32_t hwm = 16 * 1024;
|
|
947
1311
|
|
|
948
1312
|
rocksdb::WriteBatch batch;
|
|
949
|
-
rocksdb::WriteOptions
|
|
1313
|
+
rocksdb::WriteOptions writeOptions;
|
|
950
1314
|
rocksdb::Status status;
|
|
951
1315
|
|
|
952
1316
|
while (true) {
|
|
@@ -954,7 +1318,7 @@ NAPI_METHOD(db_clear) {
|
|
|
954
1318
|
|
|
955
1319
|
while (bytesRead <= hwm && it.Valid() && it.Increment()) {
|
|
956
1320
|
const auto key = it.CurrentKey();
|
|
957
|
-
batch.Delete(key);
|
|
1321
|
+
batch.Delete(column, key);
|
|
958
1322
|
bytesRead += key.size();
|
|
959
1323
|
it.Next();
|
|
960
1324
|
}
|
|
@@ -964,7 +1328,7 @@ NAPI_METHOD(db_clear) {
|
|
|
964
1328
|
break;
|
|
965
1329
|
}
|
|
966
1330
|
|
|
967
|
-
status = database->db_->Write(
|
|
1331
|
+
status = database->db_->Write(writeOptions, &batch);
|
|
968
1332
|
if (!status.ok()) {
|
|
969
1333
|
break;
|
|
970
1334
|
}
|
|
@@ -980,7 +1344,9 @@ NAPI_METHOD(db_clear) {
|
|
|
980
1344
|
|
|
981
1345
|
NAPI_METHOD(db_get_property) {
|
|
982
1346
|
NAPI_ARGV(2);
|
|
983
|
-
|
|
1347
|
+
|
|
1348
|
+
Database* database;
|
|
1349
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
984
1350
|
|
|
985
1351
|
NapiSlice property;
|
|
986
1352
|
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[1], property));
|
|
@@ -996,7 +1362,9 @@ NAPI_METHOD(db_get_property) {
|
|
|
996
1362
|
|
|
997
1363
|
NAPI_METHOD(iterator_init) {
|
|
998
1364
|
NAPI_ARGV(2);
|
|
999
|
-
|
|
1365
|
+
|
|
1366
|
+
Database* database;
|
|
1367
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1000
1368
|
|
|
1001
1369
|
const auto options = argv[1];
|
|
1002
1370
|
const auto reverse = BooleanProperty(env, options, "reverse").value_or(false);
|
|
@@ -1013,8 +1381,13 @@ NAPI_METHOD(iterator_init) {
|
|
|
1013
1381
|
const auto gt = StringProperty(env, options, "gt");
|
|
1014
1382
|
const auto gte = StringProperty(env, options, "gte");
|
|
1015
1383
|
|
|
1016
|
-
auto
|
|
1017
|
-
|
|
1384
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
1385
|
+
|
|
1386
|
+
std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db_->GetSnapshot(),
|
|
1387
|
+
[=](const auto ptr) { database->db_->ReleaseSnapshot(ptr); });
|
|
1388
|
+
|
|
1389
|
+
auto iterator = std::make_unique<Iterator>(database, column, reverse, keys, values, limit, lt, lte, gt, gte,
|
|
1390
|
+
fillCache, keyAsBuffer, valueAsBuffer, highWaterMarkBytes, snapshot);
|
|
1018
1391
|
|
|
1019
1392
|
napi_value result;
|
|
1020
1393
|
NAPI_STATUS_THROWS(napi_create_external(env, iterator.get(), Finalize<Iterator>, iterator.get(), &result));
|
|
@@ -1028,7 +1401,9 @@ NAPI_METHOD(iterator_init) {
|
|
|
1028
1401
|
|
|
1029
1402
|
NAPI_METHOD(iterator_seek) {
|
|
1030
1403
|
NAPI_ARGV(2);
|
|
1031
|
-
|
|
1404
|
+
|
|
1405
|
+
Iterator* iterator;
|
|
1406
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator));
|
|
1032
1407
|
|
|
1033
1408
|
NapiSlice target;
|
|
1034
1409
|
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[1], target));
|
|
@@ -1041,7 +1416,9 @@ NAPI_METHOD(iterator_seek) {
|
|
|
1041
1416
|
|
|
1042
1417
|
NAPI_METHOD(iterator_close) {
|
|
1043
1418
|
NAPI_ARGV(1);
|
|
1044
|
-
|
|
1419
|
+
|
|
1420
|
+
Iterator* iterator;
|
|
1421
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator));
|
|
1045
1422
|
|
|
1046
1423
|
iterator->Detach(env);
|
|
1047
1424
|
iterator->Close();
|
|
@@ -1049,6 +1426,20 @@ NAPI_METHOD(iterator_close) {
|
|
|
1049
1426
|
return 0;
|
|
1050
1427
|
}
|
|
1051
1428
|
|
|
1429
|
+
NAPI_METHOD(iterator_get_sequence) {
|
|
1430
|
+
NAPI_ARGV(1);
|
|
1431
|
+
|
|
1432
|
+
Iterator* iterator;
|
|
1433
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator));
|
|
1434
|
+
|
|
1435
|
+
const auto seq = iterator->snapshot_->GetSequenceNumber();
|
|
1436
|
+
|
|
1437
|
+
napi_value result;
|
|
1438
|
+
NAPI_STATUS_THROWS(napi_create_bigint_int64(env, seq, &result));
|
|
1439
|
+
|
|
1440
|
+
return 0;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1052
1443
|
struct NextWorker final : public Worker {
|
|
1053
1444
|
NextWorker(napi_env env, Iterator* iterator, uint32_t size, napi_value callback)
|
|
1054
1445
|
: Worker(env, iterator->database_, callback, "leveldown.iterator.next"), iterator_(iterator), size_(size) {}
|
|
@@ -1073,33 +1464,29 @@ struct NextWorker final : public Worker {
|
|
|
1073
1464
|
if (iterator_->keys_ && iterator_->values_) {
|
|
1074
1465
|
auto k = iterator_->CurrentKey();
|
|
1075
1466
|
auto v = iterator_->CurrentValue();
|
|
1076
|
-
cache_.push_back({});
|
|
1077
|
-
cache_.back().PinSelf(k);
|
|
1078
|
-
cache_.push_back({});
|
|
1079
|
-
cache_.back().PinSelf(v);
|
|
1080
1467
|
bytesRead += k.size() + v.size();
|
|
1468
|
+
cache_.push_back(k.ToString());
|
|
1469
|
+
cache_.push_back(v.ToString());
|
|
1081
1470
|
} else if (iterator_->keys_) {
|
|
1082
1471
|
auto k = iterator_->CurrentKey();
|
|
1083
|
-
cache_.push_back({});
|
|
1084
|
-
cache_.back().PinSelf(k);
|
|
1085
|
-
cache_.push_back({});
|
|
1086
|
-
// no value
|
|
1087
1472
|
bytesRead += k.size();
|
|
1473
|
+
cache_.push_back(k.ToString());
|
|
1474
|
+
cache_.push_back(std::nullopt);
|
|
1088
1475
|
} else if (iterator_->values_) {
|
|
1089
1476
|
auto v = iterator_->CurrentValue();
|
|
1090
|
-
cache_.push_back({});
|
|
1091
|
-
// no key
|
|
1092
|
-
cache_.push_back({});
|
|
1093
|
-
cache_.back().PinSelf(v);
|
|
1094
1477
|
bytesRead += v.size();
|
|
1478
|
+
cache_.push_back(std::nullopt);
|
|
1479
|
+
cache_.push_back(v.ToString());
|
|
1095
1480
|
}
|
|
1096
1481
|
|
|
1097
1482
|
if (bytesRead > iterator_->highWaterMarkBytes_ || cache_.size() / 2 >= size_) {
|
|
1098
|
-
finished_ =
|
|
1483
|
+
finished_ = false;
|
|
1099
1484
|
return rocksdb::Status::OK();
|
|
1100
1485
|
}
|
|
1101
1486
|
}
|
|
1102
1487
|
|
|
1488
|
+
finished_ = true;
|
|
1489
|
+
|
|
1103
1490
|
return iterator_->Status();
|
|
1104
1491
|
}
|
|
1105
1492
|
|
|
@@ -1108,15 +1495,15 @@ struct NextWorker final : public Worker {
|
|
|
1108
1495
|
napi_value result;
|
|
1109
1496
|
NAPI_STATUS_RETURN(napi_create_array_with_length(env, size, &result));
|
|
1110
1497
|
|
|
1111
|
-
for (size_t
|
|
1498
|
+
for (size_t n = 0; n < size; n += 2) {
|
|
1112
1499
|
napi_value key;
|
|
1113
1500
|
napi_value val;
|
|
1114
1501
|
|
|
1115
|
-
NAPI_STATUS_RETURN(Convert(env, std::move(cache_[
|
|
1116
|
-
NAPI_STATUS_RETURN(Convert(env, std::move(cache_[
|
|
1502
|
+
NAPI_STATUS_RETURN(Convert(env, std::move(cache_[n + 0]), iterator_->keyAsBuffer_, key));
|
|
1503
|
+
NAPI_STATUS_RETURN(Convert(env, std::move(cache_[n + 1]), iterator_->valueAsBuffer_, val));
|
|
1117
1504
|
|
|
1118
|
-
NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(
|
|
1119
|
-
NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(
|
|
1505
|
+
NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(n + 0), key));
|
|
1506
|
+
NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(n + 1), val));
|
|
1120
1507
|
}
|
|
1121
1508
|
|
|
1122
1509
|
cache_.clear();
|
|
@@ -1124,20 +1511,22 @@ struct NextWorker final : public Worker {
|
|
|
1124
1511
|
napi_value argv[3];
|
|
1125
1512
|
NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
|
|
1126
1513
|
argv[1] = result;
|
|
1127
|
-
NAPI_STATUS_RETURN(napi_get_boolean(env,
|
|
1514
|
+
NAPI_STATUS_RETURN(napi_get_boolean(env, finished_, &argv[2]));
|
|
1128
1515
|
return CallFunction(env, callback, 3, argv);
|
|
1129
1516
|
}
|
|
1130
1517
|
|
|
1131
1518
|
private:
|
|
1132
|
-
std::vector<
|
|
1519
|
+
std::vector<std::optional<std::string>> cache_;
|
|
1133
1520
|
Iterator* iterator_ = nullptr;
|
|
1134
1521
|
uint32_t size_ = 0;
|
|
1135
|
-
bool finished_ =
|
|
1522
|
+
bool finished_ = true;
|
|
1136
1523
|
};
|
|
1137
1524
|
|
|
1138
1525
|
NAPI_METHOD(iterator_nextv) {
|
|
1139
1526
|
NAPI_ARGV(3);
|
|
1140
|
-
|
|
1527
|
+
|
|
1528
|
+
Iterator* iterator;
|
|
1529
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator));
|
|
1141
1530
|
|
|
1142
1531
|
uint32_t size;
|
|
1143
1532
|
NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &size));
|
|
@@ -1152,9 +1541,12 @@ NAPI_METHOD(iterator_nextv) {
|
|
|
1152
1541
|
|
|
1153
1542
|
NAPI_METHOD(batch_do) {
|
|
1154
1543
|
NAPI_ARGV(3);
|
|
1155
|
-
|
|
1544
|
+
|
|
1545
|
+
Database* database;
|
|
1546
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1156
1547
|
|
|
1157
1548
|
const auto operations = argv[1];
|
|
1549
|
+
const auto options = argv[2];
|
|
1158
1550
|
|
|
1159
1551
|
rocksdb::WriteBatch batch;
|
|
1160
1552
|
|
|
@@ -1168,26 +1560,30 @@ NAPI_METHOD(batch_do) {
|
|
|
1168
1560
|
NapiSlice type;
|
|
1169
1561
|
NAPI_STATUS_THROWS(ToNapiSlice(env, GetProperty(env, element, "type"), type));
|
|
1170
1562
|
|
|
1563
|
+
const auto column = GetColumnFamily(database, env, element);
|
|
1564
|
+
|
|
1171
1565
|
if (type == "del") {
|
|
1172
1566
|
NapiSlice key;
|
|
1173
1567
|
NAPI_STATUS_THROWS(ToNapiSlice(env, GetProperty(env, element, "key"), key));
|
|
1174
|
-
batch.Delete(key);
|
|
1568
|
+
batch.Delete(column, key);
|
|
1175
1569
|
} else if (type == "put") {
|
|
1176
1570
|
NapiSlice key;
|
|
1177
1571
|
NAPI_STATUS_THROWS(ToNapiSlice(env, GetProperty(env, element, "key"), key));
|
|
1178
1572
|
NapiSlice value;
|
|
1179
1573
|
NAPI_STATUS_THROWS(ToNapiSlice(env, GetProperty(env, element, "value"), value));
|
|
1180
|
-
batch.Put(key, value);
|
|
1574
|
+
batch.Put(column, key, value);
|
|
1181
1575
|
}
|
|
1182
1576
|
}
|
|
1183
1577
|
|
|
1184
|
-
rocksdb::WriteOptions
|
|
1185
|
-
return ToError(env, database->db_->Write(
|
|
1578
|
+
rocksdb::WriteOptions writeOptions;
|
|
1579
|
+
return ToError(env, database->db_->Write(writeOptions, &batch));
|
|
1186
1580
|
}
|
|
1187
1581
|
|
|
1188
1582
|
NAPI_METHOD(batch_init) {
|
|
1189
1583
|
NAPI_ARGV(1);
|
|
1190
|
-
|
|
1584
|
+
|
|
1585
|
+
Database* database;
|
|
1586
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1191
1587
|
|
|
1192
1588
|
auto batch = new rocksdb::WriteBatch();
|
|
1193
1589
|
|
|
@@ -1197,35 +1593,53 @@ NAPI_METHOD(batch_init) {
|
|
|
1197
1593
|
}
|
|
1198
1594
|
|
|
1199
1595
|
NAPI_METHOD(batch_put) {
|
|
1200
|
-
NAPI_ARGV(
|
|
1201
|
-
|
|
1596
|
+
NAPI_ARGV(5);
|
|
1597
|
+
|
|
1598
|
+
Database* database;
|
|
1599
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1600
|
+
|
|
1601
|
+
rocksdb::WriteBatch* batch;
|
|
1602
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], (void**)(&batch)));
|
|
1202
1603
|
|
|
1203
1604
|
NapiSlice key;
|
|
1204
|
-
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[
|
|
1605
|
+
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[2], key));
|
|
1205
1606
|
|
|
1206
1607
|
NapiSlice val;
|
|
1207
|
-
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[
|
|
1608
|
+
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[3], val));
|
|
1609
|
+
|
|
1610
|
+
const auto options = argv[4];
|
|
1611
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
1208
1612
|
|
|
1209
|
-
batch->Put(key, val);
|
|
1613
|
+
batch->Put(column, key, val);
|
|
1210
1614
|
|
|
1211
1615
|
return 0;
|
|
1212
1616
|
}
|
|
1213
1617
|
|
|
1214
1618
|
NAPI_METHOD(batch_del) {
|
|
1215
|
-
NAPI_ARGV(
|
|
1216
|
-
|
|
1619
|
+
NAPI_ARGV(4);
|
|
1620
|
+
|
|
1621
|
+
Database* database;
|
|
1622
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1623
|
+
|
|
1624
|
+
rocksdb::WriteBatch* batch;
|
|
1625
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
|
|
1217
1626
|
|
|
1218
1627
|
NapiSlice key;
|
|
1219
|
-
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[
|
|
1628
|
+
NAPI_STATUS_THROWS(ToNapiSlice(env, argv[2], key));
|
|
1220
1629
|
|
|
1221
|
-
|
|
1630
|
+
const auto options = argv[3];
|
|
1631
|
+
const auto column = GetColumnFamily(database, env, options);
|
|
1632
|
+
|
|
1633
|
+
batch->Delete(column, key);
|
|
1222
1634
|
|
|
1223
1635
|
return 0;
|
|
1224
1636
|
}
|
|
1225
1637
|
|
|
1226
1638
|
NAPI_METHOD(batch_clear) {
|
|
1227
|
-
NAPI_ARGV(
|
|
1228
|
-
|
|
1639
|
+
NAPI_ARGV(2);
|
|
1640
|
+
|
|
1641
|
+
rocksdb::WriteBatch* batch;
|
|
1642
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
|
|
1229
1643
|
|
|
1230
1644
|
batch->Clear();
|
|
1231
1645
|
|
|
@@ -1234,13 +1648,15 @@ NAPI_METHOD(batch_clear) {
|
|
|
1234
1648
|
|
|
1235
1649
|
NAPI_METHOD(batch_write) {
|
|
1236
1650
|
NAPI_ARGV(3);
|
|
1237
|
-
|
|
1651
|
+
|
|
1652
|
+
Database* database;
|
|
1653
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
1238
1654
|
|
|
1239
1655
|
rocksdb::WriteBatch* batch;
|
|
1240
1656
|
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
|
|
1241
1657
|
|
|
1242
|
-
rocksdb::WriteOptions
|
|
1243
|
-
return ToError(env, database->db_->Write(
|
|
1658
|
+
rocksdb::WriteOptions writeOptions;
|
|
1659
|
+
return ToError(env, database->db_->Write(writeOptions, batch));
|
|
1244
1660
|
}
|
|
1245
1661
|
|
|
1246
1662
|
NAPI_INIT() {
|
|
@@ -1258,6 +1674,11 @@ NAPI_INIT() {
|
|
|
1258
1674
|
NAPI_EXPORT_FUNCTION(iterator_seek);
|
|
1259
1675
|
NAPI_EXPORT_FUNCTION(iterator_close);
|
|
1260
1676
|
NAPI_EXPORT_FUNCTION(iterator_nextv);
|
|
1677
|
+
NAPI_EXPORT_FUNCTION(iterator_get_sequence);
|
|
1678
|
+
|
|
1679
|
+
NAPI_EXPORT_FUNCTION(updates_init);
|
|
1680
|
+
NAPI_EXPORT_FUNCTION(updates_close);
|
|
1681
|
+
NAPI_EXPORT_FUNCTION(updates_next);
|
|
1261
1682
|
|
|
1262
1683
|
NAPI_EXPORT_FUNCTION(batch_do);
|
|
1263
1684
|
NAPI_EXPORT_FUNCTION(batch_init);
|