@nxtedition/rocksdb 7.0.63 → 7.0.67

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
@@ -12,11 +12,13 @@
12
12
  #include <rocksdb/filter_policy.h>
13
13
  #include <rocksdb/merge_operator.h>
14
14
  #include <rocksdb/options.h>
15
+ #include <rocksdb/slice.h>
15
16
  #include <rocksdb/slice_transform.h>
17
+ #include <rocksdb/status.h>
16
18
  #include <rocksdb/table.h>
17
19
  #include <rocksdb/write_batch.h>
18
20
 
19
- #include <array>
21
+ #include <iostream>
20
22
  #include <memory>
21
23
  #include <optional>
22
24
  #include <set>
@@ -25,6 +27,7 @@
25
27
  #include <vector>
26
28
 
27
29
  #include "max_rev_operator.h"
30
+ #include "util.h"
28
31
 
29
32
  class NullLogger : public rocksdb::Logger {
30
33
  public:
@@ -37,298 +40,6 @@ struct Database;
37
40
  struct Iterator;
38
41
  struct Updates;
39
42
 
40
- #define NAPI_STATUS_RETURN(call) \
41
- { \
42
- auto _status = (call); \
43
- if (_status != napi_ok) { \
44
- return _status; \
45
- } \
46
- }
47
-
48
- #define ROCKS_STATUS_RETURN(call) \
49
- { \
50
- auto _status = (call); \
51
- if (!_status.ok()) { \
52
- return _status; \
53
- } \
54
- }
55
-
56
- #define ROCKS_STATUS_THROWS(call) \
57
- { \
58
- auto _status = (call); \
59
- if (!_status.ok()) { \
60
- napi_throw(env, ToError(env, _status)); \
61
- return NULL; \
62
- } \
63
- }
64
-
65
- static napi_value CreateError(napi_env env, const std::optional<std::string_view>& code, const std::string_view& msg) {
66
- napi_value codeValue = nullptr;
67
- if (code) {
68
- NAPI_STATUS_THROWS(napi_create_string_utf8(env, code->data(), code->size(), &codeValue));
69
- }
70
- napi_value msgValue;
71
- NAPI_STATUS_THROWS(napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue));
72
- napi_value error;
73
- NAPI_STATUS_THROWS(napi_create_error(env, codeValue, msgValue, &error));
74
- return error;
75
- }
76
-
77
- static bool HasProperty(napi_env env, napi_value obj, const std::string_view& key) {
78
- bool has = false;
79
- napi_has_named_property(env, obj, key.data(), &has);
80
- return has;
81
- }
82
-
83
- static napi_value GetProperty(napi_env env, napi_value obj, const std::string_view& key) {
84
- napi_value value = nullptr;
85
- napi_get_named_property(env, obj, key.data(), &value);
86
- return value;
87
- }
88
-
89
- static std::optional<bool> BooleanProperty(napi_env env, napi_value obj, const std::string_view& key) {
90
- if (HasProperty(env, obj, key.data())) {
91
- const auto value = GetProperty(env, obj, key.data());
92
- bool result;
93
- napi_get_value_bool(env, value, &result);
94
- return result;
95
- }
96
-
97
- return {};
98
- }
99
-
100
- static bool EncodingIsBuffer(napi_env env, napi_value obj, const std::string_view& option) {
101
- napi_value value;
102
- size_t size;
103
-
104
- if (napi_get_named_property(env, obj, option.data(), &value) == napi_ok &&
105
- napi_get_value_string_utf8(env, value, nullptr, 0, &size) == napi_ok) {
106
- // Value is either "buffer" or "utf8" so we can tell them apart just by size
107
- return size == 6;
108
- }
109
-
110
- return false;
111
- }
112
-
113
- static std::optional<uint32_t> Uint32Property(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
- uint32_t result;
117
- napi_get_value_uint32(env, value, &result);
118
- return result;
119
- }
120
-
121
- return {};
122
- }
123
-
124
- static std::optional<int> Int32Property(napi_env env, napi_value obj, const std::string_view& key) {
125
- if (HasProperty(env, obj, key.data())) {
126
- const auto value = GetProperty(env, obj, key.data());
127
- int result;
128
- napi_get_value_int32(env, value, &result);
129
- return result;
130
- }
131
-
132
- return {};
133
- }
134
-
135
- static napi_status ToString(napi_env env, napi_value from, std::string& to) {
136
- napi_valuetype type;
137
- NAPI_STATUS_RETURN(napi_typeof(env, from, &type));
138
-
139
- if (type == napi_string) {
140
- size_t length = 0;
141
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, nullptr, 0, &length));
142
- to.resize(length, '\0');
143
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, &to[0], length + 1, &length));
144
- } else {
145
- bool isBuffer;
146
- NAPI_STATUS_RETURN(napi_is_buffer(env, from, &isBuffer));
147
-
148
- if (isBuffer) {
149
- char* buf = nullptr;
150
- size_t length = 0;
151
- NAPI_STATUS_RETURN(napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length));
152
- to.assign(buf, length);
153
- } else {
154
- return napi_invalid_arg;
155
- }
156
- }
157
-
158
- return napi_ok;
159
- }
160
-
161
- void noop(void* arg1, void* arg2) {}
162
-
163
- // TODO (fix): Should use rocksdb::Slice since "to" cannot outlive "from".
164
- static napi_status ToString(napi_env env, napi_value from, rocksdb::PinnableSlice& to) {
165
- napi_valuetype type;
166
- NAPI_STATUS_RETURN(napi_typeof(env, from, &type));
167
-
168
- if (type == napi_string) {
169
- size_t length = 0;
170
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, nullptr, 0, &length));
171
- to.GetSelf()->resize(length, '\0');
172
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, &(*to.GetSelf())[0], length + 1, &length));
173
- to.PinSelf();
174
- } else {
175
- bool isBuffer;
176
- NAPI_STATUS_RETURN(napi_is_buffer(env, from, &isBuffer));
177
-
178
- if (isBuffer) {
179
- char* buf = nullptr;
180
- size_t length = 0;
181
- NAPI_STATUS_RETURN(napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length));
182
-
183
- // TODO (fix): Should extend life of "from". Or "to" should be a non-pinnable slice.
184
- to.PinSlice(rocksdb::Slice(buf, length), noop, nullptr, nullptr);
185
- } else {
186
- return napi_invalid_arg;
187
- }
188
- }
189
-
190
- return napi_ok;
191
- }
192
-
193
- static std::optional<std::string> StringProperty(napi_env env, napi_value opts, const std::string_view& name) {
194
- if (HasProperty(env, opts, name)) {
195
- const auto property = GetProperty(env, opts, name);
196
- std::string value;
197
- ToString(env, property, value);
198
- return value;
199
- }
200
- return {};
201
- }
202
-
203
- static napi_status CallFunction(napi_env env, napi_value callback, const int argc, napi_value* argv) {
204
- napi_value global;
205
-
206
- NAPI_STATUS_RETURN(napi_get_global(env, &global));
207
- NAPI_STATUS_RETURN(napi_call_function(env, global, callback, argc, argv, nullptr));
208
-
209
- return napi_ok;
210
- }
211
-
212
- static napi_value ToError(napi_env env, const rocksdb::Status& status) {
213
- if (status.ok()) {
214
- return 0;
215
- }
216
-
217
- const auto msg = status.ToString();
218
-
219
- if (status.IsNotFound()) {
220
- return CreateError(env, "LEVEL_NOT_FOUND", msg);
221
- } else if (status.IsCorruption()) {
222
- return CreateError(env, "LEVEL_CORRUPTION", msg);
223
- } else if (status.IsTryAgain()) {
224
- return CreateError(env, "LEVEL_TRYAGAIN", msg);
225
- } else if (status.IsIOError()) {
226
- if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
227
- return CreateError(env, "LEVEL_LOCKED", msg);
228
- } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
229
- return CreateError(env, "LEVEL_LOCKED", msg);
230
- } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
231
- return CreateError(env, "LEVEL_LOCKED", msg);
232
- } else {
233
- return CreateError(env, "LEVEL_IO_ERROR", msg);
234
- }
235
- }
236
-
237
- return CreateError(env, {}, msg);
238
- }
239
-
240
- template <typename T>
241
- static void Finalize(napi_env env, void* data, void* hint) {
242
- if (hint) {
243
- delete reinterpret_cast<T*>(hint);
244
- }
245
- }
246
-
247
- template <typename T>
248
- napi_status Convert(napi_env env, T&& s, bool asBuffer, napi_value& result) {
249
- if (!s) {
250
- return napi_get_null(env, &result);
251
- } else if (asBuffer) {
252
- using Y = typename std::decay<decltype(*s)>::type;
253
- auto ptr = new Y(std::move(*s));
254
- return napi_create_external_buffer(env, ptr->size(), const_cast<char*>(ptr->data()), Finalize<Y>, ptr, &result);
255
- } else {
256
- return napi_create_string_utf8(env, s->data(), s->size(), &result);
257
- }
258
- }
259
-
260
- /**
261
- * Base worker class. Handles the async work. Derived classes can override the
262
- * following virtual methods (listed in the order in which they're called):
263
- *
264
- * - Execute (abstract, worker pool thread): main work
265
- * - OnOk (main thread): call JS callback on success
266
- * - OnError (main thread): call JS callback on error
267
- * - Destroy (main thread): do cleanup regardless of success
268
- */
269
- struct Worker {
270
- Worker(napi_env env, Database* database, napi_value callback, const std::string& resourceName) : database_(database) {
271
- NAPI_STATUS_THROWS_VOID(napi_create_reference(env, callback, 1, &callbackRef_));
272
- napi_value asyncResourceName;
273
- NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName.data(), resourceName.size(), &asyncResourceName));
274
- NAPI_STATUS_THROWS_VOID(
275
- napi_create_async_work(env, callback, asyncResourceName, Worker::Execute, Worker::Complete, this, &asyncWork_));
276
- }
277
-
278
- virtual ~Worker() {}
279
-
280
- static void Execute(napi_env env, void* data) {
281
- auto self = reinterpret_cast<Worker*>(data);
282
- self->status_ = self->Execute(*self->database_);
283
- }
284
-
285
- static void Complete(napi_env env, napi_status status, void* data) {
286
- auto self = reinterpret_cast<Worker*>(data);
287
-
288
- // TODO (fix): napi status handling...
289
-
290
- napi_value callback;
291
- napi_get_reference_value(env, self->callbackRef_, &callback);
292
-
293
- if (self->status_.ok()) {
294
- self->OnOk(env, callback);
295
- } else {
296
- self->OnError(env, callback, ToError(env, self->status_));
297
- }
298
-
299
- self->Destroy(env);
300
-
301
- napi_delete_reference(env, self->callbackRef_);
302
- napi_delete_async_work(env, self->asyncWork_);
303
-
304
- delete self;
305
- }
306
-
307
- virtual rocksdb::Status Execute(Database& database) = 0;
308
-
309
- virtual napi_status OnOk(napi_env env, napi_value callback) {
310
- napi_value argv[1];
311
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
312
-
313
- return CallFunction(env, callback, 1, &argv[0]);
314
- }
315
-
316
- virtual napi_status OnError(napi_env env, napi_value callback, napi_value err) {
317
- return CallFunction(env, callback, 1, &err);
318
- }
319
-
320
- virtual void Destroy(napi_env env) {}
321
-
322
- void Queue(napi_env env) { napi_queue_async_work(env, asyncWork_); }
323
-
324
- Database* database_;
325
-
326
- private:
327
- napi_ref callbackRef_;
328
- napi_async_work asyncWork_;
329
- rocksdb::Status status_;
330
- };
331
-
332
43
  struct ColumnFamily {
333
44
  napi_ref ref;
334
45
  napi_value val;
@@ -336,80 +47,69 @@ struct ColumnFamily {
336
47
  rocksdb::ColumnFamilyDescriptor descriptor;
337
48
  };
338
49
 
339
- struct Database {
340
- void AttachIterator(napi_env env, Iterator* iterator) {
341
- iterators_.insert(iterator);
342
- IncrementPriorityWork(env);
343
- }
344
-
345
- void DetachIterator(napi_env env, Iterator* iterator) {
346
- iterators_.erase(iterator);
347
- DecrementPriorityWork(env);
348
- }
50
+ struct Closable {
51
+ virtual ~Closable() {}
52
+ virtual rocksdb::Status Close() = 0;
53
+ };
349
54
 
350
- void AttachUpdates(napi_env env, Updates* update) {
351
- updates_.insert(update);
352
- IncrementPriorityWork(env);
353
- }
55
+ struct Database final {
56
+ ~Database() { assert(!db); }
354
57
 
355
- void DetachUpdates(napi_env env, Updates* update) {
356
- updates_.erase(update);
357
- DecrementPriorityWork(env);
358
- }
359
-
360
- void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, priorityRef_, &priorityWork_); }
58
+ rocksdb::Status Close() {
59
+ if (!db) {
60
+ return rocksdb::Status::OK();
61
+ }
361
62
 
362
- void DecrementPriorityWork(napi_env env) {
363
- napi_reference_unref(env, priorityRef_, &priorityWork_);
63
+ for (auto closable : closables) {
64
+ closable->Close();
65
+ }
66
+ closables.clear();
364
67
 
365
- if (priorityWork_ == 0 && pendingCloseWorker_) {
366
- pendingCloseWorker_->Queue(env);
367
- pendingCloseWorker_ = nullptr;
68
+ for (auto& [id, column] : columns) {
69
+ db->DestroyColumnFamilyHandle(column.handle);
368
70
  }
369
- }
71
+ columns.clear();
370
72
 
371
- bool HasPriorityWork() const { return priorityWork_ > 0; }
73
+ db->FlushWAL(true);
372
74
 
373
- std::unique_ptr<rocksdb::DB> db_;
374
- Worker* pendingCloseWorker_;
375
- std::set<Iterator*> iterators_;
376
- std::set<Updates*> updates_;
377
- std::map<int32_t, ColumnFamily> columns_;
378
- napi_ref priorityRef_;
75
+ auto db2 = std::move(db);
76
+ return db2->Close();
77
+ }
379
78
 
380
- private:
381
- uint32_t priorityWork_ = 0;
79
+ std::unique_ptr<rocksdb::DB> db;
80
+ std::set<Closable*> closables;
81
+ std::map<int32_t, ColumnFamily> columns;
382
82
  };
383
83
 
384
84
  enum BatchOp { Empty, Put, Delete, Merge, Data };
385
85
 
386
86
  struct BatchEntry {
387
87
  BatchOp op = BatchOp::Empty;
388
- std::optional<std::string> key;
389
- std::optional<std::string> val;
390
- std::optional<ColumnFamily> column;
88
+ std::optional<std::string> key = std::nullopt;
89
+ std::optional<std::string> val = std::nullopt;
90
+ std::optional<ColumnFamily> column = std::nullopt;
391
91
  };
392
92
 
393
93
  struct BatchIterator : public rocksdb::WriteBatch::Handler {
394
94
  BatchIterator(Database* database,
395
- bool keys = true,
396
- bool values = true,
397
- bool data = true,
398
- const rocksdb::ColumnFamilyHandle* column = nullptr,
399
- bool keyAsBuffer = false,
400
- bool valueAsBuffer = false)
95
+ const bool keys,
96
+ const bool values,
97
+ const bool data,
98
+ const rocksdb::ColumnFamilyHandle* column,
99
+ const Encoding keyEncoding,
100
+ const Encoding valueEncoding)
401
101
  : database_(database),
402
102
  keys_(keys),
403
103
  values_(values),
404
104
  data_(data),
405
105
  column_(column),
406
- keyAsBuffer_(keyAsBuffer),
407
- valueAsBuffer_(valueAsBuffer) {}
106
+ keyEncoding_(keyEncoding),
107
+ valueEncoding_(valueEncoding) {}
408
108
 
409
109
  napi_status Iterate(napi_env env, const rocksdb::WriteBatch& batch, napi_value* result) {
410
110
  cache_.reserve(batch.Count());
411
111
 
412
- batch.Iterate(this); // TODO (fix): Handle error?
112
+ ROCKS_STATUS_RETURN_NAPI(batch.Iterate(this));
413
113
 
414
114
  napi_value putStr;
415
115
  NAPI_STATUS_RETURN(napi_create_string_utf8(env, "put", NAPI_AUTO_LENGTH, &putStr));
@@ -444,11 +144,11 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
444
144
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 0, op));
445
145
 
