@nxtedition/rocksdb 5.2.26 → 5.2.29

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
@@ -1,28 +1,27 @@
1
1
  #define NAPI_VERSION 8
2
2
 
3
+ #include <assert.h>
3
4
  #include <napi-macros.h>
4
5
  #include <node_api.h>
5
- #include <assert.h>
6
6
 
7
- #include <rocksdb/db.h>
8
- #include <rocksdb/write_batch.h>
9
- #include <rocksdb/cache.h>
10
- #include <rocksdb/filter_policy.h>
11
7
  #include <rocksdb/cache.h>
12
8
  #include <rocksdb/comparator.h>
9
+ #include <rocksdb/db.h>
13
10
  #include <rocksdb/env.h>
11
+ #include <rocksdb/filter_policy.h>
14
12
  #include <rocksdb/options.h>
15
13
  #include <rocksdb/table.h>
14
+ #include <rocksdb/write_batch.h>
16
15
 
17
- namespace leveldb = rocksdb;
18
-
19
- #include <set>
16
+ #include <array>
20
17
  #include <memory>
18
+ #include <optional>
19
+ #include <set>
21
20
  #include <string>
22
21
  #include <vector>
23
22
 
24
23
  class NullLogger : public rocksdb::Logger {
25
- public:
24
+ public:
26
25
  using rocksdb::Logger::Logv;
27
26
  virtual void Logv(const char* format, va_list ap) override {}
28
27
  virtual size_t GetLogFileSize() const override { return 0; }
@@ -31,7 +30,7 @@ public:
31
30
  struct Database;
32
31
  struct Iterator;
33
32
 
34
- #define NAPI_DB_CONTEXT() \
33
+ #define NAPI_DB_CONTEXT() \
35
34
  Database* database = nullptr; \
36
35
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
37
36
 
@@ -39,29 +38,29 @@ struct Iterator;
39
38
  Iterator* iterator = nullptr; \
40
39
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator));
41
40
 
42
- #define NAPI_BATCH_CONTEXT() \
41
+ #define NAPI_BATCH_CONTEXT() \
43
42
  rocksdb::WriteBatch* batch = nullptr; \
44
43
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&batch));
45
44
 
