@harperfast/rocksdb-js 1.1.1 → 1.3.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,13 +558,18 @@ 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
- const RESET_BUFFER_MODE = 1024;
547
- const REUSE_BUFFER_MODE = 512;
548
573
  const SAVE_BUFFER_SIZE = 8192;
549
574
  /**
550
575
  * A store wraps the `NativeDatabase` binding and database settings so that a
@@ -606,6 +631,24 @@ var Store = class {
606
631
  */
607
632
  maxKeySize;
608
633
  /**
634
+ * The maximum number of memtables that can be queued per column family
635
+ * before writes stall. Higher values absorb write bursts while flushes catch
636
+ * up, at the cost of memory.
637
+ */
638
+ maxWriteBufferNumber;
639
+ /**
640
+ * The bytes of recent memtable history to retain in memory for transaction
641
+ * conflict checking. `-1` derives the value from
642
+ * `maxWriteBufferNumber * writeBufferSize`.
643
+ */
644
+ maxWriteBufferSizeToMaintain;
645
+ /**
646
+ * The total memtable budget in bytes across all column families. When the
647
+ * sum of memtables reaches this size, RocksDB flushes the largest one. `0`
648
+ * disables the global trigger so per-CF `writeBufferSize` drives flushing.
649
+ */
650
+ dbWriteBufferSize;
651
+ /**
609
652
  * The name of the store (e.g. the column family). Defaults to `'default'`.
610
653
  */
611
654
  name;
