@harperfast/rocksdb-js 1.1.0 → 1.2.0

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/dist/index.mjs CHANGED
@@ -428,42 +428,62 @@ var DBI = class DBI {
428
428
 
429
429
  //#endregion
430
430
  //#region src/dbi-iterator.ts
431
+ const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST } = constants;
432
+ const DONE_RESULT = Object.freeze({
433
+ done: true,
434
+ value: void 0
435
+ });
431
436
  /**
432
- * Wraps an iterator, namely the `NativeIterator` class, and decodes the key
433
- * and value.
437
+ * Wraps the `NativeIterator` C++ binding, decoding keys and values from the
438
+ * shared key/value buffers (fast path) or from per-iteration buffers (slow
439
+ * fallback path used for oversized data or stable-buffer decoders).
440
+ *
441
+ * The native `next()` returns a primitive signal (and writes lengths to the
442
+ * shared `ITERATOR_STATE` buffer) instead of constructing a JS result object,
443
+ * so this class is responsible for building the `IteratorResult`.
434
444
  */
435
445
  var DBIterator = class {
436
446
  iterator;
437
447
  store;
438
448
  #includeValues;
439
- constructor(iterator, store, options) {
449
+ constructor(iterator, store, includeValues) {
440
450
  this.iterator = iterator;
441
451
  this.store = store;
442
- this.#includeValues = options?.values ?? true;
452
+ this.#includeValues = includeValues;
443
453
  }
444
454
  [Symbol.iterator]() {
445
455
  return this;
446
456
  }
447
- next(...[_value]) {
457
+ next() {
448
458
  const result = this.iterator.next();
449
- if (result.done) return result;
459
+ if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
460
+ const includeValues = this.#includeValues;
450
461
  const value = {};
451
- value.key = this.store.decodeKey(result.value.key);
452
- if (this.#includeValues) value.value = this.store.decodeValue(result.value.value);
462
+ if (result === ITERATOR_RESULT_FAST) {
463
+ value.key = this.store.readKey(KEY_BUFFER, 0, ITERATOR_STATE[0]);
464
+ if (includeValues) {
465
+ VALUE_BUFFER.end = ITERATOR_STATE[1];
466
+ value.value = this.store.decodeValue(VALUE_BUFFER);
467
+ }
468
+ } else {
469
+ const slow = result;
470
+ value.key = this.store.decodeKey(slow.key);
471
+ if (includeValues && slow.value !== void 0) value.value = this.store.decodeValue(slow.value);
472
+ }
453
473
  return {
454
474
  done: false,
455
475
  value
456
476
  };
457
477
  }
458
478
  return(value) {
459
- if (this.iterator.return) return this.iterator.return(value);
479
+ this.iterator.return?.();
460
480
  return {
461
481
  done: true,
462
482
  value
463
483
  };
464
484
  }
465
485
  throw(err) {
466
- if (this.iterator.throw) return this.iterator.throw(err);
486
+ this.iterator.throw?.(err);
467
487
  throw err;
468
488
  }
469
489
  };
@@ -538,10 +558,17 @@ function createFixedBuffer(size) {
538
558
 
539
559
  //#endregion
540
560
  //#region src/store.ts
541
- const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_FLAG } = constants;
561
+ const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_FLAG, ITERATOR_REVERSE_FLAG, ITERATOR_INCLUSIVE_END_FLAG, ITERATOR_EXCLUSIVE_START_FLAG, ITERATOR_INCLUDE_VALUES_FLAG, ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG, ITERATOR_CONTEXT_IS_TRANSACTION_FLAG } = constants;
542
562
  const KEY_BUFFER_SIZE = 4096;
543
563
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
544
564
  const VALUE_BUFFER = createFixedBuffer(64 * 1024);
565
+ /**
566
+ * Backing buffer for the shared iterator state. Layout (Uint32Array view):
567
+ * [0] = key length written into KEY_BUFFER by the most recent iterator step
568
+ * [1] = value length written into VALUE_BUFFER by the most recent step
569
+ */
570
+ const ITERATOR_STATE_BUFFER = Buffer.allocUnsafeSlow(8);
571
+ const ITERATOR_STATE = new Uint32Array(ITERATOR_STATE_BUFFER.buffer, ITERATOR_STATE_BUFFER.byteOffset, 2);
545
572
  const MAX_KEY_SIZE = 1024 * 1024;
546
573
  const RESET_BUFFER_MODE = 1024;
547
574
  const REUSE_BUFFER_MODE = 512;
@@ -720,6 +747,52 @@ var Store = class {
720
747
  this.db.close();
721
748
  }
722
749
  /**
750
+ * Compacts the entire key range of the database asynchronously.
751
+ * This triggers manual compaction which removes tombstones and reclaims space.
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * const db = RocksDatabase.open('/path/to/database');
756
+ * await db.compact();
757
+ * ```
758
+ */
759
+ compact(options) {
760
+ let startBuffer;
761
+ let endBuffer;
762
+ if (options?.start !== void 0) {
763
+ const start = this.encodeKey(options.start);
764
+ startBuffer = Buffer.from(start.subarray(start.start, start.end));
765
+ }
766
+ if (options?.end !== void 0) {
767
+ const end = this.encodeKey(options.end);
768
+ endBuffer = Buffer.from(end.subarray(end.start, end.end));
769
+ }
770
+ return new Promise((resolve, reject) => this.db.compact(resolve, reject, startBuffer, endBuffer));
771
+ }
772
+ /**
773
+ * Compacts the entire key range of the database synchronously.
774
+ * This triggers manual compaction which removes tombstones and reclaims space.
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * const db = RocksDatabase.open('/path/to/database');
779
+ * db.compactSync();
780
+ * ```
781
+ */
782
+ compactSync(options) {
783
+ let startBuffer;
784
+ let endBuffer;
785
+ if (options?.start !== void 0) {
786
+ const start = this.encodeKey(options.start);
787
+ startBuffer = Buffer.from(start.subarray(start.start, start.end));
788
+ }
789
+ if (options?.end !== void 0) {
790
+ const end = this.encodeKey(options.end);
791
+ endBuffer = Buffer.from(end.subarray(end.start, end.end));
792
+ }
793
+ this.db.compactSync(startBuffer, endBuffer);
794
+ }
795
+ /**
723
796
  * Decodes a key from the database.
724
797
  *
725
798
  * @param key - The key to decode.
@@ -807,27 +880,45 @@ var Store = class {
807
880
  }
808
881
  getRange(context, options) {
809
882
  if (!this.db.opened) throw new Error("Database not open");
810
- options = { ...options };
811
- const unencodedStartKey = options.key ?? options.start;
812
- if (unencodedStartKey !== void 0) {
813
- const start = this.encodeKey(unencodedStartKey);
814
- options.start = Buffer.from(start.subarray(start.start, start.end));
883
+ let startUnencoded = options?.key ?? options?.start;
884
+ let endUnencoded = options?.key ?? options?.end;
885
+ const includeValues = options?.values ?? true;
886
+ const reverse = options?.reverse ?? false;
887
+ let exclusiveStart = options?.exclusiveStart ?? false;
888
+ let inclusiveEnd = options?.inclusiveEnd ?? false;
889
+ if (options?.key !== void 0) inclusiveEnd = true;
890
+ if (reverse) {
891
+ const tmp = startUnencoded;
892
+ startUnencoded = endUnencoded;
893
+ endUnencoded = tmp;
894
+ exclusiveStart = options?.exclusiveStart ?? true;
895
+ inclusiveEnd = options?.inclusiveEnd ?? true;
815
896
  }
816
- if (options.key !== void 0) {
817
- options.end = options.start;
818
- options.inclusiveEnd = true;
819
- } else if (options.end !== void 0) {
820
- const end = this.encodeKey(options.end);
821
- options.end = Buffer.from(end.subarray(end.start, end.end));
897
+ const keyBuffer = this.keyBuffer;
898
+ let startKeyEnd = 0;
899
+ let endKeyStart = 0;
900
+ let endKeyEnd = 0;
901
+ if (startUnencoded !== void 0) {
902
+ startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
903
+ if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
822
904
  }
823
- if (options.reverse) {
824
- const start = options.start;
825
- options.start = options.end;
826
- options.end = start;
827
- options.exclusiveStart = options.exclusiveStart ?? true;
828
- options.inclusiveEnd = options.inclusiveEnd ?? true;
905
+ if (endUnencoded !== void 0) if (endUnencoded === startUnencoded) {
906
+ endKeyStart = 0;
907
+ endKeyEnd = startKeyEnd;
908
+ } else {
909
+ endKeyStart = startKeyEnd;
910
+ endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
911
+ if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
829
912
  }
830
- return new ExtendedIterable(new DBIterator(new NativeIterator(context, options), this, options));
913
+ let flags = 0;
914
+ if (reverse) flags |= ITERATOR_REVERSE_FLAG;
915
+ if (exclusiveStart) flags |= ITERATOR_EXCLUSIVE_START_FLAG;
916
+ if (inclusiveEnd) flags |= ITERATOR_INCLUSIVE_END_FLAG;
917
+ if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
918
+ if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
919
+ if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
920
+ const advancedOptions = options !== void 0 && (options.adaptiveReadahead !== void 0 || options.asyncIO !== void 0 || options.autoReadaheadSize !== void 0 || options.backgroundPurgeOnIteratorCleanup !== void 0 || options.fillCache !== void 0 || options.readaheadSize !== void 0 || options.tailing !== void 0) ? options : void 0;
921
+ return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues));
831
922
  }
832
923
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
833
924
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -1194,6 +1285,38 @@ var RocksDatabase = class RocksDatabase extends DBI {
1194
1285
  this.store.close();
1195
1286
  }
1196
1287
  /**
1288
+ * Compacts the entire key range of the database asynchronously.
1289
+ * This triggers manual compaction which removes tombstones and reclaims space.
1290
+ *
1291
+ * @example
1292
+ * ```typescript
1293
+ * const db = RocksDatabase.open('/path/to/database');
1294
+ * await db.compact();
1295
+ * ```
1296
+ */
1297
+ compact(options) {
1298
+ return this.store.compact(options);
1299
+ }
1300
+ /**
1301
+ * Compacts the entire key range of the database synchronously.
1302
+ * This triggers manual compaction which removes tombstones and reclaims space.
1303
+ *
1304
+ * @example
1305
+ * ```typescript
1306
+ * const db = RocksDatabase.open('/path/to/database');
1307
+ * db.compactSync();
1308
+ * ```
1309
+ */
1310
+ compactSync(options) {
1311
+ return this.store.compactSync(options);
1312
+ }
1313
+ /**
1314
+ * Returns the list of column families in the RocksDB database.
1315
+ */
1316
+ get columns() {
1317
+ return this.store.db.columns;
1318
+ }
1319
+ /**
1197
1320
  * Set global database settings.
1198
1321
  *
1199
1322
  * @param options - The options for the database.
@@ -1269,6 +1392,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
1269
1392
  return this.store.db.getDBProperty(propertyName);
1270
1393
  }
1271
1394
  /**
1395
+ * Retrieves the estimated number of keys in the database.
1396
+ *
1397
+ * @example
1398
+ * ```typescript
1399
+ * const db = RocksDatabase.open('/path/to/database');
1400
+ * const estimated = db.getEstimatedKeyCount();
1401
+ * console.log(estimated);
1402
+ * ```
1403
+ */
1404
+ getEstimatedKeyCount() {
1405
+ return this.getDBIntProperty("rocksdb.estimate-num-keys") ?? 0;
1406
+ }
1407
+ /**
1272
1408
  * Returns the current timestamp as a monotonically increasing timestamp in
1273
1409
  * milliseconds represented as a decimal number.
1274
1410
  *
@@ -1407,6 +1543,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
1407
1543
  if (store.open()) return this;
1408
1544
  store.db.setDefaultValueBuffer(VALUE_BUFFER);
1409
1545
  store.db.setDefaultKeyBuffer(KEY_BUFFER);
1546
+ store.db.setIteratorState(ITERATOR_STATE_BUFFER);
1410
1547
  /**
1411
1548
  * The encoder initialization precedence is:
1412
1549
  * 1. encoder.Encoder
@@ -1437,14 +1574,14 @@ var RocksDatabase = class RocksDatabase extends DBI {
1437
1574
  return buffer && store.decoder?.decode ? store.decoder.decode(buffer) : void 0;
1438
1575
  };
1439
1576
  opts.saveStructures = (structures, isCompatible) => {
1440
- return this.transactionSync((txn) => {
1577
+ return this.transactionSync((txn, _attempt) => {
1441
1578
  const existingStructuresBuffer = this.getBinarySync(sharedStructuresKey);
1442
1579
  const existingStructures = existingStructuresBuffer && store.decoder?.decode ? store.decoder.decode(existingStructuresBuffer) : void 0;
1443
1580
  if (typeof isCompatible == "function") {
1444
1581
  if (!isCompatible(existingStructures)) return false;
1445
1582
  } else if (existingStructures && existingStructures.length !== isCompatible) return false;
1446
1583
  txn.putSync(sharedStructuresKey, structures);
1447
- });
1584
+ }, { retryOnBusy: true });
1448
1585
  };
1449
1586
  }
1450
1587
  store.encoder = new EncoderClass({
@@ -1461,16 +1598,15 @@ var RocksDatabase = class RocksDatabase extends DBI {
1461
1598
  };
1462
1599
  store.decoder = store.encoder;
1463
1600
  }
1464
- if (typeof store.encoder?.writeKey === "function" && !store.encoder?.encode) {
1465
- store.encoder = {
1466
- ...store.encoder,
1467
- encode: (value, _mode) => {
1468
- const bytesWritten = store.writeKey(value, store.encodeBuffer, 0);
1469
- return store.encodeBuffer.subarray(0, bytesWritten);
1470
- }
1471
- };
1472
- store.encoder.copyBuffers = true;
1473
- }
1601
+ if (typeof store.encoder?.writeKey === "function" && !store.encoder?.encode) store.encoder = {
1602
+ ...store.encoder,
1603
+ encode: (value, _mode) => {
1604
+ const bytesWritten = store.writeKey(value, store.encodeBuffer, 0);
1605
+ return store.encodeBuffer.subarray(0, bytesWritten);
1606
+ },
1607
+ copyBuffers: true
1608
+ };
1609
+ if (store.encoder) store.encoder.name = this.#name;
1474
1610
  if (store.decoder && store.decoder.needsStableBuffer !== true) store.decoderCopies = true;
1475
1611
  if (store.decoder?.readKey && !store.decoder.decode) {
1476
1612
  store.decoder.decode = (buffer) => {
@@ -1779,6 +1915,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
1779
1915
  logBuffer.logId = 0;
1780
1916
  logBuffer.size = 0;
1781
1917
  logBuffer.dataView = new DataView(logBuffer.buffer);
1918
+ size = 0;
1782
1919
  }
1783
1920
  }
1784
1921
  dataView = logBuffer.dataView;
@@ -1906,7 +2043,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
1906
2043
  //#region src/index.ts
1907
2044
  const versions = {
1908
2045
  rocksdb: version,
1909
- "rocksdb-js": "1.1.0"
2046
+ "rocksdb-js": "1.2.0"
1910
2047
  };
1911
2048
 
1912
2049
  //#endregion