446
146
  napi_value key;
447
- NAPI_STATUS_RETURN(Convert(env, cache_[n].key, keyAsBuffer_, key));
147
+ NAPI_STATUS_RETURN(Convert(env, cache_[n].key, keyEncoding_, key));
448
148
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 1, key));
449
149
 
450
150
  napi_value val;
451
- NAPI_STATUS_RETURN(Convert(env, cache_[n].val, valueAsBuffer_, val));
151
+ NAPI_STATUS_RETURN(Convert(env, cache_[n].val, valueEncoding_, val));
452
152
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 2, val));
453
153
 
454
154
  // TODO (fix)
@@ -466,9 +166,7 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
466
166
  return rocksdb::Status::OK();
467
167
  }
468
168
 
469
- BatchEntry entry;
470
-
471
- entry.op = BatchOp::Put;
169
+ BatchEntry entry = {BatchOp::Put};
472
170
 
473
171
  if (keys_) {
474
172
  entry.key = key.ToStringView();
@@ -478,8 +176,8 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
478
176
  entry.val = value.ToStringView();
479
177
  }
480
178
 
481
- // if (database_ && database_->columns_.find(column_family_id) != database_->columns_.end()) {
482
- // entry.column = database_->columns_[column_family_id];
179
+ // if (database_ && database_->columns.find(column_family_id) != database_->columns.end()) {
180
+ // entry.column = database_->columns[column_family_id];
483
181
  // }
484
182
 
485
183
  cache_.push_back(entry);
@@ -492,16 +190,14 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
492
190
  return rocksdb::Status::OK();
493
191
  }
494
192
 
495
- BatchEntry entry;
496
-
497
- entry.op = BatchOp::Delete;
193
+ BatchEntry entry = {BatchOp::Delete};
498
194
 
499
195
  if (keys_) {
500
196
  entry.key = key.ToStringView();
501
197
  }
502
198
 
503
- // if (database_ && database_->columns_.find(column_family_id) != database_->columns_.end()) {
504
- // entry.column = database_->columns_[column_family_id];
199
+ // if (database_ && database_->columns.find(column_family_id) != database_->columns.end()) {
200
+ // entry.column = database_->columns[column_family_id];
505
201
  // }
506
202
 
507
203
  cache_.push_back(entry);
@@ -514,9 +210,7 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
514
210
  return rocksdb::Status::OK();
515
211
  }
516
212
 
517
- BatchEntry entry;
518
-
519
- entry.op = BatchOp::Merge;
213
+ BatchEntry entry = {BatchOp::Merge};
520
214
 
521
215
  if (keys_) {
522
216
  entry.key = key.ToStringView();
@@ -526,8 +220,8 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
526
220
  entry.val = value.ToStringView();
527
221
  }
528
222
 
529
- // if (database_ && database_->columns_.find(column_family_id) != database_->columns_.end()) {
530
- // entry.column = database_->columns_[column_family_id];
223
+ // if (database_ && database_->columns.find(column_family_id) != database_->columns.end()) {
224
+ // entry.column = database_->columns[column_family_id];
531
225
  // }
532
226
 
533
227
  cache_.push_back(entry);
@@ -540,9 +234,7 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
540
234
  return;
541
235
  }
542
236
 
543
- BatchEntry entry;
544
-
545
- entry.op = BatchOp::Data;
237
+ BatchEntry entry = {BatchOp::Data};
546
238
 
547
239
  entry.val = data.ToStringView();
548
240
 
@@ -553,40 +245,47 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
553
245
 
554
246
  private:
555
247
  Database* database_;
556
- bool keys_;
557
- bool values_;
558
- bool data_;
248
+ const bool keys_;
249
+ const bool values_;
250
+ const bool data_;
559
251
  const rocksdb::ColumnFamilyHandle* column_;
560
- bool keyAsBuffer_;
561
- bool valueAsBuffer_;
252
+ const Encoding keyEncoding_;
253
+ const Encoding valueEncoding_;
562
254
  std::vector<BatchEntry> cache_;
563
255
  };
564
256
 
