autobee 2.9.5 → 2.10.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/index.js CHANGED
@@ -114,6 +114,8 @@ module.exports = class Autobee extends ReadyResource {
114
114
  this.ff = null
115
115
  this.fastForwarding = null
116
116
  this.fastForwardTo = null
117
+ this._ffCandidates = new Map()
118
+ this._ffSearching = null
117
119
 
118
120
  this._workingBee = bee
119
121
  this._workingView = new ApplyView(this._workingBee, this)
@@ -425,6 +427,7 @@ module.exports = class Autobee extends ReadyResource {
425
427
  // rugpull the rest
426
428
  await this.store.close()
427
429
 
430
+ if (this._ffSearching) await this._ffSearching
428
431
  if (this._updating) await this._updating
429
432
  if (this._draining) await this._draining
430
433
 
@@ -816,8 +819,22 @@ module.exports = class Autobee extends ReadyResource {
816
819
  const hints = await this._applyWakeupHints()
817
820
  if (!hints.length) return
818
821
 
822
+ this._queueFastForward(hints)
823
+ }
824
+
825
+ _queueFastForward(hints) {
819
826
  if (!this._ffEnabled) return
820
827
 
828
+ for (const { key, length } of hints) {
829
+ if (length === 0) continue
830
+
831
+ const hex = b4a.toString(key, 'hex')
832
+ const previous = this._ffCandidates.get(hex)
833
+ if (previous === undefined || previous < length) this._ffCandidates.set(hex, length)
834
+ }
835
+
836
+ if (!this._ffCandidates.size || this._ffSearching !== null) return
837
+
821
838
  // a scheduled fast-forward is applied by the drain before we look again
822
839
  if (this.fastForwardTo !== null || this.fastForwarding !== null) return
823
840
  if (this._interrupting || this.closing || this.bootFrom) return
@@ -829,14 +846,34 @@ module.exports = class Autobee extends ReadyResource {
829
846
  return
830
847
  }
831
848
 
849
+ this._ffSearching = this._searchFastForward().finally(() => {
850
+ this._ffSearching = null
851
+ })
852
+ }
853
+
854
+ async _searchFastForward() {
832
855
  try {
833
- const heads = await this._readCandidateHeads(hints, FastForward.DEFAULT_TIMEOUT)
856
+ while (this._ffCandidates.size) {
857
+ if (this.fastForwardTo !== null || this.fastForwarding !== null) return
858
+ if (this._interrupting || this.closing || this.bootFrom) return
859
+
860
+ const hints = flushCandidates(this._ffCandidates)
834
861
 
835
- const ff = await FastForward.fromHeads(this, heads, {
836
- timeout: FastForward.DEFAULT_TIMEOUT
837
- })
862
+ const heads = await this._readCandidateHeads(hints, FastForward.DEFAULT_TIMEOUT)
838
863
 
839
- if (ff !== null) await this._runFastForward(ff)
864
+ const ff = await FastForward.fromHeads(this, heads, {
865
+ timeout: FastForward.DEFAULT_TIMEOUT
866
+ })
867
+
868
+ if (ff === null) continue
869
+
870
+ if (this._interrupting || this.closing) {
871
+ await ff.close()
872
+ return
873
+ }
874
+
875
+ if (await this._runFastForward(ff)) return
876
+ }
840
877
  } catch (err) {
841
878
  safetyCatch(err)
842
879
  }
@@ -1475,7 +1512,7 @@ module.exports = class Autobee extends ReadyResource {
1475
1512
  const ff = await FastForward.fromHead(this, head, null, { force: true, timeout })
1476
1513
  if (ff === null) return null
1477
1514
 
1478
- if (!(await this._runFastForward(ff))) return null
1515
+ if (!(await this._runFastForward(ff, { force: true }))) return null
1479
1516
 
1480
1517
  return this.ff.promise
1481
1518
  }
@@ -1484,7 +1521,7 @@ module.exports = class Autobee extends ReadyResource {
1484
1521
  async _bootFromSystem(system) {
1485
1522
  try {
1486
1523
  const ff = new FastForward(this, system, { timeout: FastForward.DEFAULT_TIMEOUT })
1487
- return await this._runFastForward(ff)
1524
+ return await this._runFastForward(ff, { force: true })
1488
1525
  } catch (err) {
1489
1526
  safetyCatch(err)
1490
1527
  return false
@@ -1500,14 +1537,14 @@ module.exports = class Autobee extends ReadyResource {
1500
1537
 
1501
1538
  const ff = await FastForward.fromHead(this, oplog, null, { force: true, timeout })
1502
1539
 
1503
- return ff !== null && (await this._runFastForward(ff))
1540
+ return ff !== null && (await this._runFastForward(ff, { force: true }))
1504
1541
  } catch (err) {
1505
1542
  safetyCatch(err)
1506
1543
  return false
1507
1544
  }
1508
1545
  }
1509
1546
 
1510
- async _runFastForward(ff) {
1547
+ async _runFastForward(ff, { force = false } = {}) {
1511
1548
  if (this.fastForwardTo !== null || this.fastForwarding !== null) {
1512
1549
  await ff.close()
1513
1550
  return false
@@ -1516,12 +1553,15 @@ module.exports = class Autobee extends ReadyResource {
1516
1553
  this.fastForwarding = ff
1517
1554
 
1518
1555
  const result = await ff.run()
1556
+ const flushes = ff.system.flushes
1519
1557
  await ff.close()
1520
1558
 
1521
1559
  if (this.fastForwarding === ff) this.fastForwarding = null
1522
1560
 
1523
1561
  if (!result) return false
1524
1562
 
1563
+ if (!force && flushes - this.system.flushes < FastForward.MIN_GAP) return false
1564
+
1525
1565
  this.fastForwardTo = result
1526
1566
  this.ff = rrp()
1527
1567
 
@@ -1561,7 +1601,7 @@ module.exports = class Autobee extends ReadyResource {
1561
1601
  this.fastForwardTo = null
1562
1602
 
1563
1603
  // process any wakeup while fast-forward itself was in flight
1564
- await this._applyWakeupHints()
1604
+ this._queueFastForward(await this._applyWakeupHints())
1565
1605
  await this.writers.refresh()
1566
1606
 
1567
1607
  // we moved, so ask our peers to tell us their heads again
@@ -1597,6 +1637,18 @@ function getBootOption(boot) {
1597
1637
  return boot
1598
1638
  }
1599
1639
 
1640
+ function flushCandidates(candidates) {
1641
+ const hints = []
1642
+
1643
+ for (const [hex, length] of candidates) {
1644
+ hints.push({ key: b4a.from(hex, 'hex'), length })
1645
+ }
1646
+
1647
+ candidates.clear()
1648
+
1649
+ return hints
1650
+ }
1651
+
1600
1652
  function noop() {}
1601
1653
 
1602
1654
  function createAnchorCore(store, prologue, manifestData) {
@@ -38,6 +38,7 @@ class FastForward {
38
38
  }
39
39
 
40
40
  static DEFAULT_TIMEOUT = DEFAULT_OP_TIMEOUT
41
+ static MIN_GAP = MIN_FF_GAP
41
42
 
42
43
  static async fromHead(auto, head, trusted, { force = false, timeout = 0 } = {}) {
43
44
  const conservative = !force && auto._conservativeFF
package/lib/migrations.js CHANGED
@@ -84,7 +84,7 @@ async function checkAutobaseMigration(store, local, bootstrap) {
84
84
  let catchup
85
85
 
86
86
  try {
87
- length = await findLocalSystemLength(core, session, systemLength)
87
+ length = await findLocalSystemLength(store, core, session, systemLength)
88
88
  catchup = await getCatchupHeads(session, length)
89
89
  } finally {
90
90
  await session.close()
@@ -205,18 +205,14 @@ function deriveNamespace(name, bootstrap, entropy, encryptionKey) {
205
205
  // scan back for the newest block the signed core actually has locally
206
206
  // the newest INFO the signed core holds that the batch session agrees with -
207
207
  // past its dependency the batch is speculative and may have diverged
208
- async function findLocalSystemLength(core, session, systemLength) {
208
+ async function findLocalSystemLength(store, core, session, systemLength) {
209
209
  let length = Math.min(core.length, systemLength)
210
210
 
211
211
  while (length > 0) {
212
212
  const seq = length - 1
213
213
  const node = await core.get(seq, { wait: false })
214
214
 
215
- if (node) {
216
- const blk = decodeBlock(node)
217
- const entry = blk.keys[0]
218
- if (b4a.equals(entry.key, INFO) && (await sameTree(core, session, length))) return length
219
- }
215
+ if (node && (await isReachableInfo(store, core, session, node, length))) return length
220
216
 
221
217
  length--
222
218
  }
@@ -224,6 +220,15 @@ async function findLocalSystemLength(core, session, systemLength) {
224
220
  return 0
225
221
  }
226
222
 
223
+ // an INFO block the batch agrees with whose views the store has caught up to -
224
+ // otherwise the migrated view would be ahead of what the system can prove
225
+ async function isReachableInfo(store, core, session, node, length) {
226
+ const entry = decodeBlock(node).keys[0]
227
+ if (!b4a.equals(entry.key, INFO)) return false
228
+ if (!(await sameTree(core, session, length))) return false
229
+ return viewsReached(store, decodeLegacySystemInfo(entry.value))
230
+ }
231
+
227
232
  async function sameTree(core, session, length) {
228
233
  if (length <= session.signedLength) return true
229
234
  if (length > session.length) return false
@@ -358,3 +363,17 @@ async function getWriterBatch(store, head, key, encryptionKey, nodes = []) {
358
363
 
359
364
  return batch
360
365
  }
366
+
367
+ async function viewsReached(store, info) {
368
+ for (const v of info.views) {
369
+ if (!v.length) continue
370
+ const core = store.get({ key: v.key, active: false })
371
+ try {
372
+ await core.ready()
373
+ if (core.length < v.length) return false
374
+ } finally {
375
+ await core.close()
376
+ }
377
+ }
378
+ return true
379
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.9.5",
3
+ "version": "2.10.0",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",