46
- static bool IsString (napi_env env, napi_value value) {
45
+ static bool IsString(napi_env env, napi_value value) {
47
46
  napi_valuetype type;
48
47
  napi_typeof(env, value, &type);
49
48
  return type == napi_string;
50
49
  }
51
50
 
52
- static bool IsBuffer (napi_env env, napi_value value) {
51
+ static bool IsBuffer(napi_env env, napi_value value) {
53
52
  bool isBuffer;
54
53
  napi_is_buffer(env, value, &isBuffer);
55
54
  return isBuffer;
56
55
  }
57
56
 
58
- static bool IsObject (napi_env env, napi_value value) {
57
+ static bool IsObject(napi_env env, napi_value value) {
59
58
  napi_valuetype type;
60
59
  napi_typeof(env, value, &type);
61
60
  return type == napi_object;
62
61
  }
63
62
 
64
- static napi_value CreateError (napi_env env, const std::string& str) {
63
+ static napi_value CreateError(napi_env env, const std::string_view& str) {
65
64
  napi_value msg;
66
65
  napi_create_string_utf8(env, str.data(), str.size(), &msg);
67
66
  napi_value error;
@@ -69,7 +68,7 @@ static napi_value CreateError (napi_env env, const std::string& str) {
69
68
  return error;
70
69
  }
71
70
 
72
- static napi_value CreateCodeError (napi_env env, const std::string& code, const std::string& msg) {
71
+ static napi_value CreateCodeError(napi_env env, const std::string_view& code, const std::string_view& msg) {
73
72
  napi_value codeValue;
74
73
  napi_create_string_utf8(env, code.data(), code.size(), &codeValue);
75
74
  napi_value msgValue;
@@ -79,19 +78,19 @@ static napi_value CreateCodeError (napi_env env, const std::string& code, const
79
78
  return error;
80
79
  }
81
80
 
82
- static bool HasProperty (napi_env env, napi_value obj, const std::string& key) {
81
+ static bool HasProperty(napi_env env, napi_value obj, const std::string_view& key) {
83
82
  bool has = false;
84
83
  napi_has_named_property(env, obj, key.data(), &has);
85
84
  return has;
86
85
  }
87
86
 
88
- static napi_value GetProperty (napi_env env, napi_value obj, const std::string& key) {
87
+ static napi_value GetProperty(napi_env env, napi_value obj, const std::string_view& key) {
89
88
  napi_value value;
90
89
  napi_get_named_property(env, obj, key.data(), &value);
91
90
  return value;
92
91
  }
93
92
 
94
- static bool BooleanProperty (napi_env env, napi_value obj, const std::string& key, bool defaultValue) {
93
+ static bool BooleanProperty(napi_env env, napi_value obj, const std::string_view& key, bool defaultValue) {
95
94
  if (HasProperty(env, obj, key.data())) {
96
95
  const auto value = GetProperty(env, obj, key.data());
97
96
  bool result;
@@ -102,12 +101,12 @@ static bool BooleanProperty (napi_env env, napi_value obj, const std::string& ke
102
101
  return defaultValue;
103
102
  }
104
103
 
105
- static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string& option) {
104
+ static bool EncodingIsBuffer(napi_env env, napi_value obj, const std::string_view& option) {
106
105
  napi_value value;
107
106
  size_t size;
108
107
 
109
108
  if (napi_get_named_property(env, obj, option.data(), &value) == napi_ok &&
110
- napi_get_value_string_utf8(env, value, nullptr, 0, &size) == napi_ok) {
109
+ napi_get_value_string_utf8(env, value, nullptr, 0, &size) == napi_ok) {
111
110
  // Value is either "buffer" or "utf8" so we can tell them apart just by size
112
111
  return size == 6;
113
112
  }
@@ -115,7 +114,7 @@ static bool EncodingIsBuffer (napi_env env, napi_value obj, const std::string& o
115
114
  return false;
116
115
  }
117
116
 
118
- static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string& key, uint32_t defaultValue) {
117
+ static uint32_t Uint32Property(napi_env env, napi_value obj, const std::string_view& key, uint32_t defaultValue) {
119
118
  if (HasProperty(env, obj, key.data())) {
120
119
  const auto value = GetProperty(env, obj, key.data());
121
120
  uint32_t result;
@@ -126,7 +125,7 @@ static uint32_t Uint32Property (napi_env env, napi_value obj, const std::string&
126
125
  return defaultValue;
127
126
  }
128
127
 
129
- static int Int32Property (napi_env env, napi_value obj, const std::string& key, int defaultValue) {
128
+ static int Int32Property(napi_env env, napi_value obj, const std::string_view& key, int defaultValue) {
130
129
  if (HasProperty(env, obj, key.data())) {
131
130
  const auto value = GetProperty(env, obj, key.data());
132
131
  int result;
@@ -137,24 +136,27 @@ static int Int32Property (napi_env env, napi_value obj, const std::string& key,
137
136
  return defaultValue;
138
137
  }
139
138
 
140
- static std::string ToString (napi_env env, napi_value from, const std::string& defaultValue = "") {
139
+ static std::string ToString(napi_env env, napi_value from, const std::string& defaultValue = "") {
141
140
  if (IsString(env, from)) {
142
141
  size_t length = 0;
143
142
  napi_get_value_string_utf8(env, from, nullptr, 0, &length);
144
143
  std::string value(length, '\0');
145
- napi_get_value_string_utf8( env, from, &value[0], value.length() + 1, &length);
144
+ napi_get_value_string_utf8(env, from, &value[0], value.length() + 1, &length);
146
145
  return value;
147
146
  } else if (IsBuffer(env, from)) {
148
147
  char* buf = nullptr;
149
148
  size_t length = 0;
150
- napi_get_buffer_info(env, from, reinterpret_cast<void **>(&buf), &length);
149
+ napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length);
151
150
  return std::string(buf, length);
152
151
  }
153
152
 
154
153
  return defaultValue;
155
154
  }
156
155
 
157
- static std::string StringProperty (napi_env env, napi_value obj, const std::string& key, const std::string& defaultValue = "") {
156
+ static std::string StringProperty(napi_env env,
157
+ napi_value obj,
158
+ const std::string_view& key,
159
+ const std::string& defaultValue = "") {
158
160
  if (HasProperty(env, obj, key)) {
159
161
  napi_value value = GetProperty(env, obj, key);
160
162
  if (IsString(env, value)) {
@@ -165,56 +167,49 @@ static std::string StringProperty (napi_env env, napi_value obj, const std::stri
165
167
  return defaultValue;
166
168
  }
167
169
 
168
- static size_t StringOrBufferLength (napi_env env, napi_value value) {
169
- size_t size = 0;
170
-
171
- if (IsString(env, value)) {
172
- napi_get_value_string_utf8(env, value, nullptr, 0, &size);
173
- } else if (IsBuffer(env, value)) {
174
- char* buf = nullptr;
175
- napi_get_buffer_info(env, value, (void **)&buf, &size);
176
- }
177
-
178
- return size;
179
- }
180
-
181
- static std::string* RangeOption (napi_env env, napi_value opts, const std::string& name) {
170
+ static std::optional<std::string> RangeOption(napi_env env, napi_value opts, const std::string_view& name) {
182
171
  if (HasProperty(env, opts, name)) {
183
172
  const auto value = GetProperty(env, opts, name);
184
- return new std::string(ToString(env, value));
173
+ return ToString(env, value);
185
174
  }
186
175
 
187
- return nullptr;
176
+ return {};
188
177
  }
189
178
 
190
- static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
191
- uint32_t length;
192
- std::vector<std::string> result;
179
+ static napi_status CallFunction(napi_env env, napi_value callback, const int argc, napi_value* argv) {
180
+ napi_value global;
181
+ napi_get_global(env, &global);
182
+ return napi_call_function(env, global, callback, argc, argv, nullptr);
183
+ }
193
184
 
194
- if (napi_get_array_length(env, arr, &length) == napi_ok) {
195
- result.reserve(length);
185
+ static napi_value ToError(napi_env env, const rocksdb::Status& status) {
186
+ if (status.ok()) {
187
+ return 0;
188
+ }
196
189
 
197
- for (uint32_t i = 0; i < length; i++) {
198
- napi_value element;
190
+ const auto msg = status.ToString();
199
191
 
200
- if (napi_get_element(env, arr, i, &element) == napi_ok &&
201
- StringOrBufferLength(env, element) > 0) {
202
- result.push_back(ToString(env, element));
203
- }
192
+ if (status.IsNotFound()) {
193
+ return CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
194
+ } else if (status.IsCorruption()) {
195
+ return CreateCodeError(env, "LEVEL_CORRUPTION", msg);
196
+ } else if (status.IsIOError()) {
197
+ if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
198
+ return CreateCodeError(env, "LEVEL_LOCKED", msg);
199
+ } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
200
+ return CreateCodeError(env, "LEVEL_LOCKED", msg);
201
+ } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
202
+ return CreateCodeError(env, "LEVEL_LOCKED", msg);
203
+ } else {
204
+ return CreateCodeError(env, "LEVEL_IO_ERROR", msg);
204
205
  }
205
206
  }
206
207
 
207
- return result;
208
- }
209
-
210
- static napi_status CallFunction (napi_env env, napi_value callback, const int argc, napi_value* argv) {
211
- napi_value global;
212
- napi_get_global(env, &global);
213
- return napi_call_function(env, global, callback, argc, argv, nullptr);
208
+ return CreateError(env, msg);
214
209
  }
215
210
 
216
211
  template <typename T>
217
- void Convert (napi_env env, const T& s, bool asBuffer, napi_value& result) {
212
+ void Convert(napi_env env, const T& s, bool asBuffer, napi_value& result) {
218
213
  if (asBuffer) {
219
214
  napi_create_buffer_copy(env, s.size(), s.data(), nullptr, &result);
220
215
  } else {
@@ -222,112 +217,101 @@ void Convert (napi_env env, const T& s, bool asBuffer, napi_value& result) {
222
217
  }
223
218
  }
224
219
 
220
+ struct NapiSlice : public rocksdb::Slice {
221
+ NapiSlice(napi_env env, napi_value from) {
222
+ if (IsString(env, from)) {
223
+ napi_get_value_string_utf8(env, from, nullptr, 0, &size_);
224
+ char* data;
225
+ if (size_ + 1 < stack_.size()) {
226
+ data = stack_.data();
227
+ } else {
228
+ heap_.reset(new char[size_ + 1]);
229
+ data = heap_.get();
230
+ }
231
+ data[size_] = 0;
232
+ napi_get_value_string_utf8(env, from, data, size_ + 1, &size_);
233
+ data_ = data;
234
+ } else if (IsBuffer(env, from)) {
235
+ void* data;
236
+ napi_get_buffer_info(env, from, &data, &size_);
237
+ data_ = static_cast<char*>(data);
238
+ }
239
+ }
240
+
241
+ std::unique_ptr<char[]> heap_;
242
+ std::array<char, 8192> stack_;
243
+ };
244
+
225
245
  /**
226
246
  * Base worker class. Handles the async work. Derived classes can override the
227
247
  * following virtual methods (listed in the order in which they're called):
228
248
  *
229
249
  * - Execute (abstract, worker pool thread): main work
230
- * - Then (main thread): call JS callback on success
231
- * - Catch (main thread): call JS callback on error
232
- * - Finally (main thread): do cleanup regardless of success
250
+ * - OnOk (main thread): call JS callback on success
251
+ * - OnError (main thread): call JS callback on error
252
+ * - Destroy (main thread): do cleanup regardless of success
233
253
  */
234
- struct BaseWorker {
235
- BaseWorker (napi_env env,
236
- Database* database,
237
- napi_value callback,
238
- const std::string& resourceName)
239
- : database_(database) {
254
+ struct Worker {
255
+ Worker(napi_env env, Database* database, napi_value callback, const std::string& resourceName)
256
+ : database_(database) {
240
257
  NAPI_STATUS_THROWS_VOID(napi_create_reference(env, callback, 1, &callbackRef_));
241
258
  napi_value asyncResourceName;
242
- NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName.data(),
243
- NAPI_AUTO_LENGTH,
244
- &asyncResourceName));
245
- NAPI_STATUS_THROWS_VOID(napi_create_async_work(env, callback,
246
- asyncResourceName,
247
- BaseWorker::Execute,
248
- BaseWorker::Complete,
249
- this, &asyncWork_));
259
+ NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName.data(), resourceName.size(), &asyncResourceName));
260
+ NAPI_STATUS_THROWS_VOID(napi_create_async_work(env, callback, asyncResourceName, Worker::Execute,
261
+ Worker::Complete, this, &asyncWork_));
250
262
  }
251
263
 
252
- virtual ~BaseWorker () {}
264
+ virtual ~Worker() {}
253
265
 
254
- static void Execute (napi_env env, void* data) {
255
- auto self = reinterpret_cast<BaseWorker*>(data);
256
- self->status_ = self->Execute();
266
+ static void Execute(napi_env env, void* data) {
267
+ auto self = reinterpret_cast<Worker*>(data);
268
+ self->status_ = self->Execute(*self->database_);
257
269
  }
258
270
 
259
- static void Complete (napi_env env, napi_status status, void* data) {
260
- auto self = reinterpret_cast<BaseWorker*>(data);
271
+ static void Complete(napi_env env, napi_status status, void* data) {
272
+ auto self = reinterpret_cast<Worker*>(data);
261
273
 
262
274
  napi_value callback;
263
275
  napi_get_reference_value(env, self->callbackRef_, &callback);
264
276
 
265
277
  if (self->status_.ok()) {
266
- self->Then(env, callback);
278
+ self->OnOk(env, callback);
267
279
  } else {
268
- self->Catch(env, callback);
280
+ self->OnError(env, callback, ToError(env, self->status_));
269
281
  }
270
282
 
271
- self->Finally(env);
272
- }
283
+ self->Destroy(env);
273
284
 
274
- virtual rocksdb::Status Execute () = 0;
285
+ napi_delete_reference(env, self->callbackRef_);
286
+ napi_delete_async_work(env, self->asyncWork_);
275
287
 
276
- virtual void Then (napi_env env, napi_value callback) {
277
- napi_value argv;
278
- napi_get_null(env, &argv);
279
- CallFunction(env, callback, 1, &argv);
288
+ delete self;
280
289
  }
281
290
 
282
- virtual void Catch (napi_env env, napi_value callback) {
283
- napi_value argv;
284
-
285
- const auto msg = status_.ToString();
286
-
287
- if (status_.IsNotFound()) {
288
- argv = CreateCodeError(env, "LEVEL_NOT_FOUND", msg);
289
- } else if (status_.IsCorruption()) {
290
- argv = CreateCodeError(env, "LEVEL_CORRUPTION", msg);
291
- } else if (status_.IsIOError()) {
292
- if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
293
- argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
294
- } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
295
- argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
296
- } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
297
- argv = CreateCodeError(env, "LEVEL_LOCKED", msg);
298
- } else {
299
- argv = CreateCodeError(env, "LEVEL_IO_ERROR", msg);
300
- }
301
- } else {
302
- argv = CreateError(env, msg);
303
- }
291
+ virtual rocksdb::Status Execute(Database& database) = 0;
304
292
 
293
+ virtual void OnOk(napi_env env, napi_value callback) {
294
+ napi_value argv;
295
+ napi_get_null(env, &argv);
305
296
  CallFunction(env, callback, 1, &argv);
306
297
  }
307
298
 
308
- virtual void Finally (napi_env env) {
309
- napi_delete_reference(env, callbackRef_);
310
- napi_delete_async_work(env, asyncWork_);
299
+ virtual void OnError(napi_env env, napi_value callback, napi_value err) { CallFunction(env, callback, 1, &err); }
311
300
 
312
- delete this;
313
- }
301
+ virtual void Destroy(napi_env env) {}
314
302
 
315
- void Queue (napi_env env) {
316
- napi_queue_async_work(env, asyncWork_);
317
- }
303
+ void Queue(napi_env env) { napi_queue_async_work(env, asyncWork_); }
318
304
 
319
305
  Database* database_;
320
306
 
321
- private:
307
+ private:
322
308
  napi_ref callbackRef_;
323
309
  napi_async_work asyncWork_;
324
310
  rocksdb::Status status_;
325
311
  };
326
312
 
327
313
  struct Database {
328
- rocksdb::Status Open (const rocksdb::Options& options,
329
- const bool readOnly,
330
- const char* location) {
314
+ rocksdb::Status Open(const rocksdb::Options& options, const bool readOnly, const char* location) {
331
315
  if (readOnly) {
332
316
  rocksdb::DB* db = nullptr;
333
317
  const auto status = rocksdb::DB::OpenForReadOnly(options, location, &db);
@@ -341,75 +325,19 @@ struct Database {
341
325
  }
342
326
  }
343
327
 
344
- void CloseDatabase () {
345
- db_.reset();
346
- }
347
-
348
- rocksdb::Status Put (const rocksdb::WriteOptions& options,
349
- const std::string& key,
350
- const std::string& value) {
351
- return db_->Put(options, db_->DefaultColumnFamily(), key, value);
352
- }
353
-
354
- rocksdb::Status Get (const rocksdb::ReadOptions& options,
355
- const std::string& key,
356
- rocksdb::PinnableSlice& value) {
357
- return db_->Get(options, db_->DefaultColumnFamily(), key, &value);
358
- }
359
-
360
- rocksdb::Status Del (const rocksdb::WriteOptions& options,
361
- const std::string& key) {
362
- return db_->Delete(options, db_->DefaultColumnFamily(), key);
363
- }
364
-
365
- rocksdb::Status WriteBatch (const rocksdb::WriteOptions& options,
366
- rocksdb::WriteBatch* batch) {
367
- return db_->Write(options, batch);
368
- }
369
-
370
- uint64_t ApproximateSize (const rocksdb::Range* range) {
371
- uint64_t size = 0;
372
- db_->GetApproximateSizes(range, 1, &size);
373
- return size;
374
- }
375
-
376
- void CompactRange (const rocksdb::Slice* start,
377
- const rocksdb::Slice* end) {
378
- rocksdb::CompactRangeOptions options;
379
- db_->CompactRange(options, start, end);
380
- }
381
-
382
- void GetProperty (const std::string& property, std::string& value) {
383
- db_->GetProperty(property, &value);
384
- }
385
-
386
- const rocksdb::Snapshot* NewSnapshot () {
387
- return db_->GetSnapshot();
388
- }
389
-
390
- rocksdb::Iterator* NewIterator (const rocksdb::ReadOptions& options) {
391
- return db_->NewIterator(options);
392
- }
393
-
394
- void ReleaseSnapshot (const rocksdb::Snapshot* snapshot) {
395
- return db_->ReleaseSnapshot(snapshot);
396
- }
397
-
398
- void AttachIterator (napi_env env, Iterator* iterator) {
328
+ void AttachIterator(napi_env env, Iterator* iterator) {
399
329
  iterators_.insert(iterator);
400
330
  IncrementPriorityWork(env);
401
331
  }
402
332
 
403
- void DetachIterator (napi_env env, Iterator* iterator) {
333
+ void DetachIterator(napi_env env, Iterator* iterator) {
404
334
  iterators_.erase(iterator);
405
335
  DecrementPriorityWork(env);
406
336
  }
407
337
 
408
- void IncrementPriorityWork (napi_env env) {
409
- napi_reference_ref(env, prioritRef_, &priorityWork_);
410
- }
338
+ void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, prioritRef_, &priorityWork_); }
411
339
 
412
- void DecrementPriorityWork (napi_env env) {
340
+ void DecrementPriorityWork(napi_env env) {
413
341
  napi_reference_unref(env, prioritRef_, &priorityWork_);
414
342
 
415
343
  if (priorityWork_ == 0 && pendingCloseWorker_) {
@@ -418,208 +346,106 @@ struct Database {
418
346
  }
419
347
  }
420
348
 
421
- bool HasPriorityWork () const {
422
- return priorityWork_ > 0;
423
- }
349
+ bool HasPriorityWork() const { return priorityWork_ > 0; }
424
350
 
425
351
  std::unique_ptr<rocksdb::DB> db_;
426
- BaseWorker* pendingCloseWorker_;
352
+ Worker* pendingCloseWorker_;
427
353
  std::set<Iterator*> iterators_;
428
354
  napi_ref prioritRef_;
429
355
 
430
- private:
356
+ private:
431
357
  uint32_t priorityWork_ = 0;
432
358
  };
433
359
 
434
- /**
435
- * Base worker class for doing async work that defers closing the database.
436
- */
437
- struct PriorityWorker : public BaseWorker {
438
- PriorityWorker (napi_env env, Database* database, napi_value callback, const char* resourceName)
439
- : BaseWorker(env, database, callback, resourceName) {
440
- database_->IncrementPriorityWork(env);
441
- }
442
-
443
- virtual ~PriorityWorker () {}
444
-
445
- void Finally (napi_env env) override {
446
- database_->DecrementPriorityWork(env);
447
- BaseWorker::Finally(env);
448
- }
449
- };
450
-
451
360
  struct BaseIterator {
452
361
  BaseIterator(Database* database,
453
362
  const bool reverse,
454
- const std::string* lt,
455
- const std::string* lte,
456
- const std::string* gt,
457
- const std::string* gte,
363
+ const std::optional<std::string>& lt,
364
+ const std::optional<std::string>& lte,
365
+ const std::optional<std::string>& gt,
366
+ const std::optional<std::string>& gte,
458
367
  const int limit,
459
368
  const bool fillCache)
460
- : database_(database),
461
- lt_(lt),
462
- lte_(lte),
463
- gt_(gt),
464
- gte_(gte),
465
- snapshot_(database->NewSnapshot(), [this](const rocksdb::Snapshot* ptr) {
466
- database_->ReleaseSnapshot(ptr);
467
- }),
468
- iterator_(database->NewIterator([&]{
469
- rocksdb::ReadOptions options;
470
- if (lt_ && !lte_) {
471
- upper_bound_ = rocksdb::Slice(lt_->data(), lt_->size());
472
- options.iterate_upper_bound = &upper_bound_;
473
- }
474
- if (gte_) {
475
- lower_bound_ = rocksdb::Slice(gte_->data(), gte_->size());
476
- options.iterate_lower_bound = &lower_bound_;
477
- }
478
- options.fill_cache = fillCache;
479
- options.snapshot = snapshot_.get();
480
- return options;
481
- }())),
482
- reverse_(reverse),
483
- limit_(limit) {
484
- }
485
-
486
- virtual ~BaseIterator () {
487
- assert(!iterator_);
488
- }
489
-
490
- bool DidSeek () const {
491
- return didSeek_;
492
- }
493
-
494
- void SeekToRange () {
369
+ : database_(database),
370
+ lt_(!lte ? lt : *lte + '\0'),
371
+ gte_(gte ? gte : (gt ? std::optional<std::string>(*gt + '\0') : std::nullopt)),
372
+ snapshot_(database_->db_->GetSnapshot(),
373
+ [this](const rocksdb::Snapshot* ptr) { database_->db_->ReleaseSnapshot(ptr); }),
374
+ iterator_(database->db_->NewIterator([&] {
375
+ rocksdb::ReadOptions options;
376
+ if (lt_) {
377
+ upper_bound_ = rocksdb::Slice(lt_->data(), lt_->size());
378
+ options.iterate_upper_bound = &upper_bound_;
379
+ }
380
+ if (gte_) {
381
+ lower_bound_ = rocksdb::Slice(gte_->data(), gte_->size());
382
+ options.iterate_lower_bound = &lower_bound_;
383
+ }
384
+ options.fill_cache = fillCache;
385
+ options.snapshot = snapshot_.get();
386
+ return options;
387
+ }())),
388
+ reverse_(reverse),
389
+ limit_(limit) {}
390
+
391
+ virtual ~BaseIterator() { assert(!iterator_); }
392
+
393
+ bool DidSeek() const { return didSeek_; }
394
+
395
+ void SeekToRange() {
495
396
  didSeek_ = true;
496
397
 
497
- if (!reverse_ && gt_ && !gte_) {
498
- iterator_->Seek(*gt_);
499
-
500
- if (iterator_->Valid() && iterator_->key().compare(*gt_) == 0) {
501
- iterator_->Next();
502
- }
503
- } else if (reverse_ && lte_) {
504
- iterator_->Seek(*lte_);
505
-
506
- if (!iterator_->Valid()) {
507
- iterator_->SeekToLast();
508
- } else if (iterator_->key().compare(*lte_) > 0) {
509
- iterator_->Prev();
510
- }
511
- } else if (reverse_) {
398
+ if (reverse_) {
512
399
  iterator_->SeekToLast();
513
400
  } else {
514
401
  iterator_->SeekToFirst();
515
402
  }
516
403
  }
517
404
 
518
- void Seek (const std::string& target) {
405
+ void Seek(const std::string& target) {
519
406
  didSeek_ = true;
520
407
 
521
- if (OutOfRange(target)) {
522
- return SeekToEnd();
523
- }
524
-
525
- iterator_->Seek(target);
526
-
527
- if (iterator_->Valid()) {
528
- const auto cmp = iterator_->key().compare(target);
529
- if (reverse_ ? cmp > 0 : cmp < 0) {
530
- Next();
531
- }
532
- } else {
533
- SeekToFirst();
408
+ if ((lt_ && target.compare(*lt_) >= 0) || (gte_ && target.compare(*gte_) < 0)) {
409
+ // TODO (fix): Why is this required? Seek should handle it?
410
+ // https://github.com/facebook/rocksdb/issues/9904
411
+ iterator_->SeekToLast();
534
412
  if (iterator_->Valid()) {
535
- const auto cmp = iterator_->key().compare(target);
536
- if (reverse_ ? cmp > 0 : cmp < 0) {
537
- SeekToEnd();
538
- }
413
+ iterator_->Next();
539
414
  }
415
+ } else if (reverse_) {
416
+ iterator_->SeekForPrev(target);
417
+ } else {
418
+ iterator_->Seek(target);
540
419
  }
541
420
  }
542
421
 
543
- void Close () {
422
+ void Close() {
544
423
  snapshot_.reset();
545
424
  iterator_.reset();
546
425
  }
547
426
 
548
- bool Valid () const {
549
- if (!iterator_->Valid()) {
550
- return false;
551
- }
427
+ bool Valid() const { return iterator_->Valid(); }
552
428
 
553
- if (lte_ && iterator_->key().compare(*lte_) > 0) {
554
- return false;
555
- }
429
+ bool Increment() { return limit_ < 0 || ++count_ <= limit_; }
556
430
 
557
- if (!gte_ && gt_ && iterator_->key().compare(*gt_) <= 0) {
558
- return false;
559
- }
560
-
561
- return true;
431
+ void Next() {
432
+ if (reverse_)
433
+ iterator_->Prev();
434
+ else
435
+ iterator_->Next();
562
436
  }
563
437
 
564
- bool Increment () {
565
- return limit_ < 0 || ++count_ <= limit_;
566
- }
438
+ rocksdb::Slice CurrentKey() const { return iterator_->key(); }
567
439
 
568
- void Next () {
569
- if (reverse_) iterator_->Prev();
570
- else iterator_->Next();
571
- }
440
+ rocksdb::Slice CurrentValue() const { return iterator_->value(); }
572
441
 
573
- void SeekToFirst () {
574
- if (reverse_) iterator_->SeekToLast();
575
- else iterator_->SeekToFirst();
576
- }
577
-
578
- void SeekToLast () {
579
- if (reverse_) iterator_->SeekToFirst();
580
- else iterator_->SeekToLast();
581
- }
582
-
583
- void SeekToEnd () {
584
- SeekToLast();
585
- Next();
586
- }
587
-
588
- rocksdb::Slice CurrentKey () const {
589
- return iterator_->key();
590
- }
591
-
592
- rocksdb::Slice CurrentValue () const {
593
- return iterator_->value();
594
- }
595
-
596
- rocksdb::Status Status () const {
597
- return iterator_->status();
598
- }
599
-
600
- bool OutOfRange (const rocksdb::Slice& target) const {
601
- if (lte_) {
602
- if (target.compare(*lte_) > 0) return true;
603
- } else if (lt_) {
604
- if (target.compare(*lt_) >= 0) return true;
605
- }
606
-
607
- if (gte_) {
608
- if (target.compare(*gte_) < 0) return true;
609
- } else if (gt_) {
610
- if (target.compare(*gt_) <= 0) return true;
611
- }
612
-
613
- return false;
614
- }
442
+ rocksdb::Status Status() const { return iterator_->status(); }
615
443
 
616
444
  Database* database_;
617
445
 
618
- private:
619
- const std::unique_ptr<const std::string> lt_;
620
- const std::unique_ptr<const std::string> lte_;
621
- const std::unique_ptr<const std::string> gt_;
622
- const std::unique_ptr<const std::string> gte_;
446
+ private:
447
+ const std::optional<std::string> lt_;
448
+ const std::optional<std::string> gte_;
623
449
  rocksdb::Slice lower_bound_;
624
450
  rocksdb::Slice upper_bound_;
625
451
  std::shared_ptr<const rocksdb::Snapshot> snapshot_;
@@ -631,37 +457,38 @@ private:
631
457
  };
632
458
 
633
459
  struct Iterator final : public BaseIterator {
634
- Iterator (Database* database,
635
- const bool reverse,
636
- const bool keys,
637
- const bool values,
638
- const int limit,
639
- const std::string* lt,
640
- const std::string* lte,
641
- const std::string* gt,
642
- const std::string* gte,
643
- const bool fillCache,
644
- const bool keyAsBuffer,
645
- const bool valueAsBuffer,
646
- const uint32_t highWaterMarkBytes)
647
- : BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
648
- keys_(keys),
649
- values_(values),
650
- keyAsBuffer_(keyAsBuffer),
651
- valueAsBuffer_(valueAsBuffer),
652
- highWaterMarkBytes_(highWaterMarkBytes),
653
- first_(true),
654
- ref_(nullptr) {
655
- }
656
-
657
- void Attach (napi_env env, napi_value context) {
460
+ Iterator(Database* database,
461
+ const bool reverse,
462
+ const bool keys,
463
+ const bool values,
464
+ const int limit,
465
+ const std::optional<std::string>& lt,
466
+ const std::optional<std::string>& lte,
467
+ const std::optional<std::string>& gt,
468
+ const std::optional<std::string>& gte,
469
+ const bool fillCache,
470
+ const bool keyAsBuffer,
471
+ const bool valueAsBuffer,
472
+ const uint32_t highWaterMarkBytes)
473
+ : BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache),
474
+ keys_(keys),
475
+ values_(values),
476
+ keyAsBuffer_(keyAsBuffer),
477
+ valueAsBuffer_(valueAsBuffer),
478
+ highWaterMarkBytes_(highWaterMarkBytes),
479
+ first_(true),
480
+ ref_(nullptr) {}
481
+
482
+ void Attach(napi_env env, napi_value context) {
658
483
  napi_create_reference(env, context, 1, &ref_);
659
484
  database_->AttachIterator(env, this);
660
485
  }
661
486
 
662
- void Detach (napi_env env) {
487
+ void Detach(napi_env env) {
663
488
  database_->DetachIterator(env, this);
664
- if (ref_) napi_delete_reference(env, ref_);
489
+ if (ref_) {
490
+ napi_delete_reference(env, ref_);
491
+ }
665
492
  }
666
493
 
667
494
  const bool keys_;
@@ -671,7 +498,7 @@ struct Iterator final : public BaseIterator {
671
498
  const uint32_t highWaterMarkBytes_;
672
499
  bool first_;
673
500
 
674
- private:
501
+ private:
675
502
  napi_ref ref_;
676
503
  };
677
504
 
@@ -680,15 +507,16 @@ private:
680
507
  * already-scheduled napi_async_work items have finished, which gives us
681
508
  * the guarantee that no db operations will be in-flight at this time.
682
509
  */
683
- static void env_cleanup_hook (void* arg) {
510
+ static void env_cleanup_hook(void* arg) {
684
511
  auto database = reinterpret_cast<Database*>(arg);
685
512
 
686
- // Do everything that db_close() does but synchronously. We're expecting that GC
687
- // did not (yet) collect the database because that would be a user mistake (not
688
- // closing their db) made during the lifetime of the environment. That's different
689
- // from an environment being torn down (like the main process or a worker thread)
690
- // where it's our responsibility to clean up. Note also, the following code must
691
- // be a safe noop if called before db_open() or after db_close().
513
+ // Do everything that db_close() does but synchronously. We're expecting that
514
+ // GC did not (yet) collect the database because that would be a user mistake
515
+ // (not closing their db) made during the lifetime of the environment. That's
516
+ // different from an environment being torn down (like the main process or a
517
+ // worker thread) where it's our responsibility to clean up. Note also, the
518
+ // following code must be a safe noop if called before db_open() or after
519
+ // db_close().
692
520
  if (database && database->db_) {
693
521
  // TODO: does not do `napi_delete_reference(env, iterator->ref_)`. Problem?
694
522
  for (auto it : database->iterators_) {
@@ -696,15 +524,16 @@ static void env_cleanup_hook (void* arg) {
696
524
  }
697
525
 
698
526
  // Having closed the iterators (and released snapshots) we can safely close.
699
- database->CloseDatabase();
527
+ database->db_->Close();
700
528
  }
701
529
  }
702
530
 
703
- static void FinalizeDatabase (napi_env env, void* data, void* hint) {
531
+ static void FinalizeDatabase(napi_env env, void* data, void* hint) {
704
532
  if (data) {
705
533
  auto database = reinterpret_cast<Database*>(data);
706
534
  napi_remove_env_cleanup_hook(env, env_cleanup_hook, database);
707
- if (database->prioritRef_) napi_delete_reference(env, database->prioritRef_);
535
+ if (database->prioritRef_)
536
+ napi_delete_reference(env, database->prioritRef_);
708
537
  delete database;
709
538
  }
710
539
  }
@@ -714,39 +543,33 @@ NAPI_METHOD(db_init) {
714
543
  napi_add_env_cleanup_hook(env, env_cleanup_hook, database);
715
544
 
716
545
  napi_value result;
717
- NAPI_STATUS_THROWS(napi_create_external(env, database,
718
- FinalizeDatabase,
719
- nullptr, &result));
546
+ NAPI_STATUS_THROWS(napi_create_external(env, database, FinalizeDatabase, nullptr, &result));
720
547
 
721
548
  NAPI_STATUS_THROWS(napi_create_reference(env, result, 0, &database->prioritRef_));
722
549
 
723
550
  return result;
724
551
  }
725
552
 
726
- struct OpenWorker final : public PriorityWorker {
727
- OpenWorker (napi_env env,
728
- Database* database,
729
- napi_value callback,
730
- const std::string& location,
731
- const bool createIfMissing,
732
- const bool errorIfExists,
733
- const bool compression,
734
- const uint32_t writeBufferSize,
735
- const uint32_t blockSize,
736
- const uint32_t maxOpenFiles,
737
- const uint32_t blockRestartInterval,
738
- const uint32_t maxFileSize,
739
- const uint32_t cacheSize,
740
- const std::string& infoLogLevel,
741
- const bool readOnly)
742
- : PriorityWorker(env, database, callback, "leveldown.db.open"),
743
- readOnly_(readOnly),
744
- location_(location) {
553
+ struct OpenWorker final : public Worker {
554
+ OpenWorker(napi_env env,
555
+ Database* database,
556
+ napi_value callback,
557
+ const std::string& location,
558
+ const bool createIfMissing,
559
+ const bool errorIfExists,
560
+ const bool compression,
561
+ const uint32_t writeBufferSize,
562
+ const uint32_t blockSize,
563
+ const uint32_t maxOpenFiles,
564
+ const uint32_t blockRestartInterval,
565
+ const uint32_t maxFileSize,
566
+ const uint32_t cacheSize,
567
+ const std::string& infoLogLevel,
568
+ const bool readOnly)
569
+ : Worker(env, database, callback, "leveldown.db.open"), readOnly_(readOnly), location_(location) {
745
570
  options_.create_if_missing = createIfMissing;
746
571
  options_.error_if_exists = errorIfExists;
747
- options_.compression = compression
748
- ? rocksdb::kSnappyCompression
749
- : rocksdb::kNoCompression;
572
+ options_.compression = compression ? rocksdb::kSnappyCompression : rocksdb::kNoCompression;
750
573
  options_.write_buffer_size = writeBufferSize;
751
574
  options_.max_open_files = maxOpenFiles;
752
575
  options_.max_log_file_size = maxFileSize;
@@ -755,13 +578,20 @@ struct OpenWorker final : public PriorityWorker {
755
578
  if (infoLogLevel.size() > 0) {
756
579
  rocksdb::InfoLogLevel lvl = {};
757
580
 
758
- if (infoLogLevel == "debug") lvl = rocksdb::InfoLogLevel::DEBUG_LEVEL;
759
- else if (infoLogLevel == "info") lvl = rocksdb::InfoLogLevel::INFO_LEVEL;
760
- else if (infoLogLevel == "warn") lvl = rocksdb::InfoLogLevel::WARN_LEVEL;
761
- else if (infoLogLevel == "error") lvl = rocksdb::InfoLogLevel::ERROR_LEVEL;
762
- else if (infoLogLevel == "fatal") lvl = rocksdb::InfoLogLevel::FATAL_LEVEL;
763
- else if (infoLogLevel == "header") lvl = rocksdb::InfoLogLevel::HEADER_LEVEL;
764
- else napi_throw_error(env, nullptr, "invalid log level");
581
+ if (infoLogLevel == "debug")
582
+ lvl = rocksdb::InfoLogLevel::DEBUG_LEVEL;
583
+ else if (infoLogLevel == "info")
584
+ lvl = rocksdb::InfoLogLevel::INFO_LEVEL;
585
+ else if (infoLogLevel == "warn")
586
+ lvl = rocksdb::InfoLogLevel::WARN_LEVEL;
587
+ else if (infoLogLevel == "error")
588
+ lvl = rocksdb::InfoLogLevel::ERROR_LEVEL;
589
+ else if (infoLogLevel == "fatal")
590
+ lvl = rocksdb::InfoLogLevel::FATAL_LEVEL;
591
+ else if (infoLogLevel == "header")
592
+ lvl = rocksdb::InfoLogLevel::HEADER_LEVEL;
593
+ else
594
+ napi_throw_error(env, nullptr, "invalid log level");
765
595
 
766
596
  options_.info_log_level = lvl;
767
597
  } else {
@@ -785,14 +615,10 @@ struct OpenWorker final : public PriorityWorker {
785
615
  tableOptions.format_version = 5;
786
616
  tableOptions.checksum = rocksdb::kxxHash64;
787
617
 
788
- options_.table_factory.reset(
789
- rocksdb::NewBlockBasedTableFactory(tableOptions)
790
- );
618
+ options_.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tableOptions));
791
619
  }
792
620
 
793
- rocksdb::Status Execute () override {
794
- return database_->Open(options_, readOnly_, location_.c_str());
795
- }
621
+ rocksdb::Status Execute(Database& database) override { return database.Open(options_, readOnly_, location_.c_str()); }
796
622
 
797
623
  rocksdb::Options options_;
798
624
  const bool readOnly_;
@@ -813,39 +639,30 @@ NAPI_METHOD(db_open) {
813
639
  const auto infoLogLevel = StringProperty(env, options, "infoLogLevel");
814
640
 
815
641
  const auto cacheSize = Uint32Property(env, options, "cacheSize", 8 << 20);
816
- const auto writeBufferSize = Uint32Property(env, options , "writeBufferSize" , 4 << 20);
642
+ const auto writeBufferSize = Uint32Property(env, options, "writeBufferSize", 4 << 20);
817
643
  const auto blockSize = Uint32Property(env, options, "blockSize", 4096);
818
644
  const auto maxOpenFiles = Uint32Property(env, options, "maxOpenFiles", 1000);
819
- const auto blockRestartInterval = Uint32Property(env, options,
820
- "blockRestartInterval", 16);
645
+ const auto blockRestartInterval = Uint32Property(env, options, "blockRestartInterval", 16);
821
646
  const auto maxFileSize = Uint32Property(env, options, "maxFileSize", 2 << 20);
822
647
 
823
648
  const auto callback = argv[3];
824
649
 
825
- auto worker = new OpenWorker(env, database, callback, location,
826
- createIfMissing, errorIfExists,
827
- compression, writeBufferSize, blockSize,
828
- maxOpenFiles, blockRestartInterval,
829
- maxFileSize, cacheSize,
830
- infoLogLevel, readOnly);
650
+ auto worker =
651
+ new OpenWorker(env, database, callback, location, createIfMissing, errorIfExists, compression, writeBufferSize,
652
+ blockSize, maxOpenFiles, blockRestartInterval, maxFileSize, cacheSize, infoLogLevel, readOnly);
831
653
  worker->Queue(env);
832
654
 
833
655
  return 0;
834
656
  }
835
657
 
836
- struct CloseWorker final : public BaseWorker {
837
- CloseWorker (napi_env env,
838
- Database* database,
839
- napi_value callback)
840
- : BaseWorker(env, database, callback, "leveldown.db.close") {}
658
+ struct CloseWorker final : public Worker {
659
+ CloseWorker(napi_env env, Database* database, napi_value callback)
660
+ : Worker(env, database, callback, "leveldown.db.close") {}
841
661
 
842
- rocksdb::Status Execute () override {
843
- database_->CloseDatabase();
844
- return rocksdb::Status::OK();
845
- }
662
+ rocksdb::Status Execute(Database& database) override { return database.db_->Close(); }
846
663
  };
847
664
 
848
- napi_value noop_callback (napi_env env, napi_callback_info info) {
665
+ napi_value noop_callback(napi_env env, napi_callback_info info) {
849
666
  return 0;
850
667
  }
851
668
 
@@ -866,72 +683,62 @@ NAPI_METHOD(db_close) {
866
683
  return 0;
867
684
  }
868
685
 
869
- struct PutWorker final : public PriorityWorker {
870
- PutWorker (napi_env env,
871
- Database* database,
872
- napi_value callback,
873
- const std::string& key,
874
- const std::string& value,
875
- bool sync)
876
- : PriorityWorker(env, database, callback, "rocks_level.db.put"),
877
- key_(key), value_(value), sync_(sync) {
878
- }
879
-
880
- rocksdb::Status Execute () override {
881
- rocksdb::WriteOptions options;
882
- options.sync = sync_;
883
- return database_->Put(options, key_, value_);
884
- }
885
-
886
- const std::string key_;
887
- const std::string value_;
888
- const bool sync_;
889
- };
890
-
891
686
  NAPI_METHOD(db_put) {
892
- NAPI_ARGV(5);
687
+ NAPI_ARGV(4);
893
688
  NAPI_DB_CONTEXT();
894
689
 
895
690
  const auto key = ToString(env, argv[1]);
896
691
  const auto value = ToString(env, argv[2]);
897
- const auto sync = BooleanProperty(env, argv[3], "sync", false);
898
- const auto callback = argv[4];
899
-
900
- auto worker = new PutWorker(env, database, callback, key, value, sync);
901
- worker->Queue(env);
902
692
 
903
- return 0;
693
+ rocksdb::WriteOptions options;
694
+ return ToError(env, database->db_->Put(options, key, value));
904
695
  }
905
696
 
906
- struct GetWorker final : public PriorityWorker {
907
- GetWorker (napi_env env,
908
- Database* database,
909
- napi_value callback,
910
- const std::string& key,
911
- const bool asBuffer,
912
- const bool fillCache)
913
- : PriorityWorker(env, database, callback, "rocks_level.db.get"),
914
- key_(key), asBuffer_(asBuffer), fillCache_(fillCache) {
697
+ struct GetWorker final : public Worker {
698
+ GetWorker(napi_env env,
699
+ Database* database,
700
+ napi_value callback,
701
+ const std::string& key,
702
+ const bool asBuffer,
703
+ const bool fillCache)
704
+ : Worker(env, database, callback, "rocks_level.db.get"),
705
+ key_(key),
706
+ asBuffer_(asBuffer),
707
+ fillCache_(fillCache),
708
+ snapshot_(database_->db_->GetSnapshot(),
709
+ [this](const rocksdb::Snapshot* ptr) { database_->db_->ReleaseSnapshot(ptr); }) {
710
+ database_->IncrementPriorityWork(env);
915
711
  }
916
712
 
917
- rocksdb::Status Execute () override {
713
+ rocksdb::Status Execute(Database& database) override {
918
714
  rocksdb::ReadOptions options;
919
715
  options.fill_cache = fillCache_;
920
- return database_->Get(options, key_, value_);
716
+ options.snapshot = snapshot_.get();
717
+
718
+ auto status = database.db_->Get(options, database.db_->DefaultColumnFamily(), key_, &value_);
719
+ snapshot_ = nullptr;
720
+
721
+ return status;
921
722
  }
922
723
 
923
- void Then (napi_env env, napi_value callback) override {
724
+ void OnOk(napi_env env, napi_value callback) override {
924
725
  napi_value argv[2];
925
726
  napi_get_null(env, &argv[0]);
926
727
  Convert(env, std::move(value_), asBuffer_, argv[1]);
927
728
  CallFunction(env, callback, 2, argv);
928
729
  }
929
730
 
930
- private:
731
+ void Destroy(napi_env env) override {
732
+ database_->DecrementPriorityWork(env);
733
+ Worker::Destroy(env);
734
+ }
735
+
736
+ private:
931
737
  const std::string key_;
932
738
  rocksdb::PinnableSlice value_;
933
739
  const bool asBuffer_;
934
740
  const bool fillCache_;
741
+ std::shared_ptr<const rocksdb::Snapshot> snapshot_;
935
742
  };
936
743
 
937
744
  NAPI_METHOD(db_get) {
@@ -950,37 +757,28 @@ NAPI_METHOD(db_get) {
950
757
  return 0;
951
758
  }
952
759
 
953
- struct GetManyWorker final : public PriorityWorker {
954
- GetManyWorker (napi_env env,
955
- Database* database,
956
- const std::vector<std::string>& keys,
957
- napi_value callback,
958
- const bool valueAsBuffer,
959
- const bool fillCache)
960
- : PriorityWorker(env, database, callback, "leveldown.get.many"),
961
- keys_(keys), valueAsBuffer_(valueAsBuffer), fillCache_(fillCache),
962
- snapshot_(database->NewSnapshot()) {
963
- }
964
-
965
- ~GetManyWorker () {
966
- if (snapshot_) {
967
- database_->ReleaseSnapshot(snapshot_);
968
- snapshot_ = nullptr;
969
- }
760
+ struct GetManyWorker final : public Worker {
761
+ GetManyWorker(napi_env env,
762
+ Database* database,
763
+ const std::vector<std::string>& keys,
764
+ napi_value callback,
765
+ const bool valueAsBuffer,
766
+ const bool fillCache)
767
+ : Worker(env, database, callback, "leveldown.get.many"),
768
+ keys_(keys),
769
+ valueAsBuffer_(valueAsBuffer),
770
+ fillCache_(fillCache),
771
+ snapshot_(database_->db_->GetSnapshot(),
772
+ [this](const rocksdb::Snapshot* ptr) { database_->db_->ReleaseSnapshot(ptr); }) {
773
+ database_->IncrementPriorityWork(env);
970
774
  }
971
775
 
972
- rocksdb::Status Execute () override {
776
+ rocksdb::Status Execute(Database& database) override {
973
777
  rocksdb::ReadOptions options;
974
- options.snapshot = snapshot_;
975
778
  options.fill_cache = fillCache_;
976
-
977
- status_ = database_->db_->MultiGet(
978
- options,
979
- std::vector<rocksdb::Slice>(keys_.begin(), keys_.end()),
980
- &values_
981
- );
982
-
983
- database_->ReleaseSnapshot(snapshot_);
779
+ options.snapshot = snapshot_.get();
780
+
781
+ status_ = database.db_->MultiGet(options, std::vector<rocksdb::Slice>(keys_.begin(), keys_.end()), &values_);
984
782
  snapshot_ = nullptr;
985
783
 
986
784
  for (auto status : status_) {
@@ -992,7 +790,7 @@ struct GetManyWorker final : public PriorityWorker {
992
790
  return rocksdb::Status::OK();
993
791
  }
994
792
 
995
- void Then (napi_env env, napi_value callback) override {
793
+ void OnOk(napi_env env, napi_value callback) override {
996
794
  const auto size = values_.size();
997
795
 
998
796
  napi_value array;
@@ -1014,20 +812,39 @@ struct GetManyWorker final : public PriorityWorker {
1014
812
  CallFunction(env, callback, 2, argv);
1015
813
  }
1016
814
 
1017
- private:
815
+ void Destroy(napi_env env) override {
816
+ database_->DecrementPriorityWork(env);
817
+ Worker::Destroy(env);
818
+ }
819
+
820
+ private:
1018
821
  const std::vector<std::string> keys_;
1019
822
  std::vector<std::string> values_;
1020
823
  std::vector<rocksdb::Status> status_;
1021
824
  const bool valueAsBuffer_;
1022
825
  const bool fillCache_;
1023
- const rocksdb::Snapshot* snapshot_;
826
+ std::shared_ptr<const rocksdb::Snapshot> snapshot_;
1024
827
  };
1025
828
 
1026
829
  NAPI_METHOD(db_get_many) {
1027
830
  NAPI_ARGV(4);
1028
831
  NAPI_DB_CONTEXT();
1029
832
 
1030
- const auto keys = KeyArray(env, argv[1]);
833
+ std::vector<std::string> keys;
834
+ {
835
+ uint32_t length;
836
+ NAPI_STATUS_THROWS(napi_get_array_length(env, argv[1], &length));
837
+
838
+ keys.reserve(length);
839
+
840
+ for (uint32_t i = 0; i < length; i++) {
841
+ napi_value element;
842
+
843
+ NAPI_STATUS_THROWS(napi_get_element(env, argv[1], i, &element));
844
+ keys.push_back(ToString(env, element));
845
+ }
846
+ }
847
+
1031
848
  const auto options = argv[2];
1032
849
  const bool asBuffer = EncodingIsBuffer(env, options, "valueEncoding");
1033
850
  const bool fillCache = BooleanProperty(env, options, "fillCache", true);
@@ -1039,190 +856,67 @@ NAPI_METHOD(db_get_many) {
1039
856
  return 0;
1040
857
  }
1041
858
 
1042
- struct DelWorker final : public PriorityWorker {
1043
- DelWorker (napi_env env,
1044
- Database* database,
1045
- napi_value callback,
1046
- const std::string& key,
1047
- bool sync)
1048
- : PriorityWorker(env, database, callback, "rocks_level.db.del"),
1049
- key_(key), sync_(sync) {
1050
- }
1051
-
1052
- rocksdb::Status Execute () override {
1053
- rocksdb::WriteOptions options;
1054
- options.sync = sync_;
1055
- return database_->Del(options, key_);
1056
- }
1057
-
1058
- const std::string key_;
1059
- const bool sync_;
1060
- };
1061
-
1062
859
  NAPI_METHOD(db_del) {
1063
- NAPI_ARGV(4);
860
+ NAPI_ARGV(3);
1064
861
  NAPI_DB_CONTEXT();
1065
862
 
1066
863
  const auto key = ToString(env, argv[1]);
1067
- const auto sync = BooleanProperty(env, argv[2], "sync", false);
1068
- const auto callback = argv[3];
1069
864
 
1070
- auto worker = new DelWorker(env, database, callback, key, sync);
1071
- worker->Queue(env);
1072
-
1073
- return 0;
865
+ rocksdb::WriteOptions options;
866
+ return ToError(env, database->db_->Delete(options, key));
1074
867
  }
1075
868
 
1076
- struct ClearWorker final : public PriorityWorker {
1077
- ClearWorker (napi_env env,
1078
- Database* database,
1079
- napi_value callback,
1080
- const bool reverse,
1081
- const int limit,
1082
- const std::string* lt,
1083
- const std::string* lte,
1084
- const std::string* gt,
1085
- const std::string* gte)
1086
- : PriorityWorker(env, database, callback, "rocks_level.db.clear"),
1087
- iterator_(database, reverse, lt, lte, gt, gte, limit, false) {
1088
- }
1089
-
1090
- rocksdb::Status Execute () override {
1091
- iterator_.SeekToRange();
1092
-
1093
- // TODO: add option
1094
- const uint32_t hwm = 16 * 1024;
1095
-
1096
- rocksdb::WriteBatch batch;
1097
- rocksdb::WriteOptions options;
1098
- rocksdb::Status status;
1099
-
1100
- while (true) {
1101
- size_t bytesRead = 0;
1102
-
1103
- while (bytesRead <= hwm && iterator_.Valid() && iterator_.Increment()) {
1104
- const auto key = iterator_.CurrentKey();
1105
- batch.Delete(key);
1106
- bytesRead += key.size();
1107
- iterator_.Next();
1108
- }
1109
-
1110
- status = iterator_.Status();
1111
- if (!status.ok() || bytesRead == 0) {
1112
- break;
1113
- }
1114
-
1115
- status = database_->WriteBatch(options, &batch);
1116
- if (!status.ok()) {
1117
- break;
1118
- }
1119
-
1120
- batch.Clear();
1121
- }
1122
-
1123
- iterator_.Close();
1124
-
1125
- return status;
1126
- }
1127
-
1128
- private:
1129
- BaseIterator iterator_;
1130
- };
1131
-
1132
869
  NAPI_METHOD(db_clear) {
1133
- NAPI_ARGV(3);
870
+ NAPI_ARGV(2);
1134
871
  NAPI_DB_CONTEXT();
1135
872
 
1136
- napi_value options = argv[1];
1137
- napi_value callback = argv[2];
873
+ const auto reverse = BooleanProperty(env, argv[1], "reverse", false);
874
+ const auto limit = Int32Property(env, argv[1], "limit", -1);
1138
875
 
1139
- const auto reverse = BooleanProperty(env, options, "reverse", false);
1140
- const auto limit = Int32Property(env, options, "limit", -1);
1141
-
1142
- const auto lt = RangeOption(env, options, "lt");
1143
- const auto lte = RangeOption(env, options, "lte");
1144
- const auto gt = RangeOption(env, options, "gt");
1145
- const auto gte = RangeOption(env, options, "gte");
876
+ const auto lt = RangeOption(env, argv[1], "lt");
877
+ const auto lte = RangeOption(env, argv[1], "lte");
878
+ const auto gt = RangeOption(env, argv[1], "gt");
879
+ const auto gte = RangeOption(env, argv[1], "gte");
1146
880
 
1147
- auto worker = new ClearWorker(env, database, callback, reverse, limit, lt, lte, gt, gte);
1148
- worker->Queue(env);
881
+ // TODO (perf): Use DeleteRange.
1149
882
 
1150
- return 0;
1151
- }
883
+ BaseIterator it(database, reverse, lt, lte, gt, gte, limit, false);
1152
884
 
1153
- struct ApproximateSizeWorker final : public PriorityWorker {
1154
- ApproximateSizeWorker (napi_env env,
1155
- Database* database,
1156
- napi_value callback,
1157
- const std::string& start,
1158
- const std::string& end)
1159
- : PriorityWorker(env, database, callback, "rocks_level.db.approximate_size"),
1160
- start_(start), end_(end) {}
1161
-
1162
- rocksdb::Status Execute () override {
1163
- rocksdb::Range range(start_, end_);
1164
- size_ = database_->ApproximateSize(&range);
1165
- return rocksdb::Status::OK();
1166
- }
885
+ it.SeekToRange();
1167
886
 
1168
- void Then (napi_env env, napi_value callback) override {
1169
- napi_value argv[2];
1170
- napi_get_null(env, &argv[0]);
1171
- napi_create_int64(env, size_, &argv[1]);
1172
- CallFunction(env, callback, 2, argv);
1173
- }
887
+ // TODO: add option
888
+ const uint32_t hwm = 16 * 1024;
1174
889
 
1175
- std::string start_;
1176
- std::string end_;
1177
- uint64_t size_;
1178
- };
890
+ rocksdb::WriteBatch batch;
891
+ rocksdb::WriteOptions options;
892
+ rocksdb::Status status;
1179
893
 
1180
- NAPI_METHOD(db_approximate_size) {
1181
- NAPI_ARGV(4);
1182
- NAPI_DB_CONTEXT();
894
+ while (true) {
895
+ size_t bytesRead = 0;
1183
896
 
1184
- const auto start = ToString(env, argv[1]);
1185
- const auto end = ToString(env, argv[2]);
1186
- const auto callback = argv[3];
897
+ while (bytesRead <= hwm && it.Valid() && it.Increment()) {
898
+ const auto key = it.CurrentKey();
899
+ batch.Delete(key);
900
+ bytesRead += key.size();
901
+ it.Next();
902
+ }
1187
903
 
1188
- auto worker = new ApproximateSizeWorker(env, database, callback, start, end);
1189
- worker->Queue(env);
904
+ status = it.Status();
905
+ if (!status.ok() || bytesRead == 0) {
906
+ break;
907
+ }
1190
908
 
1191
- return 0;
1192
- }
909
+ status = database->db_->Write(options, &batch);
910
+ if (!status.ok()) {
911
+ break;
912
+ }
1193
913
 
1194
- struct CompactRangeWorker final : public PriorityWorker {
1195
- CompactRangeWorker (napi_env env,
1196
- Database* database,
1197
- napi_value callback,
1198
- const std::string& start,
1199
- const std::string& end)
1200
- : PriorityWorker(env, database, callback, "rocks_level.db.compact_range"),
1201
- start_(start), end_(end) {}
1202
-
1203
- rocksdb::Status Execute () override {
1204
- rocksdb::Slice start = start_;
1205
- rocksdb::Slice end = end_;
1206
- database_->CompactRange(&start, &end);
1207
- return rocksdb::Status::OK();
914
+ batch.Clear();
1208
915
  }
1209
916
 
1210
- const std::string start_;
1211
- const std::string end_;
1212
- };
1213
-
1214
- NAPI_METHOD(db_compact_range) {
1215
- NAPI_ARGV(4);
1216
- NAPI_DB_CONTEXT();
1217
-
1218
- const auto start = ToString(env, argv[1]);
1219
- const auto end = ToString(env, argv[2]);
1220
- const auto callback = argv[3];
1221
-
1222
- auto worker = new CompactRangeWorker(env, database, callback, start, end);
1223
- worker->Queue(env);
917
+ it.Close();
1224
918
 
1225
- return 0;
919
+ return ToError(env, status);
1226
920
  }
1227
921
 
1228
922
  NAPI_METHOD(db_get_property) {
@@ -1232,7 +926,7 @@ NAPI_METHOD(db_get_property) {
1232
926
  const auto property = ToString(env, argv[1]);
1233
927
 
1234
928
  std::string value;
1235
- database->GetProperty(property, value);
929
+ database->db_->GetProperty(property, &value);
1236
930
 
1237
931
  napi_value result;
1238
932
  napi_create_string_utf8(env, value.data(), value.size(), &result);
@@ -1240,63 +934,7 @@ NAPI_METHOD(db_get_property) {
1240
934
  return result;
1241
935
  }
1242
936
 
1243
- struct DestroyWorker final : public BaseWorker {
1244
- DestroyWorker (napi_env env,
1245
- const std::string& location,
1246
- napi_value callback)
1247
- : BaseWorker(env, nullptr, callback, "rocks_level.destroy_db"),
1248
- location_(location) {}
1249
-
1250
- ~DestroyWorker () {}
1251
-
1252
- rocksdb::Status Execute () override {
1253
- rocksdb::Options options;
1254
- return rocksdb::DestroyDB(location_, options);
1255
- }
1256
-
1257
- const std::string location_;
1258
- };
1259
-
1260
- NAPI_METHOD(destroy_db) {
1261
- NAPI_ARGV(2);
1262
-
1263
- const auto location = ToString(env, argv[0]);
1264
- const auto callback = argv[1];
1265
-
1266
- auto worker = new DestroyWorker(env, location, callback);
1267
- worker->Queue(env);
1268
-
1269
- return 0;
1270
- }
1271
-
1272
- struct RepairWorker final : public BaseWorker {
1273
- RepairWorker (napi_env env,
1274
- const std::string& location,
1275
- napi_value callback)
1276
- : BaseWorker(env, nullptr, callback, "rocks_level.repair_db"),
1277
- location_(location) {}
1278
-
1279
- rocksdb::Status Execute () override {
1280
- rocksdb::Options options;
1281
- return rocksdb::RepairDB(location_, options);
1282
- }
1283
-
1284
- const std::string location_;
1285
- };
1286
-
1287
- NAPI_METHOD(repair_db) {
1288
- NAPI_ARGV(2);
1289
-
1290
- const auto location = ToString(env, argv[1]);
1291
- const auto callback = argv[1];
1292
-
1293
- auto worker = new RepairWorker(env, location, callback);
1294
- worker->Queue(env);
1295
-
1296
- return 0;
1297
- }
1298
-
1299
- static void FinalizeIterator (napi_env env, void* data, void* hint) {
937
+ static void FinalizeIterator(napi_env env, void* data, void* hint) {
1300
938
  if (data) {
1301
939
  delete reinterpret_cast<Iterator*>(data);
1302
940
  }
@@ -1321,14 +959,11 @@ NAPI_METHOD(iterator_init) {
1321
959
  const auto gt = RangeOption(env, options, "gt");
1322
960
  const auto gte = RangeOption(env, options, "gte");
1323
961
 
1324
- auto iterator = new Iterator(database, reverse, keys,
1325
- values, limit, lt, lte, gt, gte, fillCache,
1326
- keyAsBuffer, valueAsBuffer, highWaterMarkBytes);
962
+ auto iterator = new Iterator(database, reverse, keys, values, limit, lt, lte, gt, gte, fillCache, keyAsBuffer,
963
+ valueAsBuffer, highWaterMarkBytes);
1327
964
  napi_value result;
1328
965
 
1329
- NAPI_STATUS_THROWS(napi_create_external(env, iterator,
1330
- FinalizeIterator,
1331
- nullptr, &result));
966
+ NAPI_STATUS_THROWS(napi_create_external(env, iterator, FinalizeIterator, nullptr, &result));
1332
967
 
1333
968
  // Prevent GC of JS object before the iterator is closed (explicitly or on
1334
969
  // db close) and keep track of non-closed iterators to end them on db close.
@@ -1348,24 +983,21 @@ NAPI_METHOD(iterator_seek) {
1348
983
  return 0;
1349
984
  }
1350
985
 
1351
- struct CloseIteratorWorker final : public BaseWorker {
1352
- CloseIteratorWorker (napi_env env,
1353
- Iterator* iterator,
1354
- napi_value callback)
1355
- : BaseWorker(env, iterator->database_, callback, "leveldown.iterator.end"),
1356
- iterator_(iterator) {}
986
+ struct CloseIteratorWorker final : public Worker {
987
+ CloseIteratorWorker(napi_env env, Iterator* iterator, napi_value callback)
988
+ : Worker(env, iterator->database_, callback, "leveldown.iterator.end"), iterator_(iterator) {}
1357
989
 
1358
- rocksdb::Status Execute () override {
990
+ rocksdb::Status Execute(Database& database) override {
1359
991
  iterator_->Close();
1360
992
  return rocksdb::Status::OK();
1361
993
  }
1362
994
 
1363
- void Finally (napi_env env) override {
995
+ void Destroy(napi_env env) override {
1364
996
  iterator_->Detach(env);
1365
- BaseWorker::Finally(env);
997
+ Worker::Destroy(env);
1366
998
  }
1367
999
 
1368
- private:
1000
+ private:
1369
1001
  Iterator* iterator_;
1370
1002
  };
1371
1003
 
@@ -1381,16 +1013,11 @@ NAPI_METHOD(iterator_close) {
1381
1013
  return 0;
1382
1014
  }
1383
1015
 
1384
- struct NextWorker final : public BaseWorker {
1385
- NextWorker (napi_env env,
1386
- Iterator* iterator,
1387
- uint32_t size,
1388
- napi_value callback)
1389
- : BaseWorker(env, iterator->database_, callback,
1390
- "leveldown.iterator.next"),
1391
- iterator_(iterator), size_(size) {}
1016
+ struct NextWorker final : public Worker {
1017
+ NextWorker(napi_env env, Iterator* iterator, uint32_t size, napi_value callback)
1018
+ : Worker(env, iterator->database_, callback, "leveldown.iterator.next"), iterator_(iterator), size_(size) {}
1392
1019
 
1393
- rocksdb::Status Execute () override {
1020
+ rocksdb::Status Execute(Database& database) override {
1394
1021
  if (!iterator_->DidSeek()) {
1395
1022
  iterator_->SeekToRange();
1396
1023
  }
@@ -1398,16 +1025,18 @@ struct NextWorker final : public BaseWorker {
1398
1025
  // Limit the size of the cache to prevent starving the event loop
1399
1026
  // in JS-land while we're recursively calling process.nextTick().
1400
1027
 
1401
- finished_ = false;
1402
1028
  cache_.reserve(size_ * 2);
1403
1029
  size_t bytesRead = 0;
1404
1030
 
1405
1031
  while (true) {
1406
- if (!iterator_->first_) iterator_->Next();
1407
- else iterator_->first_ = false;
1032
+ if (!iterator_->first_)
1033
+ iterator_->Next();
1034
+ else
1035
+ iterator_->first_ = false;
1036
+
1037
+ if (!iterator_->Valid() || !iterator_->Increment())
1038
+ break;
1408
1039
 
1409
- if (!iterator_->Valid() || !iterator_->Increment()) break;
1410
-
1411
1040
  if (iterator_->keys_ && iterator_->values_) {
1412
1041
  auto k = iterator_->CurrentKey();
1413
1042
  auto v = iterator_->CurrentValue();
@@ -1435,7 +1064,7 @@ struct NextWorker final : public BaseWorker {
1435
1064
  return iterator_->Status();
1436
1065
  }
1437
1066
 
1438
- void Then (napi_env env, napi_value callback) override {
1067
+ void OnOk(napi_env env, napi_value callback) override {
1439
1068
  const auto size = cache_.size();
1440
1069
  napi_value result;
1441
1070
  napi_create_array_with_length(env, size, &result);
@@ -1460,7 +1089,7 @@ struct NextWorker final : public BaseWorker {
1460
1089
  CallFunction(env, callback, 3, argv);
1461
1090
  }
1462
1091
 
1463
- private:
1092
+ private:
1464
1093
  std::vector<std::string> cache_;
1465
1094
  Iterator* iterator_ = nullptr;
1466
1095
  uint32_t size_ = 0;
@@ -1473,7 +1102,8 @@ NAPI_METHOD(iterator_nextv) {
1473
1102
 
1474
1103
  uint32_t size;
1475
1104
  NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &size));
1476
- if (size == 0) size = 1;
1105
+ if (size == 0)
1106
+ size = 1;
1477
1107
 
1478
1108
  const auto callback = argv[2];
1479
1109
 
@@ -1483,74 +1113,51 @@ NAPI_METHOD(iterator_nextv) {
1483
1113
  return 0;
1484
1114
  }
1485
1115
 
1486
- struct BatchWorker final : public PriorityWorker {
1487
- BatchWorker (napi_env env,
1488
- Database* database,
1489
- napi_value callback,
1490
- napi_value array,
1491
- const bool sync)
1492
- : PriorityWorker(env, database, callback, "rocks_level.batch.do"), sync_(sync) {
1493
- uint32_t length;
1494
- NAPI_STATUS_THROWS_VOID(napi_get_array_length(env, array, &length));
1495
-
1496
- for (uint32_t i = 0; i < length; i++) {
1497
- napi_value element;
1498
- NAPI_STATUS_THROWS_VOID(napi_get_element(env, array, i, &element));
1116
+ NAPI_METHOD(batch_do) {
1117
+ NAPI_ARGV(3);
1118
+ NAPI_DB_CONTEXT();
1499
1119
 
1500
- if (!IsObject(env, element)) continue;
1120
+ const auto operations = argv[1];
1501
1121
 
1502
- const auto type = StringProperty(env, element, "type");
1122
+ rocksdb::WriteBatch batch;
1503
1123
 
1504
- if (type == "del") {
1505
- if (!HasProperty(env, element, "key")) continue;
1506
- const auto key = ToString(env, GetProperty(env, element, "key"));
1124
+ uint32_t length;
1125
+ NAPI_STATUS_THROWS(napi_get_array_length(env, operations, &length));
1507
1126
 
1508
- batch_.Delete(key);
1509
- if (!hasData_) hasData_ = true;
1510
- } else if (type == "put") {
1511
- if (!HasProperty(env, element, "key")) continue;
1512
- if (!HasProperty(env, element, "value")) continue;
1127
+ for (uint32_t i = 0; i < length; i++) {
1128
+ napi_value element;
1129
+ NAPI_STATUS_THROWS(napi_get_element(env, operations, i, &element));
1513
1130
 
1514
- const auto key = ToString(env, GetProperty(env, element, "key"));
1515
- const auto value = ToString(env, GetProperty(env, element, "value"));
1131
+ if (!IsObject(env, element))
1132
+ continue;
1516
1133
 
1517
- batch_.Put(key, value);
1518
- if (!hasData_) hasData_ = true;
1519
- }
1520
- }
1521
- }
1134
+ const auto type = StringProperty(env, element, "type");
1522
1135
 
1523
- rocksdb::Status Execute () override {
1524
- if (!hasData_) {
1525
- return rocksdb::Status::OK();
1526
- }
1136
+ if (type == "del") {
1137
+ if (!HasProperty(env, element, "key"))
1138
+ continue;
1527
1139
 
1528
- rocksdb::WriteOptions options;
1529
- options.sync = sync_;
1530
- return database_->WriteBatch(options, &batch_);
1531
- }
1140
+ const auto key = NapiSlice(env, GetProperty(env, element, "key"));
1532
1141
 
1533
- private:
1534
- rocksdb::WriteBatch batch_;
1535
- const bool sync_;
1536
- bool hasData_;
1537
- };
1538
-
1539
- NAPI_METHOD(batch_do) {
1540
- NAPI_ARGV(4);
1541
- NAPI_DB_CONTEXT();
1142
+ batch.Delete(key);
1143
+ } else if (type == "put") {
1144
+ if (!HasProperty(env, element, "key"))
1145
+ continue;
1146
+ if (!HasProperty(env, element, "value"))
1147
+ continue;
1542
1148
 
1543
- const auto array = argv[1];
1544
- const auto sync = BooleanProperty(env, argv[2], "sync", false);
1545
- const auto callback = argv[3];
1149
+ const auto key = NapiSlice(env, GetProperty(env, element, "key"));
1150
+ const auto value = NapiSlice(env, GetProperty(env, element, "value"));
1546
1151
 
1547
- auto worker = new BatchWorker(env, database, callback, array, sync);
1548
- worker->Queue(env);
1152
+ batch.Put(key, value);
1153
+ }
1154
+ }
1549
1155
 
1550
- return 0;
1156
+ rocksdb::WriteOptions options;
1157
+ return ToError(env, database->db_->Write(options, &batch));
1551
1158
  }
1552
1159
 
1553
- static void FinalizeBatch (napi_env env, void* data, void* hint) {
1160
+ static void FinalizeBatch(napi_env env, void* data, void* hint) {
1554
1161
  if (data) {
1555
1162
  delete reinterpret_cast<rocksdb::WriteBatch*>(data);
1556
1163
  }
@@ -1571,8 +1178,8 @@ NAPI_METHOD(batch_put) {
1571
1178
  NAPI_ARGV(3);
1572
1179
  NAPI_BATCH_CONTEXT();
1573
1180
 
1574
- const auto key = ToString(env, argv[1]);
1575
- const auto value = ToString(env, argv[2]);
1181
+ const auto key = NapiSlice(env, argv[1]);
1182
+ const auto value = NapiSlice(env, argv[2]);
1576
1183
 
1577
1184
  batch->Put(key, value);
1578
1185
 
@@ -1583,7 +1190,7 @@ NAPI_METHOD(batch_del) {
1583
1190
  NAPI_ARGV(2);
1584
1191
  NAPI_BATCH_CONTEXT();
1585
1192
 
1586
- const auto key = ToString(env, argv[1]);
1193
+ const auto key = NapiSlice(env, argv[1]);
1587
1194
 
1588
1195
  batch->Delete(key);
1589
1196
 
@@ -1599,51 +1206,15 @@ NAPI_METHOD(batch_clear) {
1599
1206
  return 0;
1600
1207
  }
1601
1208
 
1602
- struct BatchWriteWorker final : public PriorityWorker {
1603
- BatchWriteWorker (napi_env env,
1604
- Database* database,
1605
- napi_value batch,
1606
- napi_value callback,
1607
- const bool sync)
1608
- : PriorityWorker(env, database, callback, "leveldown.batch.write"),
1609
- sync_(sync) {
1610
-
1611
- NAPI_STATUS_THROWS_VOID(napi_get_value_external(env, batch, reinterpret_cast<void**>(&batch_)));
1612
-
1613
- // Prevent GC of batch object before we execute
1614
- NAPI_STATUS_THROWS_VOID(napi_create_reference(env, batch, 1, &batchRef_));
1615
- }
1616
-
1617
- rocksdb::Status Execute () override {
1618
- rocksdb::WriteOptions options;
1619
- options.sync = sync_;
1620
- return database_->WriteBatch(options, batch_);
1621
- }
1622
-
1623
- void Finally (napi_env env) override {
1624
- napi_delete_reference(env, batchRef_);
1625
- PriorityWorker::Finally(env);
1626
- }
1627
-
1628
- private:
1629
- rocksdb::WriteBatch* batch_;
1630
- const bool sync_;
1631
- napi_ref batchRef_;
1632
- };
1633
-
1634
1209
  NAPI_METHOD(batch_write) {
1635
- NAPI_ARGV(4);
1210
+ NAPI_ARGV(3);
1636
1211
  NAPI_DB_CONTEXT();
1637
1212
 
1638
- const auto batch = argv[1];
1639
- const auto options = argv[2];
1640
- const auto sync = BooleanProperty(env, options, "sync", false);
1641
- const auto callback = argv[3];
1642
-
1643
- auto worker = new BatchWriteWorker(env, database, batch, callback, sync);
1644
- worker->Queue(env);
1213
+ rocksdb::WriteBatch* batch;
1214
+ NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
1645
1215
 
1646
- return 0;
1216
+ rocksdb::WriteOptions options;
1217
+ return ToError(env, database->db_->Write(options, batch));
1647
1218
  }
1648
1219
 
1649
1220
  NAPI_INIT() {
@@ -1655,13 +1226,8 @@ NAPI_INIT() {
1655
1226
  NAPI_EXPORT_FUNCTION(db_get_many);
1656
1227
  NAPI_EXPORT_FUNCTION(db_del);
1657
1228
  NAPI_EXPORT_FUNCTION(db_clear);
1658
- NAPI_EXPORT_FUNCTION(db_approximate_size);
1659
- NAPI_EXPORT_FUNCTION(db_compact_range);
1660
1229
  NAPI_EXPORT_FUNCTION(db_get_property);
1661
1230
 
1662
- NAPI_EXPORT_FUNCTION(destroy_db);
1663
- NAPI_EXPORT_FUNCTION(repair_db);
1664
-
1665
1231
  NAPI_EXPORT_FUNCTION(iterator_init);
1666
1232
  NAPI_EXPORT_FUNCTION(iterator_seek);
1667
1233
  NAPI_EXPORT_FUNCTION(iterator_close);