565
- struct Updates : public BatchIterator {
257
+ struct Updates : public BatchIterator, public Closable {
566
258
  Updates(Database* database,
567
- int64_t seqNumber,
568
- bool keys,
569
- bool values,
570
- bool data,
259
+ const int64_t seqNumber,
260
+ const bool keys,
261
+ const bool values,
262
+ const bool data,
571
263
  const rocksdb::ColumnFamilyHandle* column,
572
- bool keyAsBuffer,
573
- bool valueAsBuffer)
574
- : BatchIterator(database, keys, values, data, column, keyAsBuffer, valueAsBuffer),
264
+ const Encoding keyEncoding,
265
+ const Encoding valueEncoding)
266
+ : BatchIterator(database, keys, values, data, column, keyEncoding, valueEncoding),
575
267
  database_(database),
576
268
  start_(seqNumber) {}
577
269
 
578
- void Close() { iterator_.reset(); }
270
+ virtual ~Updates() { assert(!iterator_); }
579
271
 
580
- void Attach(napi_env env, napi_value context) {
581
- napi_create_reference(env, context, 1, &ref_);
582
- database_->AttachUpdates(env, this);
272
+ rocksdb::Status Close() override {
273
+ iterator_.reset();
274
+ return rocksdb::Status::OK();
583
275
  }
584
276
 
585
- void Detach(napi_env env) {
586
- database_->DetachUpdates(env, this);
277
+ napi_status Attach(napi_env env, napi_value context) {
278
+ NAPI_STATUS_RETURN(napi_create_reference(env, context, 1, &ref_));
279
+ database_->closables.insert(this);
280
+ return napi_ok;
281
+ }
282
+
283
+ napi_status Detach(napi_env env) {
284
+ database_->closables.erase(this);
587
285
  if (ref_) {
588
- napi_delete_reference(env, ref_);
286
+ NAPI_STATUS_RETURN(napi_delete_reference(env, ref_));
589
287
  }
288
+ return napi_ok;
590
289
  }
591
290
 
592
291
  Database* database_;
@@ -597,7 +296,7 @@ struct Updates : public BatchIterator {
597
296
  napi_ref ref_ = nullptr;
598
297
  };
599
298
 
600
- struct BaseIterator {
299
+ struct BaseIterator : public Closable {
601
300
  BaseIterator(Database* database,
602
301
  rocksdb::ColumnFamilyHandle* column,
603
302
  const bool reverse,
@@ -670,9 +369,10 @@ struct BaseIterator {
670
369
  }
671
370
  }
672
371
 
673
- void Close() {
372
+ rocksdb::Status Close() override {
674
373
  snapshot_.reset();
675
374
  iterator_.reset();
375
+ return rocksdb::Status::OK();
676
376
  }
677
377
 
678
378
  bool Valid() const {
@@ -727,7 +427,7 @@ struct BaseIterator {
727
427
  readOptions.async_io = true;
728
428
  readOptions.adaptive_readahead = true;
729
429
 
730
- iterator_.reset(database_->db_->NewIterator(readOptions, column_));
430
+ iterator_.reset(database_->db->NewIterator(readOptions, column_));
731
431
  }
732
432
 
733
433
  int count_ = 0;
@@ -751,33 +451,35 @@ struct Iterator final : public BaseIterator {
751
451
  const std::optional<std::string>& gt,
752
452
  const std::optional<std::string>& gte,
753
453
  const bool fillCache,
754
- const bool keyAsBuffer,
755
- const bool valueAsBuffer,
454
+ const Encoding keyEncoding,
455
+ const Encoding valueEncoding,
756
456
  const size_t highWaterMarkBytes,
757
457
  std::shared_ptr<const rocksdb::Snapshot> snapshot)
758
458
  : BaseIterator(database, column, reverse, lt, lte, gt, gte, limit, fillCache, snapshot),
759
459
  keys_(keys),
760
460
  values_(values),
761
- keyAsBuffer_(keyAsBuffer),
762
- valueAsBuffer_(valueAsBuffer),
461
+ keyEncoding_(keyEncoding),
462
+ valueEncoding_(valueEncoding),
763
463
  highWaterMarkBytes_(highWaterMarkBytes) {}
764
464
 
765
- void Attach(napi_env env, napi_value context) {
766
- napi_create_reference(env, context, 1, &ref_);
767
- database_->AttachIterator(env, this);
465
+ napi_status Attach(napi_env env, napi_value context) {
466
+ NAPI_STATUS_RETURN(napi_create_reference(env, context, 1, &ref_));
467
+ database_->closables.insert(this);
468
+ return napi_ok;
768
469
  }
769
470
 
770
- void Detach(napi_env env) {
771
- database_->DetachIterator(env, this);
471
+ napi_status Detach(napi_env env) {
472
+ database_->closables.erase(this);
772
473
  if (ref_) {
773
- napi_delete_reference(env, ref_);
474
+ NAPI_STATUS_RETURN(napi_delete_reference(env, ref_));
774
475
  }
476
+ return napi_ok;
775
477
  }
776
478
 
777
479
  const bool keys_;
778
480
  const bool values_;
779
- const bool keyAsBuffer_;
780
- const bool valueAsBuffer_;
481
+ const Encoding keyEncoding_;
482
+ const Encoding valueEncoding_;
781
483
  const size_t highWaterMarkBytes_;
782
484
  bool first_ = true;
783
485
 
@@ -785,152 +487,20 @@ struct Iterator final : public BaseIterator {
785
487
  napi_ref ref_ = nullptr;
786
488
  };
787
489
 
788
- static napi_status GetColumnFamily(Database* database,
789
- napi_env env,
790
- napi_value options,
791
- rocksdb::ColumnFamilyHandle** column) {
792
- bool hasColumn = false;
793
- NAPI_STATUS_RETURN(napi_has_named_property(env, options, "column", &hasColumn));
794
-
795
- if (hasColumn) {
796
- napi_value value = nullptr;
797
- NAPI_STATUS_RETURN(napi_get_named_property(env, options, "column", &value));
798
-
799
- bool nully = false;
800
-
801
- napi_value nullVal;
802
- NAPI_STATUS_RETURN(napi_get_null(env, &nullVal));
803
- NAPI_STATUS_RETURN(napi_strict_equals(env, nullVal, value, &nully));
804
- if (nully) {
805
- *column = nullptr;
806
- return napi_ok;
807
- }
808
-
809
- napi_value undefinedVal;
810
- NAPI_STATUS_RETURN(napi_get_undefined(env, &undefinedVal));
811
- NAPI_STATUS_RETURN(napi_strict_equals(env, undefinedVal, value, &nully));
812
- if (nully) {
813
- *column = nullptr;
814
- return napi_ok;
815
- }
816
-
817
- NAPI_STATUS_RETURN(napi_get_value_external(env, value, reinterpret_cast<void**>(column)));
818
- } else if (database) {
819
- *column = database->db_->DefaultColumnFamily();
820
- } else {
821
- *column = nullptr;
822
- }
823
-
824
- return napi_ok;
825
- }
826
-
827
- template <typename State,
828
- typename ExecuteType = std::function<rocksdb::Status(State& state, Database& database)>,
829
- typename OnOkType = std::function<napi_status(State& state, napi_env env, napi_value callback)>>
830
- struct GenericWorker final : public Worker {
831
- template <typename T1, typename T2>
832
- GenericWorker(const std::string& name,
833
- napi_env env,
834
- napi_value callback,
835
- Database* database,
836
- bool priority,
837
- T1&& execute,
838
- T2&& onOK)
839
- : Worker(env, database, callback, name),
840
- priority_(priority),
841
- execute_(std::forward<T1>(execute)),
842
- onOk_(std::forward<T2>(onOK)) {
843
- if (priority_) {
844
- database_->IncrementPriorityWork(env);
845
- }
846
- }
847
-
848
- rocksdb::Status Execute(Database& database) override { return execute_(state_, database); }
849
-
850
- napi_status OnOk(napi_env env, napi_value callback) override { return onOk_(state_, env, callback); }
851
-
852
- void Destroy(napi_env env) override {
853
- if (priority_) {
854
- database_->DecrementPriorityWork(env);
855
- }
856
- Worker::Destroy(env);
857
- }
858
-
859
- private:
860
- State state_;
861
- bool priority_ = false;
862
- ExecuteType execute_;
863
- OnOkType onOk_;
864
- };
865
-
866
- template <typename State, typename T1, typename T2>
867
- Worker* createWorker(const std::string& name,
868
- napi_env env,
869
- napi_value callback,
870
- Database* database,
871
- bool priority,
872
- T1&& execute,
873
- T2&& onOk) {
874
- return new GenericWorker<State, typename std::decay<T1>::type, typename std::decay<T2>::type>(
875
- name, env, callback, database, priority, std::forward<T1>(execute), std::forward<T2>(onOk));
876
- }
877
-
878
- template <typename State, typename T1, typename T2>
879
- void runWorker(const std::string& name,
880
- napi_env env,
881
- napi_value callback,
882
- Database* database,
883
- bool priority,
884
- T1&& execute,
885
- T2&& onOk) {
886
- auto worker = new GenericWorker<State, typename std::decay<T1>::type, typename std::decay<T2>::type>(
887
- name, env, callback, database, priority, std::forward<T1>(execute), std::forward<T2>(onOk));
888
- worker->Queue(env);
889
- }
890
-
891
- /**
892
- * Hook for when the environment exits. This hook will be called after
893
- * already-scheduled napi_async_work items have finished, which gives us
894
- * the guarantee that no db operations will be in-flight at this time.
895
- */
896
490
  static void env_cleanup_hook(void* arg) {
897
491
  auto database = reinterpret_cast<Database*>(arg);
898
-
899
- // Do everything that db_close() does but synchronously. We're expecting that
900
- // GC did not (yet) collect the database because that would be a user mistake
901
- // (not closing their db) made during the lifetime of the environment. That's
902
- // different from an environment being torn down (like the main process or a
903
- // worker thread) where it's our responsibility to clean up. Note also, the
904
- // following code must be a safe noop if called before db_open() or after
905
- // db_close().
906
- if (database && database->db_) {
907
- for (auto& it : database->iterators_) {
908
- // TODO: does not do `napi_delete_reference`. Problem?
909
- it->Close();
910
- }
911
-
912
- for (auto& it : database->updates_) {
913
- // TODO: does not do `napi_delete_reference`. Problem?
914
- it->Close();
915
- }
916
-
917
- for (auto& it : database->columns_) {
918
- database->db_->DestroyColumnFamilyHandle(it.second.handle);
919
- }
920
-
921
- // Having closed the iterators (and released snapshots) we can safely close.
922
- database->db_->Close();
492
+ if (database) {
493
+ database->Close();
923
494
  }
924
495
  }
925
496
 
926
497
  static void FinalizeDatabase(napi_env env, void* data, void* hint) {
927
498
  if (data) {
928
499
  auto database = reinterpret_cast<Database*>(data);
500
+ database->Close();
929
501
  napi_remove_env_cleanup_hook(env, env_cleanup_hook, database);
930
- if (database->priorityRef_)
931
- napi_delete_reference(env, database->priorityRef_);
932
- for (auto& it : database->columns_) {
933
- napi_delete_reference(env, it.second.ref);
502
+ for (auto& [id, column] : database->columns) {
503
+ napi_delete_reference(env, column.ref);
934
504
  }
935
505
  delete database;
936
506
  }
@@ -943,94 +513,113 @@ NAPI_METHOD(db_init) {
943
513
  napi_value result;
944
514
  NAPI_STATUS_THROWS(napi_create_external(env, database, FinalizeDatabase, nullptr, &result));
945
515
 
946
- NAPI_STATUS_THROWS(napi_create_reference(env, result, 0, &database->priorityRef_));
947
-
948
516
  return result;
949
517
  }
950
518
 
951
519
  template <typename T, typename U>
952
- rocksdb::Status InitOptions(napi_env env, T& columnOptions, const U& options) {
520
+ napi_status InitOptions(napi_env env, T& columnOptions, const U& options) {
953
521
  rocksdb::ConfigOptions configOptions;
954
522
 
955
- const auto memtable_memory_budget = Uint32Property(env, options, "memtableMemoryBudget").value_or(256 * 1024 * 1024);
956
-
957
- const auto compaction = StringProperty(env, options, "compaction").value_or("level");
958
-
959
- if (compaction == "universal") {
960
- columnOptions.write_buffer_size = static_cast<size_t>(memtable_memory_budget / 4);
961
- // merge two memtables when flushing to L0
962
- columnOptions.min_write_buffer_number_to_merge = 2;
963
- // this means we'll use 50% extra memory in the worst case, but will reduce
964
- // write stalls.
965
- columnOptions.max_write_buffer_number = 6;
966
- // universal style compaction
967
- columnOptions.compaction_style = rocksdb::kCompactionStyleUniversal;
968
- columnOptions.compaction_options_universal.compression_size_percent = 80;
969
- } else {
970
- // merge two memtables when flushing to L0
971
- columnOptions.min_write_buffer_number_to_merge = 2;
972
- // this means we'll use 50% extra memory in the worst case, but will reduce
973
- // write stalls.
974
- columnOptions.max_write_buffer_number = 6;
975
- // start flushing L0->L1 as soon as possible. each file on level0 is
976
- // (memtable_memory_budget / 2). This will flush level 0 when it's bigger than
977
- // memtable_memory_budget.
978
- columnOptions.level0_file_num_compaction_trigger = 2;
979
- // doesn't really matter much, but we don't want to create too many files
980
- columnOptions.target_file_size_base = memtable_memory_budget / 8;
981
- // make Level1 size equal to Level0 size, so that L0->L1 compactions are fast
982
- columnOptions.max_bytes_for_level_base = memtable_memory_budget;
983
-
984
- // level style compaction
985
- columnOptions.compaction_style = rocksdb::kCompactionStyleLevel;
986
-
987
- // TODO (perf): only compress levels >= 2
523
+ uint64_t memtable_memory_budget = 256 * 1024 * 1024;
524
+ NAPI_STATUS_RETURN(GetProperty(env, options, "memtableMemoryBudget", memtable_memory_budget));
525
+
526
+ std::optional<std::string> compactionOpt;
527
+ NAPI_STATUS_RETURN(GetProperty(env, options, "compaction", compactionOpt));
528
+ if (compactionOpt) {
529
+ if (*compactionOpt == "universal") {
530
+ columnOptions.write_buffer_size = memtable_memory_budget / 4;
531
+ // merge two memtables when flushing to L0
532
+ columnOptions.min_write_buffer_number_to_merge = 2;
533
+ // this means we'll use 50% extra memory in the worst case, but will reduce
534
+ // write stalls.
535
+ columnOptions.max_write_buffer_number = 6;
536
+ // universal style compaction
537
+ columnOptions.compaction_style = rocksdb::kCompactionStyleUniversal;
538
+ columnOptions.compaction_options_universal.compression_size_percent = 80;
539
+ } else if (*compactionOpt == "level") {
540
+ // merge two memtables when flushing to L0
541
+ columnOptions.min_write_buffer_number_to_merge = 2;
542
+ // this means we'll use 50% extra memory in the worst case, but will reduce
543
+ // write stalls.
544
+ columnOptions.max_write_buffer_number = 6;
545
+ // start flushing L0->L1 as soon as possible. each file on level0 is
546
+ // (memtable_memory_budget / 2). This will flush level 0 when it's bigger than
547
+ // memtable_memory_budget.
548
+ columnOptions.level0_file_num_compaction_trigger = 2;
549
+ // doesn't really matter much, but we don't want to create too many files
550
+ columnOptions.target_file_size_base = memtable_memory_budget / 8;
551
+ // make Level1 size equal to Level0 size, so that L0->L1 compactions are fast
552
+ columnOptions.max_bytes_for_level_base = memtable_memory_budget;
553
+
554
+ // level style compaction
555
+ columnOptions.compaction_style = rocksdb::kCompactionStyleLevel;
556
+
557
+ // only compress levels >= 2
558
+ columnOptions.compression_per_level.resize(columnOptions.num_levels);
559
+ for (int i = 0; i < columnOptions.num_levels; ++i) {
560
+ if (i < 2) {
561
+ columnOptions.compression_per_level[i] = rocksdb::kNoCompression;
562
+ } else {
563
+ columnOptions.compression_per_level[i] = rocksdb::kZSTD;
564
+ }
565
+ }
566
+ } else {
567
+ return napi_invalid_arg;
568
+ }
988
569
  }
989
570
 
990
- columnOptions.compression =
991
- BooleanProperty(env, options, "compression").value_or((true)) ? rocksdb::kZSTD : rocksdb::kNoCompression;
992
- if (columnOptions.compression == rocksdb::kZSTD) {
571
+ bool compression = true;
572
+ NAPI_STATUS_RETURN(GetProperty(env, options, "compression", compression));
573
+
574
+ if (compression) {
575
+ columnOptions.compression = rocksdb::kZSTD;
993
576
  columnOptions.compression_opts.max_dict_bytes = 16 * 1024;
994
577
  columnOptions.compression_opts.zstd_max_train_bytes = 16 * 1024 * 100;
995
578
  // TODO (perf): compression_opts.parallel_threads
996
579
  }
997
580
 
998
- const auto prefixExtractorOpt = StringProperty(env, options, "prefixExtractor");
581
+ std::optional<std::string> prefixExtractorOpt;
582
+ NAPI_STATUS_RETURN(GetProperty(env, options, "prefixExtractor", prefixExtractorOpt));
999
583
  if (prefixExtractorOpt) {
1000
- ROCKS_STATUS_RETURN(
584
+ ROCKS_STATUS_RETURN_NAPI(
1001
585
  rocksdb::SliceTransform::CreateFromString(configOptions, *prefixExtractorOpt, &columnOptions.prefix_extractor));
1002
586
  }
1003
587
 
1004
- const auto comparatorOpt = StringProperty(env, options, "comparator");
588
+ std::optional<std::string> comparatorOpt;
589
+ NAPI_STATUS_RETURN(GetProperty(env, options, "comparator", comparatorOpt));
1005
590
  if (comparatorOpt) {
1006
- ROCKS_STATUS_RETURN(
591
+ ROCKS_STATUS_RETURN_NAPI(
1007
592
  rocksdb::Comparator::CreateFromString(configOptions, *comparatorOpt, &columnOptions.comparator));
1008
593
  }
1009
594
 
1010
- const auto mergeOperatorOpt = StringProperty(env, options, "mergeOperator");
595
+ std::optional<std::string> mergeOperatorOpt;
596
+ NAPI_STATUS_RETURN(GetProperty(env, options, "mergeOperator", mergeOperatorOpt));
1011
597
  if (mergeOperatorOpt) {
1012
598
  if (*mergeOperatorOpt == "maxRev") {
1013
599
  columnOptions.merge_operator = std::make_shared<MaxRevOperator>();
1014
600
  } else {
1015
- ROCKS_STATUS_RETURN(
601
+ ROCKS_STATUS_RETURN_NAPI(
1016
602
  rocksdb::MergeOperator::CreateFromString(configOptions, *mergeOperatorOpt, &columnOptions.merge_operator));
1017
603
  }
1018
604
  }
1019
605
 
1020
- const auto cacheSize = Uint32Property(env, options, "cacheSize").value_or(8 << 20);
606
+ uint32_t cacheSize = 8 << 20;
607
+ NAPI_STATUS_RETURN(GetProperty(env, options, "cacheSize", cacheSize));
1021
608
 
1022
609
  rocksdb::BlockBasedTableOptions tableOptions;
1023
610
 
1024
611
  if (cacheSize) {
1025
612
  tableOptions.block_cache = rocksdb::NewLRUCache(cacheSize);
1026
- tableOptions.cache_index_and_filter_blocks =
1027
- BooleanProperty(env, options, "cacheIndexAndFilterBlocks").value_or(true);
613
+ tableOptions.cache_index_and_filter_blocks = true;
614
+ NAPI_STATUS_RETURN(
615
+ GetProperty(env, options, "cacheIndexAndFilterBlocks", tableOptions.cache_index_and_filter_blocks));
1028
616
  } else {
1029
617
  tableOptions.no_block_cache = true;
1030
618
  tableOptions.cache_index_and_filter_blocks = false;
1031
619
  }
1032
620
 
1033
- const auto optimize = StringProperty(env, options, "optimize").value_or("");
621
+ std::string optimize = "";
622
+ NAPI_STATUS_RETURN(GetProperty(env, options, "optimize", optimize));
1034
623
 
1035
624
  if (optimize == "point-lookup") {
1036
625
  tableOptions.data_block_index_type = rocksdb::BlockBasedTableOptions::kDataBlockBinaryAndHash;
@@ -1042,25 +631,29 @@ rocksdb::Status InitOptions(napi_env env, T& columnOptions, const U& options) {
1042
631
  } else if (optimize == "range-lookup") {
1043
632
  // TODO?
1044
633
  } else {
1045
- tableOptions.filter_policy.reset(rocksdb::NewRibbonFilterPolicy(10));
634
+ tableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10));
1046
635
  }
1047
636
 
1048
- const auto filterPolicyOpt = StringProperty(env, options, "filterPolicy");
637
+ std::optional<std::string> filterPolicyOpt;
638
+ NAPI_STATUS_RETURN(GetProperty(env, options, "filterPolicy", filterPolicyOpt));
1049
639
  if (filterPolicyOpt) {
1050
- rocksdb::ConfigOptions configOptions;
1051
- ROCKS_STATUS_RETURN(
640
+ ROCKS_STATUS_RETURN_NAPI(
1052
641
  rocksdb::FilterPolicy::CreateFromString(configOptions, *filterPolicyOpt, &tableOptions.filter_policy));
1053
642
  }
1054
643
 
1055
- tableOptions.block_size = Uint32Property(env, options, "blockSize").value_or(4096);
1056
- tableOptions.block_restart_interval = Uint32Property(env, options, "blockRestartInterval").value_or(16);
644
+ NAPI_STATUS_RETURN(GetProperty(env, options, "blockSize", tableOptions.block_size));
645
+
646
+ NAPI_STATUS_RETURN(GetProperty(env, options, "blockRestartInterval", tableOptions.block_restart_interval));
647
+
1057
648
  tableOptions.format_version = 5;
1058
649
  tableOptions.checksum = rocksdb::kXXH3;
1059
- tableOptions.optimize_filters_for_memory = BooleanProperty(env, options, "optimizeFiltersForMemory").value_or(true);
650
+
651
+ tableOptions.optimize_filters_for_memory = true;
652
+ NAPI_STATUS_RETURN(GetProperty(env, options, "optimizeFiltersForMemory", tableOptions.optimize_filters_for_memory));
1060
653
 
1061
654
  columnOptions.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tableOptions));
1062
655
 
1063
- return rocksdb::Status::OK();
656
+ return napi_ok;
1064
657
  }
1065
658
 
1066
659
  NAPI_METHOD(db_open) {
@@ -1070,32 +663,44 @@ NAPI_METHOD(db_open) {
1070
663
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1071
664
 
1072
665
  std::string location;
1073
- NAPI_STATUS_THROWS(ToString(env, argv[1], location));
666
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], location));
1074
667
 
1075
668
  rocksdb::Options dbOptions;
1076
669
 
1077
- dbOptions.IncreaseParallelism(Uint32Property(env, argv[2], "parallelism")
1078
- .value_or(std::max<uint32_t>(1, std::thread::hardware_concurrency() / 2)));
670
+ const auto options = argv[2];
671
+
672
+ int parallelism = std::max<int>(1, std::thread::hardware_concurrency() / 2);
673
+ NAPI_STATUS_THROWS(GetProperty(env, options, "parallelism", parallelism));
674
+ dbOptions.IncreaseParallelism(parallelism);
675
+
676
+ uint32_t walTTL = 0;
677
+ NAPI_STATUS_THROWS(GetProperty(env, options, "walTTL", walTTL));
678
+ dbOptions.WAL_ttl_seconds = walTTL / 1e3;
679
+
680
+ uint32_t walSizeLimit = 0;
681
+ NAPI_STATUS_THROWS(GetProperty(env, options, "walSizeLimit", walSizeLimit));
682
+ dbOptions.WAL_size_limit_MB = walSizeLimit / 1e6;
683
+
684
+ bool walCompression = false;
685
+ NAPI_STATUS_THROWS(GetProperty(env, options, "walCompression", walCompression));
686
+ dbOptions.wal_compression =
687
+ walCompression ? rocksdb::CompressionType::kZSTD : rocksdb::CompressionType::kNoCompression;
1079
688
 
1080
- dbOptions.create_if_missing = BooleanProperty(env, argv[2], "createIfMissing").value_or(true);
1081
- dbOptions.error_if_exists = BooleanProperty(env, argv[2], "errorIfExists").value_or(false);
1082
689
  dbOptions.avoid_unnecessary_blocking_io = true;
1083
690
  dbOptions.write_dbid_to_manifest = true;
1084
691
  dbOptions.use_adaptive_mutex = true; // We don't have soo many threads in the libuv thread pool...
1085
692
  dbOptions.enable_pipelined_write = false; // We only write in the main thread...
1086
- dbOptions.WAL_ttl_seconds = Uint32Property(env, argv[2], "walTTL").value_or(0) / 1e3;
1087
- dbOptions.WAL_size_limit_MB = Uint32Property(env, argv[2], "walSizeLimit").value_or(0) / 1e6;
1088
- dbOptions.wal_compression = BooleanProperty(env, argv[2], "walCompression").value_or(false)
1089
- ? rocksdb::CompressionType::kZSTD
1090
- : rocksdb::CompressionType::kNoCompression;
1091
693
  dbOptions.create_missing_column_families = true;
1092
- dbOptions.unordered_write = BooleanProperty(env, argv[2], "unorderedWrite").value_or(false);
1093
694
  dbOptions.fail_if_options_file_error = true;
1094
- dbOptions.manual_wal_flush = BooleanProperty(env, argv[2], "manualWalFlush").value_or(false);
695
+
696
+ NAPI_STATUS_THROWS(GetProperty(env, options, "createIfMissing", dbOptions.create_if_missing));
697
+ NAPI_STATUS_THROWS(GetProperty(env, options, "errorIfExists", dbOptions.error_if_exists));
698
+ NAPI_STATUS_THROWS(GetProperty(env, options, "unorderedWrite", dbOptions.unordered_write));
1095
699
 
1096
700
  // TODO (feat): dbOptions.listeners
1097
701
 
1098
- const auto infoLogLevel = StringProperty(env, argv[2], "infoLogLevel").value_or("");
702
+ std::string infoLogLevel;
703
+ NAPI_STATUS_THROWS(GetProperty(env, options, "infoLogLevel", infoLogLevel));
1099
704
  if (infoLogLevel.size() > 0) {
1100
705
  rocksdb::InfoLogLevel lvl = {};
1101
706
 
@@ -1122,16 +727,16 @@ NAPI_METHOD(db_open) {
1122
727
  dbOptions.info_log.reset(new NullLogger());
1123
728
  }
1124
729
 
1125
- ROCKS_STATUS_THROWS(InitOptions(env, dbOptions, argv[2]));
730
+ NAPI_STATUS_THROWS(InitOptions(env, dbOptions, options));
1126
731
 
1127
732
  std::vector<rocksdb::ColumnFamilyDescriptor> descriptors;
1128
733
 
1129
734
  bool hasColumns;
1130
- NAPI_STATUS_THROWS(napi_has_named_property(env, argv[2], "columns", &hasColumns));
735
+ NAPI_STATUS_THROWS(napi_has_named_property(env, options, "columns", &hasColumns));
1131
736
 
1132
737
  if (hasColumns) {
1133
738
  napi_value columns;
1134
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "columns", &columns));
739
+ NAPI_STATUS_THROWS(napi_get_named_property(env, options, "columns", &columns));
1135
740
 
1136
741
  napi_value keys;
1137
742
  NAPI_STATUS_THROWS(napi_get_property_names(env, columns, &keys));
@@ -1147,26 +752,25 @@ NAPI_METHOD(db_open) {
1147
752
  napi_value column;
1148
753
  NAPI_STATUS_THROWS(napi_get_property(env, columns, key, &column));
1149
754
 
1150
- ROCKS_STATUS_THROWS(InitOptions(env, descriptors[n].options, column));
755
+ NAPI_STATUS_THROWS(InitOptions(env, descriptors[n].options, column));
1151
756
 
1152
- NAPI_STATUS_THROWS(ToString(env, key, descriptors[n].name));
757
+ NAPI_STATUS_THROWS(GetValue(env, key, descriptors[n].name));
1153
758
  }
1154
759
  }
1155
760
 
1156
761
  auto callback = argv[3];
1157
762
 
1158
- runWorker<std::vector<rocksdb::ColumnFamilyHandle*>>(
1159
- "leveldown.open", env, callback, database, false,
1160
- [=](auto& handles, auto& database) -> rocksdb::Status {
763
+ runAsync<std::vector<rocksdb::ColumnFamilyHandle*>>(
764
+ "leveldown.open", env, callback,
765
+ [=](auto& handles) {
1161
766
  rocksdb::DB* db = nullptr;
1162
767
  const auto status = descriptors.empty() ? rocksdb::DB::Open(dbOptions, location, &db)
1163
768
  : rocksdb::DB::Open(dbOptions, location, descriptors, &handles, &db);
1164
- database.db_.reset(db);
769
+ database->db.reset(db);
1165
770
  return status;
1166
771
  },
1167
- [=](auto& handles, napi_env env, napi_value callback) -> napi_status {
1168
- napi_value argv[2];
1169
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
772
+ [=](auto& handles, auto env, auto& argv) {
773
+ argv.resize(2);
1170
774
 
1171
775
  const auto size = handles.size();
1172
776
  NAPI_STATUS_RETURN(napi_create_object(env, &argv[1]));
@@ -1177,13 +781,11 @@ NAPI_METHOD(db_open) {
1177
781
  column.descriptor = descriptors[n];
1178
782
  NAPI_STATUS_RETURN(napi_create_external(env, column.handle, nullptr, nullptr, &column.val));
1179
783
  NAPI_STATUS_RETURN(napi_create_reference(env, column.val, 1, &column.ref));
1180
-
1181
784
  NAPI_STATUS_RETURN(napi_set_named_property(env, argv[1], descriptors[n].name.c_str(), column.val));
1182
-
1183
- database->columns_[column.handle->GetID()] = column;
785
+ database->columns[column.handle->GetID()] = column;
1184
786
  }
1185
787
 
1186
- return CallFunction(env, callback, 2, argv);
788
+ return napi_ok;
1187
789
  });
1188
790
 
1189
791
  return 0;
@@ -1202,28 +804,9 @@ NAPI_METHOD(db_close) {
1202
804
  auto callback = argv[1];
1203
805
 
1204
806
  struct State {};
1205
- auto worker = createWorker<State>(
1206
- "leveldown.close", env, callback, database, false,
1207
- [=](auto& state, auto& database) -> rocksdb::Status {
1208
- for (auto& it : database.columns_) {
1209
- database.db_->DestroyColumnFamilyHandle(it.second.handle);
1210
- }
1211
- database.columns_.clear();
1212
-
1213
- return database.db_->Close();
1214
- },
1215
- [](auto& state, napi_env env, napi_value callback) -> napi_status {
1216
- napi_value argv[1];
1217
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1218
-
1219
- return CallFunction(env, callback, 1, &argv[0]);
1220
- });
1221
-
1222
- if (!database->HasPriorityWork()) {
1223
- worker->Queue(env);
1224
- } else {
1225
- database->pendingCloseWorker_ = worker;
1226
- }
807
+ runAsync<State>(
808
+ "leveldown.close", env, callback, [=](auto& state) { return database->Close(); },
809
+ [](auto& state, auto env, auto& argv) { return napi_ok; });
1227
810
 
1228
811
  return 0;
1229
812
  }
@@ -1234,40 +817,38 @@ NAPI_METHOD(updates_init) {
1234
817
  Database* database;
1235
818
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1236
819
 
1237
- napi_value sinceProperty;
1238
- int64_t since;
1239
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "since", &sinceProperty));
1240
- NAPI_STATUS_THROWS(napi_get_value_int64(env, sinceProperty, &since));
820
+ const auto options = argv[1];
821
+
822
+ int64_t since = 0;
823
+ NAPI_STATUS_THROWS(GetProperty(env, options, "since", since));
824
+
825
+ bool keys = true;
826
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
1241
827
 
1242
- napi_value keysProperty;
1243
- bool keys;
1244
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "keys", &keysProperty));
1245
- NAPI_STATUS_THROWS(napi_get_value_bool(env, keysProperty, &keys));
828
+ bool values = true;
829
+ NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
1246
830
 
1247
- napi_value valuesProperty;
1248
- bool values;
1249
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "values", &valuesProperty));
1250
- NAPI_STATUS_THROWS(napi_get_value_bool(env, valuesProperty, &values));
831
+ bool data = true;
832
+ NAPI_STATUS_THROWS(GetProperty(env, options, "data", data));
1251
833
 
1252
- napi_value dataProperty;
1253
- bool data;
1254
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "data", &dataProperty));
1255
- NAPI_STATUS_THROWS(napi_get_value_bool(env, dataProperty, &data));
834
+ Encoding keyEncoding = Encoding::String;
835
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
1256
836
 
1257
- const bool keyAsBuffer = EncodingIsBuffer(env, argv[1], "keyEncoding");
1258
- const bool valueAsBuffer = EncodingIsBuffer(env, argv[1], "valueEncoding");
837
+ Encoding valueEncoding = Encoding::String;
838
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1259
839
 
1260
840
  rocksdb::ColumnFamilyHandle* column = nullptr;
1261
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[1], &column));
841
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1262
842
 
1263
- auto updates = std::make_unique<Updates>(database, since, keys, values, data, column, keyAsBuffer, valueAsBuffer);
843
+ auto updates =
844
+ std::unique_ptr<Updates>(new Updates(database, since, keys, values, data, column, keyEncoding, valueEncoding));
1264
845
 
1265
846
  napi_value result;
1266
847
  NAPI_STATUS_THROWS(napi_create_external(env, updates.get(), Finalize<Updates>, updates.get(), &result));
1267
848
 
1268
849
  // Prevent GC of JS object before the iterator is closed (explicitly or on
1269
850
  // db close) and keep track of non-closed iterators to end them on db close.
1270
- updates.release()->Attach(env, result);
851
+ NAPI_STATUS_THROWS(updates.release()->Attach(env, result));
1271
852
 
1272
853
  return result;
1273
854
  }
@@ -1280,12 +861,12 @@ NAPI_METHOD(updates_next) {
1280
861
 
1281
862
  auto callback = argv[1];
1282
863
 
1283
- runWorker<rocksdb::BatchResult>(
1284
- "leveldown.updates.next", env, callback, updates->database_, true,
1285
- [=](auto& batchResult, auto& database) -> rocksdb::Status {
864
+ runAsync<rocksdb::BatchResult>(
865
+ "leveldown.updates.next", env, callback,
866
+ [=](auto& batchResult) {
1286
867
  if (!updates->iterator_) {
1287
868
  rocksdb::TransactionLogIterator::ReadOptions options;
1288
- const auto status = database.db_->GetUpdatesSince(updates->start_, &updates->iterator_, options);
869
+ const auto status = updates->database_->db->GetUpdatesSince(updates->start_, &updates->iterator_, options);
1289
870
  if (!status.ok()) {
1290
871
  return status;
1291
872
  }
@@ -1301,22 +882,18 @@ NAPI_METHOD(updates_next) {
1301
882
 
1302
883
  return rocksdb::Status::OK();
1303
884
  },
1304
- [=](auto& batchResult, napi_env env, napi_value callback) -> napi_status {
1305
- napi_value argv[5];
1306
-
1307
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1308
-
885
+ [=](auto& batchResult, auto env, auto& argv) {
1309
886
  if (!batchResult.writeBatchPtr) {
1310
- return CallFunction(env, callback, 1, argv);
887
+ return napi_ok;
1311
888
  }
1312
889
 
890
+ argv.resize(5);
1313
891
  NAPI_STATUS_RETURN(updates->Iterate(env, *batchResult.writeBatchPtr, &argv[1]));
1314
-
1315
892
  NAPI_STATUS_RETURN(napi_create_int64(env, batchResult.sequence, &argv[2]));
1316
893
  NAPI_STATUS_RETURN(napi_create_int64(env, batchResult.writeBatchPtr->Count(), &argv[3]));
1317
894
  NAPI_STATUS_RETURN(napi_create_int64(env, updates->start_, &argv[4]));
1318
895
 
1319
- return CallFunction(env, callback, 5, argv);
896
+ return napi_ok;
1320
897
  });
1321
898
 
1322
899
  return 0;
@@ -1328,8 +905,8 @@ NAPI_METHOD(updates_close) {
1328
905
  Updates* updates;
1329
906
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&updates)));
1330
907
 
1331
- updates->Detach(env);
1332
- updates->Close();
908
+ ROCKS_STATUS_THROWS_NAPI(updates->Close());
909
+ NAPI_STATUS_THROWS(updates->Detach(env));
1333
910
 
1334
911
  return 0;
1335
912
  }
@@ -1349,44 +926,53 @@ NAPI_METHOD(db_get_many) {
1349
926
  for (uint32_t n = 0; n < length; n++) {
1350
927
  napi_value element;
1351
928
  NAPI_STATUS_THROWS(napi_get_element(env, argv[1], n, &element));
1352
- NAPI_STATUS_THROWS(ToString(env, element, keys[n]));
929
+ NAPI_STATUS_THROWS(GetValue(env, element, keys[n]));
1353
930
  }
1354
931
  }
1355
932
 
1356
- const bool valueAsBuffer = EncodingIsBuffer(env, argv[2], "valueEncoding");
1357
- const bool fillCache = BooleanProperty(env, argv[2], "fillCache").value_or(true);
1358
- const bool ignoreRangeDeletions = BooleanProperty(env, argv[2], "ignoreRangeDeletions").value_or(false);
933
+ const auto options = argv[2];
934
+
935
+ Encoding valueEncoding = Encoding::String;
936
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1359
937
 
1360
- rocksdb::ColumnFamilyHandle* column;
1361
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, argv[2], &column));
938
+ bool fillCache = true;
939
+ NAPI_STATUS_THROWS(GetProperty(env, options, "fillCache", fillCache));
940
+
941
+ bool ignoreRangeDeletions = false;
942
+ NAPI_STATUS_THROWS(GetProperty(env, options, "ignoreRangeDeletions", ignoreRangeDeletions));
943
+
944
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
945
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1362
946
 
1363
947
  auto callback = argv[3];
1364
948
 
1365
949
  auto snapshot = std::shared_ptr<const rocksdb::Snapshot>(
1366
- database->db_->GetSnapshot(), [database](auto ptr) { database->db_->ReleaseSnapshot(ptr); });
950
+ database->db->GetSnapshot(), [database](auto ptr) { database->db->ReleaseSnapshot(ptr); });
1367
951
 
1368
- runWorker<std::vector<rocksdb::PinnableSlice>>(
1369
- "leveldown.get.many", env, callback, database, true,
1370
- [=, keys = std::move(keys)](auto& values, Database& db) -> rocksdb::Status {
952
+ runAsync<std::vector<rocksdb::PinnableSlice>>(
953
+ "leveldown.get.many", env, callback,
954
+ [=, keys = std::move(keys), snapshot = std::move(snapshot)](auto& values) {
1371
955
  rocksdb::ReadOptions readOptions;
1372
956
  readOptions.fill_cache = fillCache;
1373
957
  readOptions.snapshot = snapshot.get();
1374
958
  readOptions.async_io = true;
1375
959
  readOptions.ignore_range_deletions = ignoreRangeDeletions;
1376
960
 
961
+ const auto size = keys.size();
962
+
1377
963
  std::vector<rocksdb::Slice> keys2;
1378
- keys2.reserve(keys.size());
964
+ keys2.reserve(size);
1379
965
  for (const auto& key : keys) {
1380
966
  keys2.emplace_back(key);
1381
967
  }
1382
968
  std::vector<rocksdb::Status> statuses;
1383
969
 
1384
- statuses.resize(keys2.size());
1385
- values.resize(keys2.size());
970
+ statuses.resize(size);
971
+ values.resize(size);
1386
972
 
1387
- db.db_->MultiGet(readOptions, column, keys2.size(), keys2.data(), values.data(), statuses.data());
973
+ database->db->MultiGet(readOptions, column, size, keys2.data(), values.data(), statuses.data());
1388
974
 
1389
- for (size_t idx = 0; idx < keys2.size(); idx++) {
975
+ for (size_t idx = 0; idx < size; idx++) {
1390
976
  if (statuses[idx].IsNotFound()) {
1391
977
  values[idx] = rocksdb::PinnableSlice(nullptr);
1392
978
  } else if (!statuses[idx].ok()) {
@@ -1396,23 +982,22 @@ NAPI_METHOD(db_get_many) {
1396
982
 
1397
983
  return rocksdb::Status::OK();
1398
984
  },
1399
- [=](auto& values, napi_env env, napi_value callback) -> napi_status {
1400
- napi_value argv[2];
1401
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
985
+ [=](auto& values, auto env, auto& argv) {
986
+ argv.resize(2);
1402
987
 
1403
988
  NAPI_STATUS_RETURN(napi_create_array_with_length(env, values.size(), &argv[1]));
1404
989
 
1405
990
  for (size_t idx = 0; idx < values.size(); idx++) {
1406
991
  napi_value element;
1407
992
  if (values[idx].GetSelf()) {
1408
- NAPI_STATUS_RETURN(Convert(env, &values[idx], valueAsBuffer, element));
993
+ NAPI_STATUS_RETURN(Convert(env, &values[idx], valueEncoding, element));
1409
994
  } else {
1410
995
  NAPI_STATUS_RETURN(napi_get_undefined(env, &element));
1411
996
  }
1412
997
  NAPI_STATUS_RETURN(napi_set_element(env, argv[1], static_cast<uint32_t>(idx), element));
1413
998
  }
1414
999
 
1415
- return CallFunction(env, callback, 2, argv);
1000
+ return napi_ok;
1416
1001
  });
1417
1002
 
1418
1003
  return 0;
@@ -1424,16 +1009,28 @@ NAPI_METHOD(db_clear) {
1424
1009
  Database* database;
1425
1010
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1426
1011
 
1427
- const auto reverse = BooleanProperty(env, argv[1], "reverse").value_or(false);
1428
- const auto limit = Int32Property(env, argv[1], "limit").value_or(-1);
1012
+ const auto options = argv[1];
1013
+
1014
+ bool reverse = false;
1015
+ NAPI_STATUS_THROWS(GetProperty(env, options, "reverse", reverse));
1016
+
1017
+ int32_t limit = -1;
1018
+ NAPI_STATUS_THROWS(GetProperty(env, options, "limit", limit));
1019
+
1020
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
1021
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1022
+
1023
+ std::optional<std::string> lt;
1024
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lt", lt));
1429
1025
 
1430
- rocksdb::ColumnFamilyHandle* column;
1431
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, argv[1], &column));
1026
+ std::optional<std::string> lte;
1027
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lte", lte));
1432
1028
 
1433
- auto lt = StringProperty(env, argv[1], "lt");
1434
- auto lte = StringProperty(env, argv[1], "lte");
1435
- auto gt = StringProperty(env, argv[1], "gt");
1436
- auto gte = StringProperty(env, argv[1], "gte");
1029
+ std::optional<std::string> gt;
1030
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gt", gt));
1031
+
1032
+ std::optional<std::string> gte;
1033
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gte", gte));
1437
1034
 
1438
1035
  if (limit == -1) {
1439
1036
  rocksdb::PinnableSlice begin;
@@ -1458,7 +1055,7 @@ NAPI_METHOD(db_clear) {
1458
1055
 
1459
1056
  if (begin.compare(end) < 0) {
1460
1057
  rocksdb::WriteOptions writeOptions;
1461
- ROCKS_STATUS_THROWS(database->db_->DeleteRange(writeOptions, column, begin, end));
1058
+ ROCKS_STATUS_THROWS_NAPI(database->db->DeleteRange(writeOptions, column, begin, end));
1462
1059
  }
1463
1060
 
1464
1061
  return 0;
@@ -1466,8 +1063,8 @@ NAPI_METHOD(db_clear) {
1466
1063
  // TODO (fix): Error handling.
1467
1064
  // TODO (fix): This should be async...
1468
1065
 
1469
- std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db_->GetSnapshot(),
1470
- [=](const auto ptr) { database->db_->ReleaseSnapshot(ptr); });
1066
+ std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db->GetSnapshot(),
1067
+ [=](const auto ptr) { database->db->ReleaseSnapshot(ptr); });
1471
1068
  BaseIterator it(database, column, reverse, lt, lte, gt, gte, limit, false, snapshot);
1472
1069
 
1473
1070
  it.SeekToRange();
@@ -1491,7 +1088,7 @@ NAPI_METHOD(db_clear) {
1491
1088
  break;
1492
1089
  }
1493
1090
 
1494
- status = database->db_->Write(writeOptions, &batch);
1091
+ status = database->db->Write(writeOptions, &batch);
1495
1092
  if (!status.ok()) {
1496
1093
  break;
1497
1094
  }
@@ -1502,7 +1099,7 @@ NAPI_METHOD(db_clear) {
1502
1099
  it.Close();
1503
1100
 
1504
1101
  if (!status.ok()) {
1505
- ROCKS_STATUS_THROWS(status);
1102
+ ROCKS_STATUS_THROWS_NAPI(status);
1506
1103
  }
1507
1104
 
1508
1105
  return 0;
@@ -1515,11 +1112,11 @@ NAPI_METHOD(db_get_property) {
1515
1112
  Database* database;
1516
1113
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1517
1114
 
1518
- rocksdb::PinnableSlice property;
1519
- NAPI_STATUS_THROWS(ToString(env, argv[1], property));
1115
+ NapiSlice property;
1116
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], property));
1520
1117
 
1521
1118
  std::string value;
1522
- database->db_->GetProperty(property, &value);
1119
+ database->db->GetProperty(property, &value);
1523
1120
 
1524
1121
  napi_value result;
1525
1122
  NAPI_STATUS_THROWS(napi_create_string_utf8(env, value.data(), value.size(), &result));
@@ -1533,7 +1130,7 @@ NAPI_METHOD(db_get_latest_sequence) {
1533
1130
  Database* database;
1534
1131
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1535
1132
 
1536
- const auto seq = database->db_->GetLatestSequenceNumber();
1133
+ const auto seq = database->db->GetLatestSequenceNumber();
1537
1134
 
1538
1135
  napi_value result;
1539
1136
  NAPI_STATUS_THROWS(napi_create_int64(env, seq, &result));
@@ -1548,35 +1145,59 @@ NAPI_METHOD(iterator_init) {
1548
1145
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1549
1146
 
1550
1147
  const auto options = argv[1];
1551
- const auto reverse = BooleanProperty(env, options, "reverse").value_or(false);
1552
- const auto keys = BooleanProperty(env, options, "keys").value_or(true);
1553
- const auto values = BooleanProperty(env, options, "values").value_or(true);
1554
- const auto fillCache = BooleanProperty(env, options, "fillCache").value_or(false);
1555
- const bool keyAsBuffer = EncodingIsBuffer(env, options, "keyEncoding");
1556
- const bool valueAsBuffer = EncodingIsBuffer(env, options, "valueEncoding");
1557
- const auto limit = Int32Property(env, options, "limit").value_or(-1);
1558
- const auto highWaterMarkBytes = Uint32Property(env, options, "highWaterMarkBytes").value_or(64 * 1024);
1559
1148
 
1560
- const auto lt = StringProperty(env, options, "lt");
1561
- const auto lte = StringProperty(env, options, "lte");
1562
- const auto gt = StringProperty(env, options, "gt");
1563
- const auto gte = StringProperty(env, options, "gte");
1149
+ bool reverse = false;
1150
+ NAPI_STATUS_THROWS(GetProperty(env, options, "reverse", reverse));
1151
+
1152
+ bool keys = true;
1153
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
1154
+
1155
+ bool values = true;
1156
+ NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
1157
+
1158
+ bool fillCache = false;
1159
+ NAPI_STATUS_THROWS(GetProperty(env, options, "fillCache", fillCache));
1160
+
1161
+ Encoding keyEncoding = Encoding::String;
1162
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
1163
+
1164
+ Encoding valueEncoding = Encoding::String;
1165
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1166
+
1167
+ int32_t limit = -1;
1168
+ NAPI_STATUS_THROWS(GetProperty(env, options, "limit", limit));
1169
+
1170
+ int32_t highWaterMarkBytes = 64 * 1024;
1171
+ NAPI_STATUS_THROWS(GetProperty(env, options, "highWaterMarkBytes", highWaterMarkBytes));
1172
+
1173
+ std::optional<std::string> lt;
1174
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lt", lt));
1564
1175
 
1565
- rocksdb::ColumnFamilyHandle* column;
1566
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, options, &column));
1176
+ std::optional<std::string> lte;
1177
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lte", lte));
1567
1178
 
1568
- std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db_->GetSnapshot(),
1569
- [=](const auto ptr) { database->db_->ReleaseSnapshot(ptr); });
1179
+ std::optional<std::string> gt;
1180
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gt", gt));
1570
1181
 
1571
- auto iterator = std::make_unique<Iterator>(database, column, reverse, keys, values, limit, lt, lte, gt, gte,
1572
- fillCache, keyAsBuffer, valueAsBuffer, highWaterMarkBytes, snapshot);
1182
+ std::optional<std::string> gte;
1183
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gte", gte));
1184
+
1185
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
1186
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1187
+
1188
+ std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db->GetSnapshot(),
1189
+ [=](const auto ptr) { database->db->ReleaseSnapshot(ptr); });
1190
+
1191
+ auto iterator =
1192
+ std::unique_ptr<Iterator>(new Iterator(database, column, reverse, keys, values, limit, lt, lte, gt, gte,
1193
+ fillCache, keyEncoding, valueEncoding, highWaterMarkBytes, snapshot));
1573
1194
 
1574
1195
  napi_value result;
1575
1196
  NAPI_STATUS_THROWS(napi_create_external(env, iterator.get(), Finalize<Iterator>, iterator.get(), &result));
1576
1197
 
1577
1198
  // Prevent GC of JS object before the iterator is closed (explicitly or on
1578
1199
  // db close) and keep track of non-closed iterators to end them on db close.
1579
- iterator.release()->Attach(env, result);
1200
+ NAPI_STATUS_THROWS(iterator.release()->Attach(env, result));
1580
1201
 
1581
1202
  return result;
1582
1203
  }
@@ -1587,8 +1208,8 @@ NAPI_METHOD(iterator_seek) {
1587
1208
  Iterator* iterator;
1588
1209
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&iterator)));
1589
1210
 
1590
- rocksdb::PinnableSlice target;
1591
- NAPI_STATUS_THROWS(ToString(env, argv[1], target));
1211
+ NapiSlice target;
1212
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], target));
1592
1213
 
1593
1214
  iterator->first_ = true;
1594
1215
  iterator->Seek(target); // TODO: Does seek causing blocking IO?
@@ -1602,8 +1223,8 @@ NAPI_METHOD(iterator_close) {
1602
1223
  Iterator* iterator;
1603
1224
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&iterator)));
1604
1225
 
1605
- iterator->Detach(env);
1606
- iterator->Close();
1226
+ ROCKS_STATUS_THROWS_NAPI(iterator->Close());
1227
+ NAPI_STATUS_THROWS(iterator->Detach(env));
1607
1228
 
1608
1229
  return 0;
1609
1230
  }
@@ -1638,9 +1259,9 @@ NAPI_METHOD(iterator_nextv) {
1638
1259
  bool finished = false;
1639
1260
  };
1640
1261
 
1641
- runWorker<State>(
1642
- std::string("leveldown.iterator.next"), env, callback, iterator->database_, true,
1643
- [=](auto& state, Database& db) -> rocksdb::Status {
1262
+ runAsync<State>(
1263
+ std::string("leveldown.iterator.next"), env, callback,
1264
+ [=](auto& state) {
1644
1265
  if (!iterator->DidSeek()) {
1645
1266
  iterator->SeekToRange();
1646
1267
  }
@@ -1685,9 +1306,8 @@ NAPI_METHOD(iterator_nextv) {
1685
1306
 
1686
1307
  return iterator->Status();
1687
1308
  },
1688
- [=](auto& state, napi_env env, napi_value callback) -> napi_status {
1689
- napi_value argv[3];
1690
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1309
+ [=](auto& state, auto env, auto& argv) {
1310
+ argv.resize(3);
1691
1311
 
1692
1312
  NAPI_STATUS_RETURN(napi_create_array_with_length(env, state.cache.size(), &argv[1]));
1693
1313
 
@@ -1695,8 +1315,8 @@ NAPI_METHOD(iterator_nextv) {
1695
1315
  napi_value key;
1696
1316
  napi_value val;
1697
1317
 
1698
- NAPI_STATUS_RETURN(Convert(env, state.cache[n + 0], iterator->keyAsBuffer_, key));
1699
- NAPI_STATUS_RETURN(Convert(env, state.cache[n + 1], iterator->valueAsBuffer_, val));
1318
+ NAPI_STATUS_RETURN(Convert(env, state.cache[n + 0], iterator->keyEncoding_, key));
1319
+ NAPI_STATUS_RETURN(Convert(env, state.cache[n + 1], iterator->valueEncoding_, val));
1700
1320
 
1701
1321
  NAPI_STATUS_RETURN(napi_set_element(env, argv[1], static_cast<int>(n + 0), key));
1702
1322
  NAPI_STATUS_RETURN(napi_set_element(env, argv[1], static_cast<int>(n + 1), val));
@@ -1704,7 +1324,7 @@ NAPI_METHOD(iterator_nextv) {
1704
1324
 
1705
1325
  NAPI_STATUS_RETURN(napi_get_boolean(env, state.finished, &argv[2]));
1706
1326
 
1707
- return CallFunction(env, callback, 3, argv);
1327
+ return napi_ok;
1708
1328
  });
1709
1329
 
1710
1330
  return 0;
@@ -1718,63 +1338,45 @@ NAPI_METHOD(batch_do) {
1718
1338
 
1719
1339
  rocksdb::WriteBatch batch;
1720
1340
 
1341
+ auto elements = argv[1];
1342
+
1721
1343
  uint32_t length;
1722
- NAPI_STATUS_THROWS(napi_get_array_length(env, argv[1], &length));
1344
+ NAPI_STATUS_THROWS(napi_get_array_length(env, elements, &length));
1723
1345
 
1724
1346
  for (uint32_t i = 0; i < length; i++) {
1725
- rocksdb::PinnableSlice type;
1726
- rocksdb::PinnableSlice key;
1727
- rocksdb::PinnableSlice value;
1347
+ NapiSlice type;
1348
+ NapiSlice key;
1349
+ NapiSlice value;
1728
1350
 
1729
1351
  napi_value element;
1730
- NAPI_STATUS_THROWS(napi_get_element(env, argv[1], i, &element));
1352
+ NAPI_STATUS_THROWS(napi_get_element(env, elements, i, &element));
1731
1353
 
1732
- napi_value typeProperty;
1733
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "type", &typeProperty));
1734
- NAPI_STATUS_THROWS(ToString(env, typeProperty, type));
1354
+ NAPI_STATUS_THROWS(GetProperty(env, element, "type", type, true));
1735
1355
 
1736
- rocksdb::ColumnFamilyHandle* column;
1737
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, element, &column));
1356
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
1357
+ NAPI_STATUS_THROWS(GetProperty(env, element, "column", column));
1738
1358
 
1739
1359
  if (type == "del") {
1740
- napi_value keyProperty;
1741
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "key", &keyProperty));
1742
- NAPI_STATUS_THROWS(ToString(env, keyProperty, key));
1743
-
1744
- ROCKS_STATUS_THROWS(batch.Delete(column, key));
1360
+ NAPI_STATUS_THROWS(GetProperty(env, element, "key", key, true));
1361
+ ROCKS_STATUS_THROWS_NAPI(batch.Delete(column, key));
1745
1362
  } else if (type == "put") {
1746
- napi_value keyProperty;
1747
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "key", &keyProperty));
1748
- NAPI_STATUS_THROWS(ToString(env, keyProperty, key));
1749
-
1750
- napi_value valueProperty;
1751
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "value", &valueProperty));
1752
- NAPI_STATUS_THROWS(ToString(env, valueProperty, value));
1753
-
1754
- ROCKS_STATUS_THROWS(batch.Put(column, key, value));
1363
+ NAPI_STATUS_THROWS(GetProperty(env, element, "key", key, true));
1364
+ NAPI_STATUS_THROWS(GetProperty(env, element, "value", value, true));
1365
+ ROCKS_STATUS_THROWS_NAPI(batch.Put(column, key, value));
1755
1366
  } else if (type == "data") {
1756
- napi_value valueProperty;
1757
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "value", &valueProperty));
1758
- NAPI_STATUS_THROWS(ToString(env, valueProperty, value));
1759
-
1760
- ROCKS_STATUS_THROWS(batch.PutLogData(value));
1367
+ NAPI_STATUS_THROWS(GetProperty(env, element, "value", value, true));
1368
+ ROCKS_STATUS_THROWS_NAPI(batch.PutLogData(value));
1761
1369
  } else if (type == "merge") {
1762
- napi_value keyProperty;
1763
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "key", &keyProperty));
1764
- NAPI_STATUS_THROWS(ToString(env, keyProperty, key));
1765
-
1766
- napi_value valueProperty;
1767
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "value", &valueProperty));
1768
- NAPI_STATUS_THROWS(ToString(env, valueProperty, value));
1769
-
1770
- ROCKS_STATUS_THROWS(batch.Merge(column, key, value));
1370
+ NAPI_STATUS_THROWS(GetProperty(env, element, "key", key, true));
1371
+ NAPI_STATUS_THROWS(GetProperty(env, element, "value", value, true));
1372
+ ROCKS_STATUS_THROWS_NAPI(batch.Merge(column, key, value));
1771
1373
  } else {
1772
- ROCKS_STATUS_THROWS(rocksdb::Status::InvalidArgument());
1374
+ NAPI_STATUS_THROWS(napi_invalid_arg);
1773
1375
  }