@@ -615,7 +658,8 @@ var Store = class {
615
658
  noBlockCache;
616
659
  /**
617
660
  * The number of threads to use for parallel operations. This is a RocksDB
618
- * option.
661
+ * option. When undefined, the native layer picks
662
+ * `max(1, hardware_concurrency() / 2)`.
619
663
  */
620
664
  parallelismThreads;
621
665
  /**
@@ -673,6 +717,11 @@ var Store = class {
673
717
  */
674
718
  transactionLogsPath;
675
719
  /**
720
+ * The per-column-family memtable size in bytes at which the memtable is
721
+ * sealed and flushed.
722
+ */
723
+ writeBufferSize;
724
+ /**
676
725
  * The function used to encode keys using the shared `keyBuffer`.
677
726
  */
678
727
  writeKey;
@@ -687,6 +736,7 @@ var Store = class {
687
736
  if (options !== void 0 && options !== null && typeof options !== "object") throw new TypeError("Database options must be an object");
688
737
  const { keyEncoding, readKey, writeKey } = initKeyEncoder(options?.keyEncoding, options?.keyEncoder);
689
738
  this.db = new NativeDatabase();
739
+ this.dbWriteBufferSize = options?.dbWriteBufferSize;
690
740
  this.decoder = options?.decoder ?? null;
691
741
  this.disableWAL = options?.disableWAL ?? false;
692
742
  this.enableStats = options?.enableStats ?? false;
@@ -697,9 +747,11 @@ var Store = class {
697
747
  this.keyBuffer = KEY_BUFFER;
698
748
  this.keyEncoding = keyEncoding;
699
749
  this.maxKeySize = options?.maxKeySize ?? MAX_KEY_SIZE;
750
+ this.maxWriteBufferNumber = options?.maxWriteBufferNumber;
751
+ this.maxWriteBufferSizeToMaintain = options?.maxWriteBufferSizeToMaintain;
700
752
  this.name = options?.name ?? "default";
701
753
  this.noBlockCache = options?.noBlockCache;
702
- this.parallelismThreads = options?.parallelismThreads ?? 1;
754
+ this.parallelismThreads = options?.parallelismThreads;
703
755
  this.path = path;
704
756
  this.pessimistic = options?.pessimistic ?? false;
705
757
  this.readOnly = options?.readOnly ?? false;
@@ -711,6 +763,7 @@ var Store = class {
711
763
  this.transactionLogMaxSize = options?.transactionLogMaxSize;
712
764
  this.transactionLogRetention = options?.transactionLogRetention;
713
765
  this.transactionLogsPath = options?.transactionLogsPath;
766
+ this.writeBufferSize = options?.writeBufferSize;
714
767
  this.writeKey = writeKey;
715
768
  }
716
769
  /**
@@ -720,6 +773,52 @@ var Store = class {
720
773
  this.db.close();
721
774
  }
722
775
  /**
776
+ * Compacts the entire key range of the database asynchronously.
777
+ * This triggers manual compaction which removes tombstones and reclaims space.
778
+ *
779
+ * @example
780
+ * ```typescript
781
+ * const db = RocksDatabase.open('/path/to/database');
782
+ * await db.compact();
783
+ * ```
784
+ */
785
+ compact(options) {
786
+ let startBuffer;
787
+ let endBuffer;
788
+ if (options?.start !== void 0) {
789
+ const start = this.encodeKey(options.start);
790
+ startBuffer = Buffer.from(start.subarray(start.start, start.end));
791
+ }
792
+ if (options?.end !== void 0) {
793
+ const end = this.encodeKey(options.end);
794
+ endBuffer = Buffer.from(end.subarray(end.start, end.end));
795
+ }
796
+ return new Promise((resolve, reject) => this.db.compact(resolve, reject, startBuffer, endBuffer));
797
+ }
798
+ /**
799
+ * Compacts the entire key range of the database synchronously.
800
+ * This triggers manual compaction which removes tombstones and reclaims space.
801
+ *
802
+ * @example
803
+ * ```typescript
804
+ * const db = RocksDatabase.open('/path/to/database');
805
+ * db.compactSync();
806
+ * ```
807
+ */
808
+ compactSync(options) {
809
+ let startBuffer;
810
+ let endBuffer;
811
+ if (options?.start !== void 0) {
812
+ const start = this.encodeKey(options.start);
813
+ startBuffer = Buffer.from(start.subarray(start.start, start.end));
814
+ }
815
+ if (options?.end !== void 0) {
816
+ const end = this.encodeKey(options.end);
817
+ endBuffer = Buffer.from(end.subarray(end.start, end.end));
818
+ }
819
+ this.db.compactSync(startBuffer, endBuffer);
820
+ }
821
+ /**
723
822
  * Decodes a key from the database.
724
823
  *
725
824
  * @param key - The key to decode.
@@ -760,7 +859,7 @@ var Store = class {
760
859
  encodeValue(value) {
761
860
  if (value && value["binary-data"]) return value["binary-data"];
762
861
  if (typeof this.encoder?.encode === "function") {
763
- if (this.encoder.copyBuffers) return this.encoder.encode(value, REUSE_BUFFER_MODE | RESET_BUFFER_MODE);
862
+ if (this.encoder.copyBuffers) return this.encoder.encode(value, 1536);
764
863
  const valueBuffer = this.encoder.encode(value);
765
864
  if (typeof valueBuffer === "string") return Buffer.from(valueBuffer);
766
865
  return valueBuffer;
@@ -807,27 +906,45 @@ var Store = class {
807
906
  }
808
907
  getRange(context, options) {
809
908
  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));
909
+ let startUnencoded = options?.key ?? options?.start;
910
+ let endUnencoded = options?.key ?? options?.end;
911
+ const includeValues = options?.values ?? true;
912
+ const reverse = options?.reverse ?? false;
913
+ let exclusiveStart = options?.exclusiveStart ?? false;
914
+ let inclusiveEnd = options?.inclusiveEnd ?? false;
915
+ if (options?.key !== void 0) inclusiveEnd = true;
916
+ if (reverse) {
917
+ const tmp = startUnencoded;
918
+ startUnencoded = endUnencoded;
919
+ endUnencoded = tmp;
920
+ exclusiveStart = options?.exclusiveStart ?? true;
921
+ inclusiveEnd = options?.inclusiveEnd ?? true;
815
922
  }
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));
923
+ const keyBuffer = this.keyBuffer;
924
+ let startKeyEnd = 0;
925
+ let endKeyStart = 0;
926
+ let endKeyEnd = 0;
927
+ if (startUnencoded !== void 0) {
928
+ startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
929
+ if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
822
930
  }
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;
931
+ if (endUnencoded !== void 0) if (endUnencoded === startUnencoded) {
932
+ endKeyStart = 0;
933
+ endKeyEnd = startKeyEnd;
934
+ } else {
935
+ endKeyStart = startKeyEnd;
936
+ endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
937
+ if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
829
938
  }
830
- return new ExtendedIterable(new DBIterator(new NativeIterator(context, options), this, options));
939
+ let flags = 0;
940
+ if (reverse) flags |= ITERATOR_REVERSE_FLAG;
941
+ if (exclusiveStart) flags |= ITERATOR_EXCLUSIVE_START_FLAG;
942
+ if (inclusiveEnd) flags |= ITERATOR_INCLUSIVE_END_FLAG;
943
+ if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
944
+ if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
945
+ if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
946
+ 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;
947
+ return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues));
831
948
  }
832
949
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
833
950
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -908,8 +1025,11 @@ var Store = class {
908
1025
  open() {
909
1026
  if (this.db.opened) return true;
910
1027
  this.db.open(this.path, {
1028
+ dbWriteBufferSize: this.dbWriteBufferSize,
911
1029
  disableWAL: this.disableWAL,
912
1030
  enableStats: this.enableStats,
1031
+ maxWriteBufferNumber: this.maxWriteBufferNumber,
1032
+ maxWriteBufferSizeToMaintain: this.maxWriteBufferSizeToMaintain,
913
1033
  mode: this.pessimistic ? "pessimistic" : "optimistic",
914
1034
  name: this.name,
915
1035
  noBlockCache: this.noBlockCache,
@@ -919,7 +1039,8 @@ var Store = class {
919
1039
  transactionLogMaxAgeThreshold: this.transactionLogMaxAgeThreshold,
920
1040
  transactionLogMaxSize: this.transactionLogMaxSize,
921
1041
  transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
922
- transactionLogsPath: this.transactionLogsPath
1042
+ transactionLogsPath: this.transactionLogsPath,
1043
+ writeBufferSize: this.writeBufferSize
923
1044
  });
924
1045
  return false;
925
1046
  }
@@ -1194,6 +1315,32 @@ var RocksDatabase = class RocksDatabase extends DBI {
1194
1315
  this.store.close();
1195
1316
  }
1196
1317
  /**
1318
+ * Compacts the entire key range of the database asynchronously.
1319
+ * This triggers manual compaction which removes tombstones and reclaims space.
1320
+ *
1321
+ * @example
1322
+ * ```typescript
1323
+ * const db = RocksDatabase.open('/path/to/database');
1324
+ * await db.compact();
1325
+ * ```
1326
+ */
1327
+ compact(options) {
1328
+ return this.store.compact(options);
1329
+ }
1330
+ /**
1331
+ * Compacts the entire key range of the database synchronously.
1332
+ * This triggers manual compaction which removes tombstones and reclaims space.
1333
+ *
1334
+ * @example
1335
+ * ```typescript
1336
+ * const db = RocksDatabase.open('/path/to/database');
1337
+ * db.compactSync();
1338
+ * ```
1339
+ */
1340
+ compactSync(options) {
1341
+ return this.store.compactSync(options);
1342
+ }
1343
+ /**
1197
1344
  * Returns the list of column families in the RocksDB database.
1198
1345
  */
1199
1346
  get columns() {
@@ -1275,6 +1422,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
1275
1422
  return this.store.db.getDBProperty(propertyName);
1276
1423
  }
1277
1424
  /**
1425
+ * Retrieves the estimated number of keys in the database.
1426
+ *
1427
+ * @example
1428
+ * ```typescript
1429
+ * const db = RocksDatabase.open('/path/to/database');
1430
+ * const estimated = db.getEstimatedKeyCount();
1431
+ * console.log(estimated);
1432
+ * ```
1433
+ */
1434
+ getEstimatedKeyCount() {
1435
+ return this.getDBIntProperty("rocksdb.estimate-num-keys") ?? 0;
1436
+ }
1437
+ /**
1278
1438
  * Returns the current timestamp as a monotonically increasing timestamp in
1279
1439
  * milliseconds represented as a decimal number.
1280
1440
  *
@@ -1413,6 +1573,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
1413
1573
  if (store.open()) return this;
1414
1574
  store.db.setDefaultValueBuffer(VALUE_BUFFER);
1415
1575
  store.db.setDefaultKeyBuffer(KEY_BUFFER);
1576
+ store.db.setIteratorState(ITERATOR_STATE_BUFFER);
1416
1577
  /**
1417
1578
  * The encoder initialization precedence is:
1418
1579
  * 1. encoder.Encoder
@@ -1784,6 +1945,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
1784
1945
  logBuffer.logId = 0;
1785
1946
  logBuffer.size = 0;
1786
1947
  logBuffer.dataView = new DataView(logBuffer.buffer);
1948
+ size = 0;
1787
1949
  }
1788
1950
  }
1789
1951
  dataView = logBuffer.dataView;
@@ -1911,7 +2073,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
1911
2073
  //#region src/index.ts
1912
2074
  const versions = {
1913
2075
  rocksdb: version,
1914
- "rocksdb-js": "1.1.1"
2076
+ "rocksdb-js": "1.3.0"
1915
2077
  };
1916
2078
 
1917
2079
  //#endregion