hypercore-storage 0.0.36 → 0.0.38

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/index.js CHANGED
@@ -162,7 +162,7 @@ class WriteBatch {
162
162
 
163
163
  flush () {
164
164
  if (this.atom) return this.atom.flush()
165
- return this.write.flush()
165
+ return flushAndDestroy(this.write)
166
166
  }
167
167
  }
168
168
 
@@ -259,15 +259,32 @@ class ReadBatch {
259
259
  }
260
260
 
261
261
  flush () {
262
- return this.read.flush()
262
+ return flushAndDestroy(this.read)
263
263
  }
264
264
 
265
265
  tryFlush () {
266
- this.read.tryFlush()
266
+ tryFlushAndDestroy(this.read)
267
267
  }
268
268
  }
269
269
 
270
- class Atomizer {
270
+ class Lock {
271
+ constructor (mutex) {
272
+ this.mutex = mutex
273
+ this.refs = 0
274
+ this.promise = null
275
+ }
276
+
277
+ acquire () {
278
+ if (this.refs++ === 0) this.promise = this.mutex.lock()
279
+ return this
280
+ }
281
+
282
+ release () {
283
+ if (--this.refs === 0) return this.mutex.unlock()
284
+ }
285
+ }
286
+
287
+ class Atom {
271
288
  constructor (db) {
272
289
  this.db = db
273
290
  this.batch = null
@@ -276,6 +293,11 @@ class Atomizer {
276
293
  this.flushing = null
277
294
  this.resolve = null
278
295
  this.reject = null
296
+
297
+ this._executing = null
298
+ this._waiting = []
299
+ this._queue = []
300
+ this._enqueue = (lock, resolve, reject) => this._queue.push({ lock, resolve, reject })
279
301
  }
280
302
 
281
303
  enter () {
@@ -286,6 +308,43 @@ class Atomizer {
286
308
  if (--this.refs === 0) this._commit()
287
309
  }
288
310
 
311
+ acquire (mutex) {
312
+ const lock = this._lock(mutex)
313
+ return new Promise(this._enqueue.bind(this, lock))
314
+ }
315
+
316
+ _lock (mutex) {
317
+ for (const lock of this._waiting) {
318
+ if (lock.mutex === mutex) return lock.acquire()
319
+ }
320
+
321
+ const lock = new Lock(mutex)
322
+ this._waiting.push(lock)
323
+
324
+ if (this._executing === null) this._executing = this._execute()
325
+
326
+ return lock.acquire()
327
+ }
328
+
329
+ async _execute () {
330
+ this.enter()
331
+ for (const { promise } of this._waiting) await promise
332
+
333
+ const queue = this._queue
334
+
335
+ this._waiting = []
336
+ this._queue = []
337
+ this._executing = null
338
+
339
+ for (const { lock, resolve, reject } of queue) {
340
+ if (this.destroyed) reject(new Error('Atomizer destroyed'))
341
+ else resolve(lock)
342
+ }
343
+
344
+ this._ensureTick() // allow caller to enter
345
+ this.exit()
346
+ }
347
+
289
348
  createBatch () {
290
349
  if (this.refs === 0) this._ensureTick()
291
350
  this.enter()
@@ -299,6 +358,8 @@ class Atomizer {
299
358
  }
300
359
 
301
360
  async _commit () {
361
+ if (this.batch === null) return
362
+
302
363
  const batch = this.batch
303
364
  const resolve = this.resolve
304
365
  const reject = this.reject
@@ -317,10 +378,12 @@ class Atomizer {
317
378
  try {
318
379
  await batch.flush()
319
380
  } catch (err) {
381
+ batch.destroy()
320
382
  reject(err)
321
383
  return
322
384
  }
323
385
 
386
+ batch.destroy()
324
387
  resolve()
325
388
  }
326
389
 
@@ -350,7 +413,7 @@ class Atomizer {
350
413
 
351
414
  module.exports = class CoreStorage {
352
415
  constructor (dir) {
353
- this.db = new RocksDB(dir)
416
+ this.db = typeof dir === 'object' ? dir : new RocksDB(dir)
354
417
  this.mutex = new RW()
355
418
  }
356
419
 
@@ -372,7 +435,7 @@ module.exports = class CoreStorage {
372
435
  try {
373
436
  const b = this.db.write()
374
437
  b.tryPut(b4a.from([TL.LOCAL_SEED]), seed)
375
- await b.flush()
438
+ await flushAndDestroy(b)
376
439
 
377
440
  return true
378
441
  } finally {
@@ -420,14 +483,14 @@ module.exports = class CoreStorage {
420
483
  return this.db.close()
421
484
  }
422
485
 
423
- atomizer () {
424
- return new Atomizer(this.db)
486
+ atom () {
487
+ return new Atom(this.db)
425
488
  }
426
489
 
427
490
  async clear () {
428
491
  const b = this.db.write()
429
492
  b.tryDeleteRange(b4a.from([TL.STORAGE_INFO]), INF)
430
- await b.flush()
493
+ await flushAndDestroy(b)
431
494
  }
432
495
 
433
496
  async has (discoveryKey) {
@@ -495,6 +558,7 @@ class HypercoreStorage {
495
558
  this.root = root
496
559
  this.db = root.db
497
560
  this.dbSnapshot = snapshot
561
+ this.dbRead = snapshot || this.db
498
562
  this.mutex = root.mutex
499
563
 
500
564
  this.discoveryKey = discoveryKey
@@ -512,8 +576,8 @@ class HypercoreStorage {
512
576
  return this.dbSnapshot !== null
513
577
  }
514
578
 
515
- atomizer () {
516
- return this.root.atomizer()
579
+ atom () {
580
+ return this.root.atom()
517
581
  }
518
582
 
519
583
  dependencyLength () {
@@ -535,14 +599,14 @@ class HypercoreStorage {
535
599
  return storage
536
600
  }
537
601
 
538
- async registerBatch (name, head) {
602
+ async registerBatch (name, head, atom) {
539
603
  await this.mutex.write.lock()
540
604
 
541
605
  const storage = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, null)
542
606
 
543
607
  try {
544
608
  const info = await getStorageInfo(this.db)
545
- const write = this.db.write()
609
+ const write = atom ? atom.createBatch() : this.db.write()
546
610
 
547
611
  storage.dataPointer = info.free++
548
612
 
@@ -556,7 +620,8 @@ class HypercoreStorage {
556
620
  batch.setBatchPointer(name, storage.dataPointer)
557
621
  if (head.rootHash) batch.setCoreHead(head) // if no root hash its the empty core - no head yet
558
622
 
559
- await write.flush()
623
+ if (atom) await atom.flush()
624
+ else await flushAndDestroy(write)
560
625
 
561
626
  storage.dependencies = await addDependencies(this.db, storage.dataPointer, head.length)
562
627
  return storage
@@ -583,7 +648,7 @@ class HypercoreStorage {
583
648
 
584
649
  snapshot () {
585
650
  assert(this.destroyed === false)
586
- const s = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.db.snapshot())
651
+ const s = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.dbRead.snapshot())
587
652
 
588
653
  for (const { data, length } of this.dependencies) s.dependencies.push({ data, length })
589
654
 
@@ -616,18 +681,35 @@ class HypercoreStorage {
616
681
  createReadBatch (opts) {
617
682
  assert(this.destroyed === false)
618
683
 
619
- const snapshot = this.dbSnapshot
620
- return new ReadBatch(this, this.db.read({ snapshot }))
684
+ return new ReadBatch(this, this.dbRead.read())
621
685
  }
622
686
 
623
- createWriteBatch (atomizer) {
687
+ createWriteBatch (atom) {
624
688
  assert(this.destroyed === false)
625
689
 
626
- if (atomizer) return new WriteBatch(this, atomizer.createBatch(), atomizer)
690
+ if (atom) return new WriteBatch(this, atom.createBatch(), atom)
627
691
 
628
692
  return new WriteBatch(this, this.db.write(), null)
629
693
  }
630
694
 
695
+ // helper for atomic flows
696
+ async createWriteBatchAndLock (atomizer, lock) {
697
+ assert(this.destroyed === false)
698
+ if (atomizer) atomizer.enter()
699
+
700
+ try {
701
+ await lock.lock()
702
+ } catch (err) {
703
+ if (atomizer) atomizer.exit()
704
+ throw err
705
+ }
706
+
707
+ const batch = this.createWriteBatch(atomizer)
708
+ if (atomizer) atomizer.exit()
709
+
710
+ return batch
711
+ }
712
+
631
713
  createBlockStream (opts = {}) {
632
714
  assert(this.destroyed === false)
633
715
  return createStream(this, createBlockStream, opts)
@@ -636,8 +718,8 @@ class HypercoreStorage {
636
718
  createUserDataStream (opts = {}) {
637
719
  assert(this.destroyed === false)
638
720
 
639
- const r = encodeUserDataRange(this.dataPointer, DATA.USER_DATA, this.dbSnapshot, opts)
640
- const s = this.db.iterator(r)
721
+ const r = encodeUserDataRange(this.dataPointer, DATA.USER_DATA, opts)
722
+ const s = this.dbRead.iterator(r)
641
723
  s._readableState.map = mapStreamUserData
642
724
  return s
643
725
  }
@@ -645,8 +727,8 @@ class HypercoreStorage {
645
727
  createTreeNodeStream (opts = {}) {
646
728
  assert(this.destroyed === false)
647
729
 
648
- const r = encodeIndexRange(this.dataPointer, DATA.TREE, this.dbSnapshot, opts)
649
- const s = this.db.iterator(r)
730
+ const r = encodeIndexRange(this.dataPointer, DATA.TREE, opts)
731
+ const s = this.dbRead.iterator(r)
650
732
  s._readableState.map = mapStreamTreeNode
651
733
  return s
652
734
  }
@@ -654,8 +736,8 @@ class HypercoreStorage {
654
736
  createBitfieldPageStream (opts = {}) {
655
737
  assert(this.destroyed === false)
656
738
 
657
- const r = encodeIndexRange(this.dataPointer, DATA.BITFIELD, this.dbSnapshot, opts)
658
- const s = this.db.iterator(r)
739
+ const r = encodeIndexRange(this.dataPointer, DATA.BITFIELD, opts)
740
+ const s = this.dbRead.iterator(r)
659
741
  s._readableState.map = mapStreamBitfieldPage
660
742
  return s
661
743
  }
@@ -663,7 +745,7 @@ class HypercoreStorage {
663
745
  async peekLastTreeNode () {
664
746
  assert(this.destroyed === false)
665
747
 
666
- const last = await this.db.peek(encodeIndexRange(this.dataPointer, DATA.TREE, this.dbSnapshot, { reverse: true }))
748
+ const last = await this.dbRead.peek(encodeIndexRange(this.dataPointer, DATA.TREE, { reverse: true }))
667
749
  if (last === null) return null
668
750
  return c.decode(m.TreeNode, last.value)
669
751
  }
@@ -671,7 +753,7 @@ class HypercoreStorage {
671
753
  async peekLastBitfieldPage () {
672
754
  assert(this.destroyed === false)
673
755
 
674
- const last = await this.db.peek(encodeIndexRange(this.dataPointer, DATA.BITFIELD, this.dbSnapshot, { reverse: true }))
756
+ const last = await this.dbRead.peek(encodeIndexRange(this.dataPointer, DATA.BITFIELD, { reverse: true }))
675
757
  if (last === null) return null
676
758
  return mapStreamBitfieldPage(last)
677
759
  }
@@ -680,19 +762,19 @@ class HypercoreStorage {
680
762
  if (this.destroyed) return
681
763
  this.destroyed = true
682
764
 
683
- if (this.dbSnapshot) this.dbSnapshot.destroy()
765
+ if (this.dbSnapshot) this.dbSnapshot.close().catch(noop)
684
766
  this.dbSnapshot = null
685
767
  }
686
768
  }
687
769
 
688
770
  function createStream (storage, createStreamType, opts) {
689
771
  return storage.dependencies.length === 0
690
- ? createStreamType(storage.db, storage.dbSnapshot, storage.dataPointer, opts)
772
+ ? createStreamType(storage.db, storage.dataPointer, opts)
691
773
  : new DependencyStream(storage, createStreamType, opts)
692
774
  }
693
775
 
694
- function createBlockStream (db, snap, data, opts) {
695
- const r = encodeIndexRange(data, DATA.BLOCK, snap, opts)
776
+ function createBlockStream (db, data, opts) {
777
+ const r = encodeIndexRange(data, DATA.BLOCK, opts)
696
778
  const s = db.iterator(r)
697
779
  s._readableState.map = mapStreamBlock
698
780
  return s
@@ -766,8 +848,8 @@ function ensureSlab (size) {
766
848
  return SLAB
767
849
  }
768
850
 
769
- function encodeIndexRange (pointer, type, snapshot, opts) {
770
- const bounded = { snapshot, gt: null, gte: null, lte: null, lt: null, reverse: !!opts.reverse, limit: toLimit(opts.limit) }
851
+ function encodeIndexRange (pointer, type, opts) {
852
+ const bounded = { gt: null, gte: null, lte: null, lt: null, reverse: !!opts.reverse, limit: toLimit(opts.limit) }
771
853
 
772
854
  if (opts.gt || opts.gt === 0) bounded.gt = encodeDataIndex(pointer, type, opts.gt)
773
855
  else if (opts.gte) bounded.gte = encodeDataIndex(pointer, type, opts.gte)
@@ -780,8 +862,8 @@ function encodeIndexRange (pointer, type, snapshot, opts) {
780
862
  return bounded
781
863
  }
782
864
 
783
- function encodeUserDataRange (pointer, type, snapshot, opts) {
784
- const bounded = { snapshot, gt: null, gte: null, lte: null, lt: null, reverse: !!opts.reverse, limit: toLimit(opts.limit) }
865
+ function encodeUserDataRange (pointer, type, opts) {
866
+ const bounded = { gt: null, gte: null, lte: null, lt: null, reverse: !!opts.reverse, limit: toLimit(opts.limit) }
785
867
 
786
868
  if (opts.gt || opts.gt === 0) bounded.gt = encodeUserDataIndex(pointer, type, opts.gt)
787
869
  else if (opts.gte) bounded.gte = encodeUserDataIndex(pointer, type, opts.gte)
@@ -904,3 +986,24 @@ function initialiseCoreData (db, { userData } = {}) {
904
986
  }
905
987
  }
906
988
  }
989
+
990
+ function noop () {}
991
+
992
+ async function tryFlushAndDestroy (batch) {
993
+ try {
994
+ await batch.flush()
995
+ } catch {}
996
+
997
+ batch.destroy()
998
+ }
999
+
1000
+ async function flushAndDestroy (batch) {
1001
+ try {
1002
+ await batch.flush()
1003
+ } catch (err) {
1004
+ batch.destroy()
1005
+ throw err
1006
+ }
1007
+
1008
+ batch.destroy()
1009
+ }
@@ -93,7 +93,7 @@ module.exports = class DependencyStream extends Readable {
93
93
  _nextStream () {
94
94
  const { data, gte, lt } = this._streams[this._next++]
95
95
 
96
- const stream = this.createStream(this.storage.db, this.storage.dbSnapshot, data, {
96
+ const stream = this.createStream(this.storage.dbRead, data, {
97
97
  reverse: this._reverse,
98
98
  limit: this._limit,
99
99
  gte,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore-storage",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "main": "index.js",
5
5
  "files": [
6
6
  "index.js",
@@ -22,7 +22,7 @@
22
22
  "queue-tick": "^1.0.1",
23
23
  "read-write-mutexify": "^2.1.0",
24
24
  "resolve-reject-promise": "^1.0.0",
25
- "rocksdb-native": "^2.2.0",
25
+ "rocksdb-native": "^3.0.0",
26
26
  "streamx": "^2.20.1"
27
27
  },
28
28
  "devDependencies": {