1774
1376
  }
1775
1377
 
1776
1378
  rocksdb::WriteOptions writeOptions;
1777
- ROCKS_STATUS_THROWS(database->db_->Write(writeOptions, &batch));
1379
+ ROCKS_STATUS_THROWS_NAPI(database->db->Write(writeOptions, &batch));
1778
1380
 
1779
1381
  return 0;
1780
1382
  }
@@ -1792,21 +1394,23 @@ NAPI_METHOD(batch_put) {
1792
1394
  NAPI_ARGV(4);
1793
1395
 
1794
1396
  rocksdb::WriteBatch* batch;
1795
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)(&batch)));
1397
+ NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&batch)));
1796
1398
 
1797
- rocksdb::PinnableSlice key;
1798
- NAPI_STATUS_THROWS(ToString(env, argv[1], key));
1399
+ NapiSlice key;
1400
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], key));
1799
1401
 
1800
- rocksdb::PinnableSlice val;
1801
- NAPI_STATUS_THROWS(ToString(env, argv[2], val));
1402
+ NapiSlice val;
1403
+ NAPI_STATUS_THROWS(GetValue(env, argv[2], val));
1802
1404
 
1803
- rocksdb::ColumnFamilyHandle* column;
1804
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[3], &column));
1405
+ const auto options = argv[3];
1406
+
1407
+ rocksdb::ColumnFamilyHandle* column = nullptr;
1408
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1805
1409
 
