@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.cjs CHANGED
@@ -453,42 +453,62 @@ var DBI = class DBI {
453
453
 
454
454
  //#endregion
455
455
  //#region src/dbi-iterator.ts
456
+ const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST } = constants;
457
+ const DONE_RESULT = Object.freeze({
458
+ done: true,
459
+ value: void 0
460
+ });
456
461
  /**
457
- * Wraps an iterator, namely the `NativeIterator` class, and decodes the key
458
- * and value.
462
+ * Wraps the `NativeIterator` C++ binding, decoding keys and values from the
463
+ * shared key/value buffers (fast path) or from per-iteration buffers (slow
464
+ * fallback path used for oversized data or stable-buffer decoders).
465
+ *
466
+ * The native `next()` returns a primitive signal (and writes lengths to the
467
+ * shared `ITERATOR_STATE` buffer) instead of constructing a JS result object,
468
+ * so this class is responsible for building the `IteratorResult`.
459
469
  */
460
470
  var DBIterator = class {
461
471
  iterator;
462
472
  store;
463
473
  #includeValues;
464
- constructor(iterator, store, options) {
474
+ constructor(iterator, store, includeValues) {
465
475
  this.iterator = iterator;
466
476
  this.store = store;
467
- this.#includeValues = options?.values ?? true;
477
+ this.#includeValues = includeValues;
468
478
  }
469
479
  [Symbol.iterator]() {
470
480
  return this;
471
481
  }
472
- next(...[_value]) {
482
+ next() {
473
483
  const result = this.iterator.next();
474
- if (result.done) return result;
484
+ if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
485
+ const includeValues = this.#includeValues;
475
486
  const value = {};
476
- value.key = this.store.decodeKey(result.value.key);
477
- if (this.#includeValues) value.value = this.store.decodeValue(result.value.value);
487
+ if (result === ITERATOR_RESULT_FAST) {
488
+ value.key = this.store.readKey(KEY_BUFFER, 0, ITERATOR_STATE[0]);
489
+ if (includeValues) {
490
+ VALUE_BUFFER.end = ITERATOR_STATE[1];
491
+ value.value = this.store.decodeValue(VALUE_BUFFER);
492
+ }
493
+ } else {
494
+ const slow = result;
495
+ value.key = this.store.decodeKey(slow.key);
496
+ if (includeValues && slow.value !== void 0) value.value = this.store.decodeValue(slow.value);
497
+ }
478
498
  return {
479
499
  done: false,
480
500
  value
481
501
  };
482
502
  }
483
503
  return(value) {
484
- if (this.iterator.return) return this.iterator.return(value);
504
+ this.iterator.return?.();
485
505
  return {
486
506
  done: true,
487
507
  value
488
508
  };
489
509
  }
490
510
  throw(err) {
491
- if (this.iterator.throw) return this.iterator.throw(err);
511
+ this.iterator.throw?.(err);
492
512
  throw err;
493
513
  }
494
514
  };
@@ -563,13 +583,18 @@ function createFixedBuffer(size) {
563
583
 
564
584
  //#endregion
565
585
  //#region src/store.ts
566
- const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_FLAG } = constants;
586
+ 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;
567
587
  const KEY_BUFFER_SIZE = 4096;
568
588
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
569
589
  const VALUE_BUFFER = createFixedBuffer(64 * 1024);
590
+ /**
591
+ * Backing buffer for the shared iterator state. Layout (Uint32Array view):
592
+ * [0] = key length written into KEY_BUFFER by the most recent iterator step
593
+ * [1] = value length written into VALUE_BUFFER by the most recent step
594
+ */
595
+ const ITERATOR_STATE_BUFFER = Buffer.allocUnsafeSlow(8);
596
+ const ITERATOR_STATE = new Uint32Array(ITERATOR_STATE_BUFFER.buffer, ITERATOR_STATE_BUFFER.byteOffset, 2);
570
597
  const MAX_KEY_SIZE = 1024 * 1024;
571
- const RESET_BUFFER_MODE = 1024;
572
- const REUSE_BUFFER_MODE = 512;
573
598
  const SAVE_BUFFER_SIZE = 8192;
574
599
  /**
575
600
  * A store wraps the `NativeDatabase` binding and database settings so that a
@@ -631,6 +656,24 @@ var Store = class {
631
656
  */
632
657
  maxKeySize;
633
658
  /**
659
+ * The maximum number of memtables that can be queued per column family
660
+ * before writes stall. Higher values absorb write bursts while flushes catch
661
+ * up, at the cost of memory.
662
+ */
663
+ maxWriteBufferNumber;
664
+ /**
665
+ * The bytes of recent memtable history to retain in memory for transaction
666
+ * conflict checking. `-1` derives the value from
667
+ * `maxWriteBufferNumber * writeBufferSize`.
668
+ */
669
+ maxWriteBufferSizeToMaintain;
670
+ /**
671
+ * The total memtable budget in bytes across all column families. When the
672
+ * sum of memtables reaches this size, RocksDB flushes the largest one. `0`
673
+ * disables the global trigger so per-CF `writeBufferSize` drives flushing.
674
+ */
675
+ dbWriteBufferSize;
676
+ /**
634
677
  * The name of the store (e.g. the column family). Defaults to `'default'`.
635
678
  */
636
679
  name;
@@ -640,7 +683,8 @@ var Store = class {
640
683
  noBlockCache;
641
684
  /**
642
685
  * The number of threads to use for parallel operations. This is a RocksDB
643
- * option.
686
+ * option. When undefined, the native layer picks
687
+ * `max(1, hardware_concurrency() / 2)`.
644
688
  */
645
689
  parallelismThreads;
646
690
  /**
@@ -698,6 +742,11 @@ var Store = class {
698
742
  */
699
743
  transactionLogsPath;
700
744
  /**
745
+ * The per-column-family memtable size in bytes at which the memtable is
746
+ * sealed and flushed.
747
+ */
748
+ writeBufferSize;
749
+ /**
701
750
  * The function used to encode keys using the shared `keyBuffer`.
702
751
  */
703
752
  writeKey;
@@ -712,6 +761,7 @@ var Store = class {
712
761
  if (options !== void 0 && options !== null && typeof options !== "object") throw new TypeError("Database options must be an object");
713
762
  const { keyEncoding, readKey, writeKey } = initKeyEncoder(options?.keyEncoding, options?.keyEncoder);
714
763
  this.db = new NativeDatabase();
764
+ this.dbWriteBufferSize = options?.dbWriteBufferSize;
715
765
  this.decoder = options?.decoder ?? null;
716
766
  this.disableWAL = options?.disableWAL ?? false;
717
767
  this.enableStats = options?.enableStats ?? false;
@@ -722,9 +772,11 @@ var Store = class {
722
772
  this.keyBuffer = KEY_BUFFER;
723
773
  this.keyEncoding = keyEncoding;
724
774
  this.maxKeySize = options?.maxKeySize ?? MAX_KEY_SIZE;
775
+ this.maxWriteBufferNumber = options?.maxWriteBufferNumber;
776
+ this.maxWriteBufferSizeToMaintain = options?.maxWriteBufferSizeToMaintain;
725
777
  this.name = options?.name ?? "default";
726
778
  this.noBlockCache = options?.noBlockCache;
727
- this.parallelismThreads = options?.parallelismThreads ?? 1;
779
+ this.parallelismThreads = options?.parallelismThreads;
728
780
  this.path = path;
729
781
  this.pessimistic = options?.pessimistic ?? false;
730
782
  this.readOnly = options?.readOnly ?? false;
@@ -736,6 +788,7 @@ var Store = class {
736
788
  this.transactionLogMaxSize = options?.transactionLogMaxSize;
737
789
  this.transactionLogRetention = options?.transactionLogRetention;
738
790
  this.transactionLogsPath = options?.transactionLogsPath;
791
+ this.writeBufferSize = options?.writeBufferSize;
739
792
  this.writeKey = writeKey;
740
793
  }
741
794
  /**
@@ -745,6 +798,52 @@ var Store = class {
745
798
  this.db.close();
746
799
  }
747
800
  /**
801
+ * Compacts the entire key range of the database asynchronously.
802
+ * This triggers manual compaction which removes tombstones and reclaims space.
803
+ *
804
+ * @example
805
+ * ```typescript
806
+ * const db = RocksDatabase.open('/path/to/database');
807
+ * await db.compact();
808
+ * ```
809
+ */
810
+ compact(options) {
811
+ let startBuffer;
812
+ let endBuffer;
813
+ if (options?.start !== void 0) {
814
+ const start = this.encodeKey(options.start);
815
+ startBuffer = Buffer.from(start.subarray(start.start, start.end));
816
+ }
817
+ if (options?.end !== void 0) {
818
+ const end = this.encodeKey(options.end);
819
+ endBuffer = Buffer.from(end.subarray(end.start, end.end));
820
+ }
821
+ return new Promise((resolve, reject) => this.db.compact(resolve, reject, startBuffer, endBuffer));
822
+ }
823
+ /**
824
+ * Compacts the entire key range of the database synchronously.
825
+ * This triggers manual compaction which removes tombstones and reclaims space.
826
+ *
827
+ * @example
828
+ * ```typescript
829
+ * const db = RocksDatabase.open('/path/to/database');
830
+ * db.compactSync();
831
+ * ```
832
+ */
833
+ compactSync(options) {
834
+ let startBuffer;
835
+ let endBuffer;
836
+ if (options?.start !== void 0) {
837
+ const start = this.encodeKey(options.start);
838
+ startBuffer = Buffer.from(start.subarray(start.start, start.end));
839
+ }
840
+ if (options?.end !== void 0) {
841
+ const end = this.encodeKey(options.end);
842
+ endBuffer = Buffer.from(end.subarray(end.start, end.end));
843
+ }
844
+ this.db.compactSync(startBuffer, endBuffer);
845
+ }
846
+ /**
748
847
  * Decodes a key from the database.
749
848
  *
750
849
  * @param key - The key to decode.
@@ -785,7 +884,7 @@ var Store = class {
785
884
  encodeValue(value) {
786
885
  if (value && value["binary-data"]) return value["binary-data"];
787
886
  if (typeof this.encoder?.encode === "function") {
788
- if (this.encoder.copyBuffers) return this.encoder.encode(value, REUSE_BUFFER_MODE | RESET_BUFFER_MODE);
887
+ if (this.encoder.copyBuffers) return this.encoder.encode(value, 1536);
789
888
  const valueBuffer = this.encoder.encode(value);
790
889
  if (typeof valueBuffer === "string") return Buffer.from(valueBuffer);
791
890
  return valueBuffer;
@@ -832,27 +931,45 @@ var Store = class {
832
931
  }
833
932
  getRange(context, options) {
834
933
  if (!this.db.opened) throw new Error("Database not open");
835
- options = { ...options };
836
- const unencodedStartKey = options.key ?? options.start;
837
- if (unencodedStartKey !== void 0) {
838
- const start = this.encodeKey(unencodedStartKey);
839
- options.start = Buffer.from(start.subarray(start.start, start.end));
934
+ let startUnencoded = options?.key ?? options?.start;
935
+ let endUnencoded = options?.key ?? options?.end;
936
+ const includeValues = options?.values ?? true;
937
+ const reverse = options?.reverse ?? false;
938
+ let exclusiveStart = options?.exclusiveStart ?? false;
939
+ let inclusiveEnd = options?.inclusiveEnd ?? false;
940
+ if (options?.key !== void 0) inclusiveEnd = true;
941
+ if (reverse) {
942
+ const tmp = startUnencoded;
943
+ startUnencoded = endUnencoded;
944
+ endUnencoded = tmp;
945
+ exclusiveStart = options?.exclusiveStart ?? true;
946
+ inclusiveEnd = options?.inclusiveEnd ?? true;
840
947
  }
841
- if (options.key !== void 0) {
842
- options.end = options.start;
843
- options.inclusiveEnd = true;
844
- } else if (options.end !== void 0) {
845
- const end = this.encodeKey(options.end);
846
- options.end = Buffer.from(end.subarray(end.start, end.end));
948
+ const keyBuffer = this.keyBuffer;
949
+ let startKeyEnd = 0;
950
+ let endKeyStart = 0;
951
+ let endKeyEnd = 0;
952
+ if (startUnencoded !== void 0) {
953
+ startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
954
+ if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
847
955
  }
848
- if (options.reverse) {
849
- const start = options.start;
850
- options.start = options.end;
851
- options.end = start;
852
- options.exclusiveStart = options.exclusiveStart ?? true;
853
- options.inclusiveEnd = options.inclusiveEnd ?? true;
956
+ if (endUnencoded !== void 0) if (endUnencoded === startUnencoded) {
957
+ endKeyStart = 0;
958
+ endKeyEnd = startKeyEnd;
959
+ } else {
960
+ endKeyStart = startKeyEnd;
961
+ endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
962
+ if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
854
963
  }
855
- return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, options), this, options));
964
+ let flags = 0;
965
+ if (reverse) flags |= ITERATOR_REVERSE_FLAG;
966
+ if (exclusiveStart) flags |= ITERATOR_EXCLUSIVE_START_FLAG;
967
+ if (inclusiveEnd) flags |= ITERATOR_INCLUSIVE_END_FLAG;
968
+ if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
969
+ if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
970
+ if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
971
+ 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;
972
+ return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues));
856
973
  }
857
974
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
858
975
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -933,8 +1050,11 @@ var Store = class {
933
1050
  open() {
934
1051
  if (this.db.opened) return true;
935
1052
  this.db.open(this.path, {
1053
+ dbWriteBufferSize: this.dbWriteBufferSize,
936
1054
  disableWAL: this.disableWAL,
937
1055
  enableStats: this.enableStats,
1056
+ maxWriteBufferNumber: this.maxWriteBufferNumber,
1057
+ maxWriteBufferSizeToMaintain: this.maxWriteBufferSizeToMaintain,
938
1058
  mode: this.pessimistic ? "pessimistic" : "optimistic",
939
1059
  name: this.name,
940
1060
  noBlockCache: this.noBlockCache,
@@ -944,7 +1064,8 @@ var Store = class {
944
1064
  transactionLogMaxAgeThreshold: this.transactionLogMaxAgeThreshold,
945
1065
  transactionLogMaxSize: this.transactionLogMaxSize,
946
1066
  transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
947
- transactionLogsPath: this.transactionLogsPath
1067
+ transactionLogsPath: this.transactionLogsPath,
1068
+ writeBufferSize: this.writeBufferSize
948
1069
  });
949
1070
  return false;
950
1071
  }
@@ -1219,6 +1340,32 @@ var RocksDatabase = class RocksDatabase extends DBI {
1219
1340
  this.store.close();
1220
1341
  }
1221
1342
  /**
1343
+ * Compacts the entire key range of the database asynchronously.
1344
+ * This triggers manual compaction which removes tombstones and reclaims space.
1345
+ *
1346
+ * @example
1347
+ * ```typescript
1348
+ * const db = RocksDatabase.open('/path/to/database');
1349
+ * await db.compact();
1350
+ * ```
1351
+ */
1352
+ compact(options) {
1353
+ return this.store.compact(options);
1354
+ }
1355
+ /**
1356
+ * Compacts the entire key range of the database synchronously.
1357
+ * This triggers manual compaction which removes tombstones and reclaims space.
1358
+ *
1359
+ * @example
1360
+ * ```typescript
1361
+ * const db = RocksDatabase.open('/path/to/database');
1362
+ * db.compactSync();
1363
+ * ```
1364
+ */
1365
+ compactSync(options) {
1366
+ return this.store.compactSync(options);
1367
+ }
1368
+ /**
1222
1369
  * Returns the list of column families in the RocksDB database.
1223
1370
  */
1224
1371
  get columns() {
@@ -1300,6 +1447,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
1300
1447
  return this.store.db.getDBProperty(propertyName);
1301
1448
  }
1302
1449
  /**
1450
+ * Retrieves the estimated number of keys in the database.
1451
+ *
1452
+ * @example
1453
+ * ```typescript
1454
+ * const db = RocksDatabase.open('/path/to/database');
1455
+ * const estimated = db.getEstimatedKeyCount();
1456
+ * console.log(estimated);
1457
+ * ```
1458
+ */
1459
+ getEstimatedKeyCount() {
1460
+ return this.getDBIntProperty("rocksdb.estimate-num-keys") ?? 0;
1461
+ }
1462
+ /**
1303
1463
  * Returns the current timestamp as a monotonically increasing timestamp in
1304
1464
  * milliseconds represented as a decimal number.
1305
1465
  *
@@ -1438,6 +1598,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
1438
1598
  if (store.open()) return this;
1439
1599
  store.db.setDefaultValueBuffer(VALUE_BUFFER);
1440
1600
  store.db.setDefaultKeyBuffer(KEY_BUFFER);
1601
+ store.db.setIteratorState(ITERATOR_STATE_BUFFER);
1441
1602
  /**
1442
1603
  * The encoder initialization precedence is:
1443
1604
  * 1. encoder.Encoder
@@ -1809,6 +1970,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
1809
1970
  logBuffer.logId = 0;
1810
1971
  logBuffer.size = 0;
1811
1972
  logBuffer.dataView = new DataView(logBuffer.buffer);
1973
+ size = 0;
1812
1974
  }
1813
1975
  }
1814
1976
  dataView = logBuffer.dataView;
@@ -1936,7 +2098,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
1936
2098
  //#region src/index.ts
1937
2099
  const versions = {
1938
2100
  rocksdb: version,
1939
- "rocksdb-js": "1.1.1"
2101
+ "rocksdb-js": "1.3.0"
1940
2102
  };
1941
2103
 
1942
2104
  //#endregion