@nxtedition/rocksdb 7.0.0-alpha.2 → 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.
Files changed (3) hide show
  1. package/binding.cc +86 -124
  2. package/index.js +35 -91
  3. package/package.json +1 -1
package/binding.cc CHANGED
@@ -31,7 +31,6 @@ class NullLogger : public rocksdb::Logger {
31
31
  struct Database;
32
32
  struct Iterator;
33
33
  struct Updates;
34
- struct Column;
35
34
 
36
35
  #define NAPI_STATUS_RETURN(call) \
37
36
  { \
@@ -365,16 +364,6 @@ struct Database {
365
364
  DecrementPriorityWork(env);
366
365
  }
367
366
 
368
- void AttachColumn(napi_env env, Column* column) {
369
- columns_.insert(column);
370
- IncrementPriorityWork(env);
371
- }
372
-
373
- void DetachColumn(napi_env env, Column* column) {
374
- columns_.erase(column);
375
- DecrementPriorityWork(env);
376
- }
377
-
378
367
  void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, priorityRef_, &priorityWork_); }
379
368
 
380
369
  void DecrementPriorityWork(napi_env env) {
@@ -392,7 +381,7 @@ struct Database {
392
381
  Worker* pendingCloseWorker_;
393
382
  std::set<Iterator*> iterators_;
394
383
  std::set<Updates*> updates_;
395
- std::set<Column*> columns_;
384
+ std::vector<rocksdb::ColumnFamilyHandle*> columns_;
396
385
  napi_ref priorityRef_;
397
386
 
398
387
  private:
@@ -597,39 +586,9 @@ struct Updates {
597
586
  napi_ref ref_ = nullptr;
598
587
  };
599
588
 
600
- struct Column {
601
- Column(Database* database, rocksdb::ColumnFamilyHandle* handle)
602
- : database_(database), handle_(handle) {}
603
-
604
- void Close() {
605
- if (handle_) {
606
- database_->db_->DestroyColumnFamilyHandle(handle_); // TODO (fix): Handle error?
607
- handle_ = nullptr;
608
- }
609
- }
610
-
611
- void Attach(napi_env env, napi_value context) {
612
- napi_create_reference(env, context, 1, &ref_);
613
- database_->AttachColumn(env, this);
614
- }
615
-
616
- void Detach(napi_env env) {
617
- database_->DetachColumn(env, this);
618
- if (ref_) {
619
- napi_delete_reference(env, ref_);
620
- }
621
- }
622
-
623
- Database* database_;
624
- rocksdb::ColumnFamilyHandle* handle_;
625
-
626
- private:
627
- napi_ref ref_ = nullptr;
628
- };
629
-
630
589
  static rocksdb::ColumnFamilyHandle* GetColumnFamily(Database* database, napi_env env, napi_value options) {
631
- auto column_opt = ExternalValueProperty<Column>(env, options, "column");
632
- return column_opt ? (*column_opt)->handle_ : database->db_->DefaultColumnFamily();
590
+ auto column_opt = ExternalValueProperty<rocksdb::ColumnFamilyHandle>(env, options, "column");
591
+ return column_opt ? *column_opt : database->db_->DefaultColumnFamily();
633
592
  }
634
593
 
635
594
  /**
@@ -659,8 +618,7 @@ static void env_cleanup_hook(void* arg) {
659
618
  }
660
619
 
661
620
  for (auto it : database->columns_) {
662
- // TODO: does not do `napi_delete_reference`. Problem?
663
- it->Close();
621
+ database->db_->DestroyColumnFamilyHandle(it);
664
622
  }
665
623
 
666
624
  // Having closed the iterators (and released snapshots) we can safely close.
@@ -695,24 +653,42 @@ struct OpenWorker final : public Worker {
695
653
  Database* database,
696
654
  napi_value callback,
697
655
  const std::string& location,
698
- rocksdb::Options options,
699
- const bool readOnly)
656
+ const rocksdb::Options& options,
657
+ std::vector<rocksdb::ColumnFamilyDescriptor> column_families)
700
658
  : Worker(env, database, callback, "leveldown.db.open"),
701
659
  options_(options),
702
- readOnly_(readOnly),
703
- location_(location) {}
660
+ location_(location),
661
+ column_families_(std::move(column_families)) {}
704
662
 
705
663
  rocksdb::Status Execute(Database& database) override {
706
664
  rocksdb::DB* db = nullptr;
707
- const auto status = readOnly_ ? rocksdb::DB::OpenForReadOnly(options_, location_, &db)
708
- : rocksdb::DB::Open(options_, location_, &db);
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);
709
668
  database.db_.reset(db);
710
669
  return status;
711
670
  }
712
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
+
713
689
  rocksdb::Options options_;
714
- const bool readOnly_;
715
690
  const std::string location_;
691
+ std::vector<rocksdb::ColumnFamilyDescriptor> column_families_;
716
692
  };
717
693
 
718
694
  napi_status InitOptions(napi_env env, auto& options, auto options2) {
@@ -805,26 +781,26 @@ NAPI_METHOD(db_open) {
805
781
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
806
782
 
807
783
  const auto location = ToString(env, argv[1]);
784
+ const auto options = argv[2];
785
+ const auto callback = argv[3];
808
786
 
809
- rocksdb::Options options;
810
-
811
- options.IncreaseParallelism(
812
- Uint32Property(env, argv[2], "parallelism").value_or(std::thread::hardware_concurrency() / 2));
787
+ rocksdb::Options dbOptions;
813
788
 
814
- options.create_if_missing = BooleanProperty(env, argv[2], "createIfMissing").value_or(true);
815
- options.error_if_exists = BooleanProperty(env, argv[2], "errorIfExists").value_or(false);
816
- options.avoid_unnecessary_blocking_io = true;
817
- options.use_adaptive_mutex = BooleanProperty(env, argv[2], "useAdaptiveMutex").value_or(true);
818
- options.enable_pipelined_write = BooleanProperty(env, argv[2], "enablePipelinedWrite").value_or(true);
819
- options.max_background_jobs =
820
- Uint32Property(env, argv[2], "maxBackgroundJobs").value_or(std::thread::hardware_concurrency() / 4);
821
- options.WAL_ttl_seconds = Uint32Property(env, argv[2], "walTTL").value_or(0) / 1e3;
822
- options.WAL_size_limit_MB = Uint32Property(env, argv[2], "walSizeLimit").value_or(0) / 1e6;
789
+ dbOptions.IncreaseParallelism(
790
+ Uint32Property(env, options, "parallelism").value_or(std::thread::hardware_concurrency() / 2));
823
791
 
824
- // TODO: Consider direct IO (https://github.com/facebook/rocksdb/wiki/Direct-IO) once
825
- // secondary compressed cache is stable.
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;
826
802
 
827
- const auto infoLogLevel = StringProperty(env, argv[2], "infoLogLevel").value_or("");
803
+ const auto infoLogLevel = StringProperty(env, options, "infoLogLevel").value_or("");
828
804
  if (infoLogLevel.size() > 0) {
829
805
  rocksdb::InfoLogLevel lvl = {};
830
806
 
@@ -843,21 +819,43 @@ NAPI_METHOD(db_open) {
843
819
  else
844
820
  napi_throw_error(env, nullptr, "invalid log level");
845
821
 
846
- options.info_log_level = lvl;
822
+ dbOptions.info_log_level = lvl;
847
823
  } else {
848
824
  // In some places RocksDB checks this option to see if it should prepare
849
825
  // debug information (ahead of logging), so set it to the highest level.
850
- options.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
851
- options.info_log.reset(new NullLogger());
826
+ dbOptions.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
827
+ dbOptions.info_log.reset(new NullLogger());
852
828
  }
853
829
 
854
- const auto readOnly = BooleanProperty(env, argv[2], "readOnly").value_or(false);
830
+ NAPI_STATUS_THROWS(InitOptions(env, dbOptions, options));
855
831
 
856
- NAPI_STATUS_THROWS(InitOptions(env, options, argv[2]));
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));
857
837
 
858
- const auto callback = argv[3];
838
+ napi_value keys;
839
+ NAPI_STATUS_THROWS(napi_get_property_names(env, columns, &keys));
840
+
841
+ uint32_t len;
842
+ NAPI_STATUS_THROWS(napi_get_array_length(env, keys, &len));
843
+
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));
850
+
851
+ rocksdb::ColumnFamilyOptions columnOptions;
852
+ NAPI_STATUS_THROWS(InitOptions(env, columnOptions, column));
853
+
854
+ column_families.emplace_back(ToString(env, key), columnOptions);
855
+ }
856
+ }
859
857
 
860
- auto worker = new OpenWorker(env, database, callback, location, options, readOnly);
858
+ auto worker = new OpenWorker(env, database, callback, location, dbOptions, column_families);
861
859
  worker->Queue(env);
862
860
 
863
861
  return 0;
@@ -867,7 +865,13 @@ struct CloseWorker final : public Worker {
867
865
  CloseWorker(napi_env env, Database* database, napi_value callback)
868
866
  : Worker(env, database, callback, "leveldown.db.close") {}
869
867
 
870
- rocksdb::Status Execute(Database& database) override { return database.db_->Close(); }
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
+ }
871
875
  };
872
876
 
873
877
  napi_value noop_callback(napi_env env, napi_callback_info info) {
@@ -1491,15 +1495,15 @@ struct NextWorker final : public Worker {
1491
1495
  napi_value result;
1492
1496
  NAPI_STATUS_RETURN(napi_create_array_with_length(env, size, &result));
1493
1497
 
1494
- for (size_t idx = 0; idx < cache_.size(); idx += 2) {
1498
+ for (size_t n = 0; n < size; n += 2) {
1495
1499
  napi_value key;
1496
1500
  napi_value val;
1497
1501
 
1498
- NAPI_STATUS_RETURN(Convert(env, std::move(cache_[idx + 0]), iterator_->keyAsBuffer_, key));
1499
- NAPI_STATUS_RETURN(Convert(env, std::move(cache_[idx + 1]), iterator_->valueAsBuffer_, val));
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));
1500
1504
 
1501
- NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(idx + 0), key));
1502
- NAPI_STATUS_RETURN(napi_set_element(env, result, static_cast<int>(idx + 1), val));
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));
1503
1507
  }
1504
1508
 
1505
1509
  cache_.clear();
@@ -1655,45 +1659,6 @@ NAPI_METHOD(batch_write) {
1655
1659
  return ToError(env, database->db_->Write(writeOptions, batch));
1656
1660
  }
1657
1661
 
1658
- NAPI_METHOD(column_init) {
1659
- NAPI_ARGV(3);
1660
-
1661
- Database* database;
1662
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
1663
-
1664
- NapiSlice name;
1665
- NAPI_STATUS_THROWS(ToNapiSlice(env, argv[1], name));
1666
-
1667
- rocksdb::ColumnFamilyOptions options;
1668
- NAPI_STATUS_THROWS(InitOptions(env, options, argv[2]));
1669
-
1670
- rocksdb::ColumnFamilyHandle* handle;
1671
- ROCKS_STATUS_THROWS(database->db_->CreateColumnFamily(options, name.ToString(), &handle));
1672
-
1673
- auto column = std::make_unique<Column>(database, handle);
1674
-
1675
- napi_value result;
1676
- NAPI_STATUS_THROWS(napi_create_external(env, column.get(), Finalize<Column>, column.get(), &result));
1677
-
1678
- // Prevent GC of JS object before the iterator is closed (explicitly or on
1679
- // db close) and keep track of non-closed iterators to end them on db close.
1680
- column.release()->Attach(env, result);
1681
-
1682
- return result;
1683
- }
1684
-
1685
- NAPI_METHOD(column_close) {
1686
- NAPI_ARGV(1);
1687
-
1688
- Column* column;
1689
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&column));
1690
-
1691
- column->Detach(env);
1692
- column->Close();
1693
-
1694
- return 0;
1695
- }
1696
-
1697
1662
  NAPI_INIT() {
1698
1663
  NAPI_EXPORT_FUNCTION(db_init);
1699
1664
  NAPI_EXPORT_FUNCTION(db_open);
@@ -1705,9 +1670,6 @@ NAPI_INIT() {
1705
1670
  NAPI_EXPORT_FUNCTION(db_clear);
1706
1671
  NAPI_EXPORT_FUNCTION(db_get_property);
1707
1672
 
1708
- NAPI_EXPORT_FUNCTION(column_init);
1709
- NAPI_EXPORT_FUNCTION(column_close);
1710
-
1711
1673
  NAPI_EXPORT_FUNCTION(iterator_init);
1712
1674
  NAPI_EXPORT_FUNCTION(iterator_seek);
1713
1675
  NAPI_EXPORT_FUNCTION(iterator_close);
package/index.js CHANGED
@@ -8,8 +8,8 @@ const { ChainedBatch } = require('./chained-batch')
8
8
  const { Iterator } = require('./iterator')
9
9
 
10
10
  const kContext = Symbol('context')
11
- const kLocation = Symbol('location')
12
11
  const kColumns = Symbol('columns')
12
+ const kLocation = Symbol('location')
13
13
 
14
14
  class RocksLevel extends AbstractLevel {
15
15
  constructor (location, options, _) {
@@ -33,8 +33,6 @@ class RocksLevel extends AbstractLevel {
33
33
  createIfMissing: true,
34
34
  errorIfExists: true,
35
35
  additionalMethods: {
36
- createColumn: true,
37
- closeColumn: true,
38
36
  updates: true,
39
37
  query: true
40
38
  }
@@ -42,29 +40,37 @@ class RocksLevel extends AbstractLevel {
42
40
 
43
41
  this[kLocation] = location
44
42
  this[kContext] = binding.db_init()
45
- this[kColumns] = new Set()
43
+ this[kColumns] = {}
46
44
  }
47
45
 
48
46
  get location () {
49
47
  return this[kLocation]
50
48
  }
51
49
 
50
+ get columns () {
51
+ return this[kColumns]
52
+ }
53
+
52
54
  _open (options, callback) {
55
+ const onOpen = (err, columns) => {
56
+ if (err) {
57
+ callback(err)
58
+ } else {
59
+ this[kColumns] = columns
60
+ callback(null)
61
+ }
62
+ }
53
63
  if (options.createIfMissing) {
54
64
  fs.mkdir(this[kLocation], { recursive: true }, (err) => {
55
65
  if (err) return callback(err)
56
- binding.db_open(this[kContext], this[kLocation], options, callback)
66
+ binding.db_open(this[kContext], this[kLocation], options, onOpen)
57
67
  })
58
68
  } else {
59
- binding.db_open(this[kContext], this[kLocation], options, callback)
69
+ binding.db_open(this[kContext], this[kLocation], options, onOpen)
60
70
  }
61
71
  }
62
72
 
63
73
  _close (callback) {
64
- for (const column of this[kColumns]) {
65
- binding.column_close(column)
66
- }
67
-
68
74
  binding.db_close(this[kContext], callback)
69
75
  }
70
76
 
@@ -115,99 +121,37 @@ class RocksLevel extends AbstractLevel {
115
121
  return binding.db_get_property(this[kContext], property)
116
122
  }
117
123
 
118
- async createColumn (name, options) {
124
+ async query (options) {
119
125
  if (this.status !== 'open') {
120
126
  throw new ModuleError('Database is not open', {
121
127
  code: 'LEVEL_DATABASE_NOT_OPEN'
122
128
  })
123
129
  }
124
130
 
125
- const column = binding.column_init(this[kContext], name, options || {})
126
-
127
- this[kColumns].add(column)
128
-
129
- return column
130
- }
131
-
132
- async * query (options) {
133
- if (this.status !== 'open') {
134
- throw new ModuleError('Database is not open', {
135
- code: 'LEVEL_DATABASE_NOT_OPEN'
136
- })
137
- }
138
-
139
- class Query {
140
- constructor (db, options) {
141
- this.context = binding.iterator_init(db[kContext], options)
142
- this.closed = false
143
- this.promise = null
144
- this.sequence = binding.iterator_get_sequence(this.context)
145
- this.db = db
146
- this.db.attachResource(this)
147
- }
148
-
149
- async next () {
150
- if (this.closed) {
151
- return {}
152
- }
153
-
154
- this.promise = new Promise(resolve => binding.iterator_nextv(this.context, 1000, (err, rows, finished) => {
155
- this.promise = null
156
- if (err) {
157
- resolve(Promise.reject(err))
158
- } else {
159
- resolve({
160
- finished,
161
- rows,
162
- sequence: this.sequence
163
- })
164
- }
165
- }))
166
-
167
- return this.promise
168
- }
169
-
170
- async close (callback) {
171
- try {
172
- await this.promise
173
- } catch {
174
- // Do nothing...
175
- }
176
-
177
- try {
178
- if (!this.closed) {
179
- this.closed = true
180
- binding.iterator_close(this.context)
181
- }
182
-
183
- if (callback) {
184
- process.nextTick(callback)
185
- }
186
- } catch (err) {
187
- if (callback) {
188
- process.nextTick(callback, err)
189
- } else {
190
- throw err
191
- }
192
- } finally {
193
- this.db.detachResource(this)
194
- }
131
+ const context = binding.iterator_init(this[kContext], options)
132
+ const sequence = binding.iterator_get_sequence(context)
133
+ const resource = {
134
+ callback: null,
135
+ close (callback) {
136
+ this.callback = callback
195
137
  }
196
138
  }
197
139
 
198
- const query = new Query(this, options)
199
140
  try {
200
- while (true) {
201
- const { finished, rows, sequence } = await query.next()
202
-
203
- yield { rows, sequence }
204
-
205
- if (finished) {
206
- return
141
+ this.attachResource(resource)
142
+ return await new Promise((resolve, reject) => binding.iterator_nextv(context, options.limit || 1000, (err, rows) => {
143
+ if (err) {
144
+ reject(err)
145
+ } else {
146
+ resolve({ rows, sequence })
207
147
  }
208
- }
148
+ }))
209
149
  } finally {
210
- await query.close()
150
+ this.detachResource(resource)
151
+ binding.iterator_close(this.context)
152
+ if (resource.callback) {
153
+ resource.callback()
154
+ }
211
155
  }
212
156
  }
213
157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/rocksdb",
3
- "version": "7.0.0-alpha.2",
3
+ "version": "7.0.0-alpha.3",
4
4
  "description": "A low-level Node.js RocksDB binding",
5
5
  "license": "MIT",
6
6
  "main": "index.js",