1806
1410
  if (column) {
1807
- ROCKS_STATUS_THROWS(batch->Put(column, key, val));
1411
+ ROCKS_STATUS_THROWS_NAPI(batch->Put(column, key, val));
1808
1412
  } else {
1809
- ROCKS_STATUS_THROWS(batch->Put(key, val));
1413
+ ROCKS_STATUS_THROWS_NAPI(batch->Put(key, val));
1810
1414
  }
1811
1415
 
1812
1416
  return 0;
@@ -1818,16 +1422,18 @@ NAPI_METHOD(batch_del) {
1818
1422
  rocksdb::WriteBatch* batch;
1819
1423
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&batch)));
1820
1424
 
1821
- rocksdb::PinnableSlice key;
1822
- NAPI_STATUS_THROWS(ToString(env, argv[1], key));
1425
+ NapiSlice key;
1426
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], key));
1823
1427
 
1824
- rocksdb::ColumnFamilyHandle* column;
1825
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[2], &column));
1428
+ const auto options = argv[2];
1429
+
1430
+ rocksdb::ColumnFamilyHandle* column = nullptr;
1431
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1826
1432
 
1827
1433
  if (column) {
1828
- ROCKS_STATUS_THROWS(batch->Delete(column, key));
1434
+ ROCKS_STATUS_THROWS_NAPI(batch->Delete(column, key));
1829
1435
  } else {
1830
- ROCKS_STATUS_THROWS(batch->Delete(key));
1436
+ ROCKS_STATUS_THROWS_NAPI(batch->Delete(key));
1831
1437
  }
1832
1438
 
1833
1439
  return 0;
@@ -1839,19 +1445,21 @@ NAPI_METHOD(batch_merge) {
1839
1445
  rocksdb::WriteBatch* batch;
1840
1446
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)(&batch)));
1841
1447
 
1842
- rocksdb::PinnableSlice key;
1843
- NAPI_STATUS_THROWS(ToString(env, argv[1], key));
1448
+ NapiSlice key;
1449
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], key));
1844
1450
 
1845
- rocksdb::PinnableSlice val;
1846
- NAPI_STATUS_THROWS(ToString(env, argv[2], val));
1451
+ NapiSlice val;
1452
+ NAPI_STATUS_THROWS(GetValue(env, argv[2], val));
1847
1453
 
1848
- rocksdb::ColumnFamilyHandle* column;
1849
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[3], &column));
1454
+ const auto options = argv[3];
1455
+
1456
+ rocksdb::ColumnFamilyHandle* column = nullptr;
1457
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1850
1458
 
1851
1459
  if (column) {
1852
- ROCKS_STATUS_THROWS(batch->Merge(column, key, val));
1460
+ ROCKS_STATUS_THROWS_NAPI(batch->Merge(column, key, val));
1853
1461
  } else {
1854
- ROCKS_STATUS_THROWS(batch->Merge(key, val));
1462
+ ROCKS_STATUS_THROWS_NAPI(batch->Merge(key, val));
1855
1463
  }
1856
1464
 
1857
1465
  return 0;
@@ -1878,7 +1486,7 @@ NAPI_METHOD(batch_write) {
1878
1486
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
1879
1487
 
1880
1488
  rocksdb::WriteOptions writeOptions;
1881
- ROCKS_STATUS_THROWS(database->db_->Write(writeOptions, batch));
1489
+ ROCKS_STATUS_THROWS_NAPI(database->db->Write(writeOptions, batch));
1882
1490
 
1883
1491
  return 0;
1884
1492
  }
@@ -1889,10 +1497,10 @@ NAPI_METHOD(batch_put_log_data) {
1889
1497
  rocksdb::WriteBatch* batch;
1890
1498
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&batch)));
1891
1499
 
1892
- rocksdb::PinnableSlice logData;
1893
- NAPI_STATUS_THROWS(ToString(env, argv[1], logData));
1500
+ NapiSlice logData;
1501
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], logData));
1894
1502
 
1895
- ROCKS_STATUS_THROWS(batch->PutLogData(logData));
1503
+ ROCKS_STATUS_THROWS_NAPI(batch->PutLogData(logData));
1896
1504
 
1897
1505
  return 0;
1898
1506
  }
@@ -1918,111 +1526,34 @@ NAPI_METHOD(batch_iterate) {
1918
1526
  rocksdb::WriteBatch* batch;
1919
1527
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
1920
1528
 
1921
- napi_value keysProperty;
1922
- bool keys;
1923
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "keys", &keysProperty));
1924
- NAPI_STATUS_THROWS(napi_get_value_bool(env, keysProperty, &keys));
1529
+ const auto options = argv[2];
1925
1530
 
1926
- napi_value valuesProperty;
1927
- bool values;
1928
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "values", &valuesProperty));
1929
- NAPI_STATUS_THROWS(napi_get_value_bool(env, valuesProperty, &values));
1531
+ bool keys = true;
1532
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
1930
1533
 
1931
- napi_value dataProperty;
1932
- bool data;
1933
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "data", &dataProperty));
1934
- NAPI_STATUS_THROWS(napi_get_value_bool(env, dataProperty, &data));
1534
+ bool values = true;
1535
+ NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
1935
1536
 
1936
- const bool keyAsBuffer = EncodingIsBuffer(env, argv[1], "keyEncoding");
1937
- const bool valueAsBuffer = EncodingIsBuffer(env, argv[1], "valueEncoding");
1537
+ bool data = true;
1538
+ NAPI_STATUS_THROWS(GetProperty(env, options, "data", data));
1539
+
1540
+ Encoding keyEncoding = Encoding::String;
1541
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
1542
+
1543
+ Encoding valueEncoding = Encoding::String;
1544
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1938
1545
 
1939
1546
  rocksdb::ColumnFamilyHandle* column = nullptr;
1940
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[2], &column));
1547
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1941
1548
 
1942
- napi_value result;
1943
- BatchIterator iterator(nullptr, keys, values, data, column, keyAsBuffer, valueAsBuffer);
1549
+ BatchIterator iterator(nullptr, keys, values, data, column, keyEncoding, valueEncoding);
1944
1550
 
1551
+ napi_value result;
1945
1552
  NAPI_STATUS_THROWS(iterator.Iterate(env, *batch, &result));
1946
1553
 
1947
1554
  return result;
1948
1555
  }
1949
1556
 
1950
- NAPI_METHOD(db_flush_wal) {
1951
- NAPI_ARGV(2);
1952
-
1953
- Database* database;
1954
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1955
-
1956
- const auto flush = BooleanProperty(env, argv[1], "flush").value_or(false);
1957
-
1958
- ROCKS_STATUS_THROWS(database->db_->FlushWAL(flush));
1959
-
1960
- return 0;
1961
- }
1962
-
1963
- template <typename T>
1964
- napi_status FromLogFile(napi_env env, const T& file, napi_value* obj) {
1965
- NAPI_STATUS_RETURN(napi_create_object(env, obj));
1966
-
1967
- napi_value pathName;
1968
- NAPI_STATUS_RETURN(napi_create_string_utf8(env, file->PathName().c_str(), NAPI_AUTO_LENGTH, &pathName));
1969
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "pathName", pathName));
1970
-
1971
- napi_value logNumber;
1972
- NAPI_STATUS_RETURN(napi_create_int64(env, file->LogNumber(), &logNumber));
1973
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "logNumber", logNumber));
1974
-
1975
- napi_value type;
1976
- NAPI_STATUS_RETURN(napi_create_int64(env, file->Type(), &type));
1977
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "type", type));
1978
-
1979
- napi_value startSequence;
1980
- NAPI_STATUS_RETURN(napi_create_int64(env, file->StartSequence(), &startSequence));
1981
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "startSequence", startSequence));
1982
-
1983
- napi_value sizeFileBytes;
1984
- NAPI_STATUS_RETURN(napi_create_int64(env, file->SizeFileBytes(), &sizeFileBytes));
1985
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "sizeFileBytes", sizeFileBytes));
1986
-
1987
- return napi_ok;
1988
- }
1989
-
1990
- NAPI_METHOD(db_get_sorted_wal_files) {
1991
- NAPI_ARGV(1);
1992
-
1993
- Database* database;
1994
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1995
-
1996
- rocksdb::VectorLogPtr files;
1997
- ROCKS_STATUS_THROWS(database->db_->GetSortedWalFiles(files));
1998
-
1999
- napi_value ret;
2000
- NAPI_STATUS_THROWS(napi_create_array_with_length(env, files.size(), &ret));
2001
-
2002
- for (size_t n = 0; n < files.size(); ++n) {
2003
- napi_value obj;
2004
- NAPI_STATUS_THROWS(FromLogFile(env, files[n], &obj));
2005
- NAPI_STATUS_THROWS(napi_set_element(env, ret, n, obj));
2006
- }
2007
-
2008
- return ret;
2009
- }
2010
-
2011
- NAPI_METHOD(db_get_current_wal_file) {
2012
- NAPI_ARGV(1);
2013
-
2014
- Database* database;
2015
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
2016
-
2017
- std::unique_ptr<rocksdb::LogFile> file;
2018
- ROCKS_STATUS_THROWS(database->db_->GetCurrentWalFile(&file));
2019
-
2020
- napi_value ret;
2021
- NAPI_STATUS_THROWS(FromLogFile(env, file, &ret));
2022
-
2023
- return ret;
2024
- }
2025
-
2026
1557
  NAPI_INIT() {
2027
1558
  NAPI_EXPORT_FUNCTION(db_init);
2028
1559
  NAPI_EXPORT_FUNCTION(db_open);
@@ -2042,10 +1573,6 @@ NAPI_INIT() {
2042
1573
  NAPI_EXPORT_FUNCTION(updates_close);
2043
1574
  NAPI_EXPORT_FUNCTION(updates_next);
2044
1575
 
2045
- NAPI_EXPORT_FUNCTION(db_flush_wal);
2046
- NAPI_EXPORT_FUNCTION(db_get_sorted_wal_files);
2047
- NAPI_EXPORT_FUNCTION(db_get_current_wal_file);
2048
-
2049
1576
  NAPI_EXPORT_FUNCTION(batch_do);
2050
1577
  NAPI_EXPORT_FUNCTION(batch_init);
2051
1578
  NAPI_EXPORT_FUNCTION(batch_put);