autobee 2.2.1 → 2.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/README.md CHANGED
@@ -247,17 +247,16 @@ Called at flush time to stamp your own oplog (with your view as `target` and a `
247
247
 
248
248
  ```js
249
249
  {
250
- head: { key, length }, // oplog head to boot from
251
- legacy: { key, length }, // pre-2.0 pointer, see below
252
- bootCondition (target, reference) {} // optional gate on the view we would land on
250
+ head: { key, length }, // oplog head to boot from
251
+ legacy: { key, length } // pre-2.0 pointer, see below
253
252
  }
254
253
  ```
255
254
 
256
255
  Pass one of `head` or `legacy`, not both.
257
256
 
258
- Without `bootCondition` this is a single attempt that gives up if the head cannot be read; with one it parks until the condition is satisfied, which today means indefinitely if it never is.
257
+ `head` must be a v3 or newer oplog node. `length` is a floor: the key is searched for its latest oplog head at or past it, and the boot lands ungated on the views that head stamps. It is a single attempt: use `moveTo` if you need to retry.
259
258
 
260
- `legacy` is for records written before boot heads were oplog heads: the key is a _system_ head and a `0` length is resolved from the core. It boots ungated - `bootCondition` does not apply - and will be removed, so don't reach for it. A bare `{ key, length }` in place of the whole struct means the same thing, older still.
259
+ `legacy` is the pre-2.0 pointer: a _system_ head whose `0` length resolves from the core. It will be removed, so don't reach for it. A bare `{ key, length }` in place of the whole struct means the same.
261
260
 
262
261
  #### `fastForward.conservative`
263
262
 
@@ -265,9 +264,11 @@ Defaults to `true`: only fast-forward onto a head a connected peer can serve who
265
264
 
266
265
  The check covers the oplog head only, not the system and view cores the fast-forward then reads.
267
266
 
268
- #### `await db.moveTo(head)`
267
+ #### `await db.moveTo(head, [options])`
269
268
 
270
- Fast-forward onto an oplog head, ignoring the usual distance and conservative checks. Resolves `{ to, from }`.
269
+ Fast-forward onto an oplog head, ignoring the usual distance and conservative checks. Resolves `{ to, from }`, or `null` if the head could not be booted.
270
+
271
+ Pass `{ timeout }` to bound the reads, so a head nobody can serve fails instead of hanging.
271
272
 
272
273
  ### Encryption
273
274
 
package/index.js CHANGED
@@ -14,7 +14,6 @@ const asserts = require('./lib/asserts.js')
14
14
  const boot = require('./lib/boot.js')
15
15
  const { resolveWeight, currentWeight } = require('./lib/witness.js')
16
16
  const encoding = require('./lib/encoding.js')
17
- const { LEGACY_OPLOG_VERSION } = require('./lib/constants.js')
18
17
  const FastForward = require('./lib/fast-forward.js')
19
18
  const System = require('./lib/system.js')
20
19
  const ApplyCalls = require('./lib/apply-calls.js')
@@ -72,7 +71,7 @@ module.exports = class Autobee extends ReadyResource {
72
71
  this.stats = { undos: 0, fastForwards: 0, drains: 0, applies: 0, appends: 0 }
73
72
 
74
73
  const systemStore = this.store.session()
75
- this.system = new System(systemStore, this.name, {
74
+ this.system = new System(this, systemStore, {
76
75
  core: systemStore.get({ preload: this._getCorePreload('system') }),
77
76
  encrypted: this.encrypted,
78
77
  getEncryptionProvider: this.getSystemEncryption
@@ -119,7 +118,6 @@ module.exports = class Autobee extends ReadyResource {
119
118
  this._appending = []
120
119
  this._draining = null
121
120
  this._updating = null
122
- this._bootWait = null
123
121
 
124
122
  this.legacyViews = handlers.legacyViews || []
125
123
 
@@ -145,7 +143,6 @@ module.exports = class Autobee extends ReadyResource {
145
143
  this.interrupted = null
146
144
  this._interrupting = false
147
145
  this._onErrorBound = this._onError.bind(this)
148
- this._bumpSoonBound = this.bumpSoon.bind(this)
149
146
  this._onGroupUpdateBound = this._onGroupUpdate.bind(this)
150
147
 
151
148
  this.wakeupCapability = null
@@ -153,7 +150,7 @@ module.exports = class Autobee extends ReadyResource {
153
150
  this.previousDrain = 0
154
151
 
155
152
  this._catchupMigratedNodes = null
156
- this._migratedHead = null
153
+ this._migrating = false
157
154
  this._prebooting = null
158
155
 
159
156
  this.ready().catch(noop)
@@ -393,7 +390,6 @@ module.exports = class Autobee extends ReadyResource {
393
390
  // rugpull the rest
394
391
  await this.store.close()
395
392
 
396
- if (this._bootWait !== null) this._bootWait.resolve()
397
393
  if (this._updating) await this._updating
398
394
  if (this._draining) await this._draining
399
395
 
@@ -437,10 +433,6 @@ module.exports = class Autobee extends ReadyResource {
437
433
  return new WriterEncryption(this)
438
434
  }
439
435
 
440
- getMostRecentHead() {
441
- return topo.getMostRecentHead(this, this.system.bee.snapshot())
442
- }
443
-
444
436
  async _preBoot() {
445
437
  if (this._handlers.wait) await this._handlers.wait()
446
438
 
@@ -458,7 +450,7 @@ module.exports = class Autobee extends ReadyResource {
458
450
  this.bootFrom = getBootOption(await this.bootFrom)
459
451
  }
460
452
 
461
- return boot(this.store, this.key, this.legacyViews, {
453
+ return boot(this.store, this.key, {
462
454
  encryptionKey: this.encryptionKey,
463
455
  keyPair: this.keyPair
464
456
  })
@@ -505,37 +497,15 @@ module.exports = class Autobee extends ReadyResource {
505
497
  const system = result.system || EMPTY_HEAD
506
498
 
507
499
  await this.system.boot(system)
500
+ if (this.system.migration) await this._runMigration()
508
501
 
509
- const migrated = await this.local.getUserData('autobee/migrated-head')
510
- if (migrated) this._migratedHead = encoding.decodeMigratedHead(migrated)
511
-
512
- let view = this.system.view
513
- if (!view) view = (this._migratedHead && this._migratedHead.view) || EMPTY_HEAD
514
-
515
- // @todo migration
516
502
  if (result.migration) {
517
- if (this._handlers.migrate) {
518
- view =
519
- (await this._handlers.migrate(result.migration.views, result.migration.system)) ||
520
- EMPTY_HEAD
521
- this._catchupMigratedNodes = result.migration.catchup
522
-
523
- this._migratedHead = {
524
- system: result.migration.system,
525
- view: view.length ? view : (this._migratedHead && this._migratedHead.view) || null
526
- }
527
-
528
- await this._storeMigratedHead()
529
- }
503
+ this._migrating = true
504
+ this._catchupMigratedNodes = result.migration.catchup
530
505
 
531
506
  // ff boot invalidated by migration
532
507
  this.bootFrom = null
533
508
 
534
- // clear legacy data
535
- await this.bootstrap.setUserData('autobase/local', null)
536
- await this.local.setUserData('autobase/boot', null)
537
- await this.local.setUserData('autobase/encryption', null)
538
-
539
509
  for (const batch of result.migration.catchup) {
540
510
  const { key, length } = batch[batch.length - 1]
541
511
  this.writers.wakeup(key, length)
@@ -547,6 +517,8 @@ module.exports = class Autobee extends ReadyResource {
547
517
  await this._workingBee.core.ready()
548
518
  await this.bee.core.ready()
549
519
 
520
+ const view = this.system.view
521
+
550
522
  this._workingBee.move(view)
551
523
  this.bee.move(view)
552
524
 
@@ -592,10 +564,6 @@ module.exports = class Autobee extends ReadyResource {
592
564
  async _bump(force) {
593
565
  if (!force && !this._bootGuard.opened) await this._bootGuard.ready()
594
566
 
595
- // resolve before the bootAll gate: a parked boot-from retries on any bump,
596
- // and its drain is the one _bootOnline itself is waiting on
597
- if (this._bootWait !== null) this._bootWait.resolve()
598
-
599
567
  if (!force && !this._bootOnlineGuard.opened) await this._bootOnlineGuard.ready()
600
568
 
601
569
  this.bumping++
@@ -668,12 +636,16 @@ module.exports = class Autobee extends ReadyResource {
668
636
  this.stats.drains++
669
637
 
670
638
  if (this.bootFrom) {
671
- const { head = null, legacy = null, bootCondition = null, wait = false } = this.bootFrom
639
+ const { head = null, legacy = null } = this.bootFrom
672
640
 
673
641
  this.bootFrom = null
674
642
 
675
- if (legacy) await this._bootFromSystem(legacy)
676
- else if (head) await this._bootFromHead(head, bootCondition, wait)
643
+ if (legacy) {
644
+ await this._bootFromSystem(legacy)
645
+ } else if (head) {
646
+ this._wakeup.hint({ key: head.key, length: head.length || 0 })
647
+ await this._bootFromHead(head)
648
+ }
677
649
  }
678
650
 
679
651
  const changes = this._hasUpdate ? new UpdateChanges(this) : null
@@ -695,6 +667,8 @@ module.exports = class Autobee extends ReadyResource {
695
667
 
696
668
  if (this.fastForwardTo !== null) {
697
669
  await this._applyFastForward()
670
+ if (changes) changes.track()
671
+ this._needsUpdate = false
698
672
  break // revaluate conditions...
699
673
  }
700
674
 
@@ -718,7 +692,7 @@ module.exports = class Autobee extends ReadyResource {
718
692
  this._updating = updating.promise
719
693
 
720
694
  try {
721
- if (this._needsUpdate) await this._update(changes)
695
+ if (this._needsUpdate) await this._update(changes, false)
722
696
  await this._storeBoot()
723
697
  } finally {
724
698
  this._updating = null
@@ -900,13 +874,13 @@ module.exports = class Autobee extends ReadyResource {
900
874
  }
901
875
  }
902
876
 
903
- async _update(changes) {
877
+ async _update(changes, fastForward) {
904
878
  this._needsUpdate = false
905
879
  this.bee.update(this._workingBee.root)
906
880
 
907
881
  if (!changes) return
908
882
 
909
- changes.finalise()
883
+ changes.finalise(fastForward)
910
884
  await this._handlers.update(this.view, changes)
911
885
  }
912
886
 
@@ -1205,9 +1179,6 @@ module.exports = class Autobee extends ReadyResource {
1205
1179
  this.stats.undos++
1206
1180
  this.trusted.clear()
1207
1181
  t.view = await this.system.undo(t.undo)
1208
- if (!t.view.length && this._migratedHead && this._migratedHead.view) {
1209
- t.view = this._migratedHead.view
1210
- }
1211
1182
  }
1212
1183
 
1213
1184
  return t
@@ -1288,8 +1259,9 @@ module.exports = class Autobee extends ReadyResource {
1288
1259
  }
1289
1260
  }
1290
1261
 
1291
- _storeBoot() {
1262
+ async _storeBoot() {
1292
1263
  const proms = []
1264
+
1293
1265
  proms.push(
1294
1266
  this.local.setUserData(
1295
1267
  'autobee/previous-drain',
@@ -1302,12 +1274,15 @@ module.exports = class Autobee extends ReadyResource {
1302
1274
  proms.push(this.local.setUserData('autobee/head', encoding.encodeBootRecord(boot)))
1303
1275
  }
1304
1276
 
1305
- return Promise.all(proms)
1306
- }
1277
+ await Promise.all(proms)
1307
1278
 
1308
- _storeMigratedHead() {
1309
- const value = this._migratedHead ? encoding.encodeMigratedHead(this._migratedHead) : null
1310
- return this.local.setUserData('autobee/migrated-head', value)
1279
+ if (this._migrating) {
1280
+ // clear legacy data
1281
+ await this.bootstrap.setUserData('autobase/local', null)
1282
+ await this.local.setUserData('autobase/boot', null)
1283
+ await this.local.setUserData('autobase/encryption', null)
1284
+ this._migrating = false
1285
+ }
1311
1286
  }
1312
1287
 
1313
1288
  static decodeValue(buf, opts) {
@@ -1404,11 +1379,11 @@ module.exports = class Autobee extends ReadyResource {
1404
1379
  this._localViewLength = 0
1405
1380
  }
1406
1381
 
1407
- async moveTo(head) {
1382
+ async moveTo(head, { timeout = 0 } = {}) {
1408
1383
  if (!this._bootGuard.opened) await this._bootGuard.ready()
1409
1384
  if (this.closing) throw new Error('Autobee closed')
1410
1385
 
1411
- const ff = await FastForward.fromHead(this, head, null, { force: true })
1386
+ const ff = await FastForward.fromHead(this, head, null, { force: true, timeout })
1412
1387
  if (ff === null) return null
1413
1388
 
1414
1389
  if (!(await this._runFastForward(ff))) return null
@@ -1427,108 +1402,20 @@ module.exports = class Autobee extends ReadyResource {
1427
1402
  }
1428
1403
  }
1429
1404
 
1430
- // the boot head seeds the view every trust decision here is made against,
1431
- // since our own view is still empty
1432
- async _bootFromHead(head, bootCondition, wait = false) {
1433
- // just a head: one attempt, we do not wait around if it cannot be read
1434
- if (bootCondition === null && !wait) {
1435
- try {
1436
- const ff = await FastForward.fromHead(this, head, null, {
1437
- force: true,
1438
- timeout: FastForward.DEFAULT_TIMEOUT
1439
- })
1440
-
1441
- return ff !== null && (await this._runFastForward(ff))
1442
- } catch (err) {
1443
- safetyCatch(err)
1444
- return false
1445
- }
1446
- }
1447
-
1448
- // park until the head can be read and, if a condition is set, something
1449
- // acceptable turns up
1450
- let opened = null
1405
+ async _bootFromHead(head) {
1406
+ const timeout = FastForward.DEFAULT_TIMEOUT
1451
1407
 
1452
1408
  try {
1453
- while (!this._interrupting) {
1454
- this._bootWait = rrp()
1455
-
1456
- try {
1457
- if (bootCondition === null) {
1458
- const ff = await FastForward.fromHead(this, head, null, {
1459
- force: true,
1460
- timeout: FastForward.DEFAULT_TIMEOUT
1461
- })
1462
-
1463
- if (ff !== null && (await this._runFastForward(ff))) return true
1464
- } else {
1465
- if (opened === null) opened = await this._bootReference(head)
1466
-
1467
- if (opened !== null) {
1468
- if (await this._bootAttempt(head, bootCondition, opened.view)) {
1469
- return true
1470
- }
1471
-
1472
- // the reference may be stale - resolve it afresh next attempt
1473
- await opened.close()
1474
- opened = null
1475
- } else if (await this._bootLegacyHead(head)) {
1476
- // boot legacy head through the migration pathway
1477
- return true
1478
- }
1479
- }
1480
- } catch (err) {
1481
- safetyCatch(err)
1482
- }
1409
+ const oplog = await this._resolveOplogHint(head.key, head.length || 0, { timeout })
1410
+ if (oplog === null) return false
1483
1411
 
1484
- if (this._interrupting) break
1412
+ const ff = await FastForward.fromHead(this, oplog, null, { force: true, timeout })
1485
1413
 
1486
- await this._bootWait.promise
1487
- }
1488
- } finally {
1489
- this._bootWait = null
1490
- if (opened !== null) await opened.close()
1414
+ return ff !== null && (await this._runFastForward(ff))
1415
+ } catch (err) {
1416
+ safetyCatch(err)
1417
+ return false
1491
1418
  }
1492
-
1493
- return false
1494
- }
1495
-
1496
- async _bootAttempt(head, bootCondition, reference) {
1497
- // peek, so the hints are still there for the drain once we have booted
1498
- const result = await this._filterHints(this._wakeup.hints)
1499
-
1500
- const candidates = await this._readCandidateHeads(result, FastForward.DEFAULT_TIMEOUT)
1501
-
1502
- const ff = await FastForward.fromHeads(this, [head, ...candidates], {
1503
- force: true,
1504
- timeout: FastForward.DEFAULT_TIMEOUT,
1505
- condition: bootCondition,
1506
- reference
1507
- })
1508
-
1509
- return ff !== null && (await this._runFastForward(ff))
1510
- }
1511
-
1512
- async _bootLegacyHead(head) {
1513
- const oplog = await FastForward.flushHead(this, head, {
1514
- timeout: FastForward.DEFAULT_TIMEOUT
1515
- })
1516
- if (oplog === null || oplog.op.version > LEGACY_OPLOG_VERSION) return false
1517
-
1518
- const ff = await FastForward.fromHead(this, head, null, {
1519
- force: true,
1520
- timeout: FastForward.DEFAULT_TIMEOUT
1521
- })
1522
-
1523
- return ff !== null && (await this._runFastForward(ff))
1524
- }
1525
-
1526
- async _bootReference(head) {
1527
- const oplog = await FastForward.flushHead(this, head, {
1528
- timeout: FastForward.DEFAULT_TIMEOUT
1529
- })
1530
-
1531
- return oplog === null ? null : this.openViewAt(oplog)
1532
1419
  }
1533
1420
 
1534
1421
  async _runFastForward(ff) {
@@ -1554,34 +1441,32 @@ module.exports = class Autobee extends ReadyResource {
1554
1441
  return true
1555
1442
  }
1556
1443
 
1444
+ async _runMigration() {
1445
+ if (!this._handlers.migrate) {
1446
+ throw new Error('Missing migration handler')
1447
+ }
1448
+
1449
+ const migration = this.system.migration
1450
+ this.system.migration = null
1451
+
1452
+ await this._handlers.migrate(migration.views, migration.head)
1453
+ }
1454
+
1557
1455
  async _applyFastForward() {
1558
1456
  const changes = this._hasUpdate ? new UpdateChanges(this) : null
1559
1457
  if (changes) changes.track()
1560
1458
 
1561
- const { head, migrate } = this.fastForwardTo
1459
+ const { head } = this.fastForwardTo
1562
1460
 
1563
1461
  const from = this.system.bee.head()
1564
1462
  const to = head
1565
1463
 
1566
1464
  this.system.bee.move(head)
1567
1465
  await this.system.reset()
1466
+ if (this.system.migration) await this._runMigration()
1568
1467
 
1569
- // migrate is set when fast-forwarding from a legacy head
1570
- if (migrate) {
1571
- const view = (await this._handlers.migrate(migrate, head)) || EMPTY_HEAD
1572
-
1573
- this._migratedHead = {
1574
- system: head,
1575
- view: view.length ? view : (this._migratedHead && this._migratedHead.view) || null
1576
- }
1577
-
1578
- await this._storeMigratedHead()
1579
- this.bee.move(view)
1580
- this._workingBee.move(view)
1581
- } else {
1582
- this.bee.move(this.system.view)
1583
- this._workingBee.move(this.system.view)
1584
- }
1468
+ this.bee.move(this.system.view)
1469
+ this._workingBee.move(this.system.view)
1585
1470
 
1586
1471
  this._approvalCheck = true
1587
1472
  this.fastForwardTo = null
@@ -1593,7 +1478,7 @@ module.exports = class Autobee extends ReadyResource {
1593
1478
  // we moved, so ask our peers to tell us their heads again
1594
1479
  this._requestWakeup()
1595
1480
 
1596
- await this._update(changes)
1481
+ await this._update(changes, true)
1597
1482
  await this._storeBoot()
1598
1483
 
1599
1484
  this.stats.fastForwards++
@@ -1614,7 +1499,7 @@ function getBootOption(boot) {
1614
1499
  if (!boot) return null
1615
1500
 
1616
1501
  // oldest style, supported for now but will go away: a bare key is legacy
1617
- if (boot.key) return { legacy: boot, head: null, bootCondition: null }
1502
+ if (boot.key) return { legacy: boot, head: null }
1618
1503
 
1619
1504
  asserts.assert(!(boot.head && boot.legacy), 'Boot from either a head or a legacy pointer')
1620
1505
 
package/lib/boot.js CHANGED
@@ -5,7 +5,6 @@ const { checkAutobaseMigration } = require('./migrations.js')
5
5
  module.exports = async function boot(
6
6
  corestore,
7
7
  key,
8
- legacyViews,
9
8
  { encrypt, encryptionKey, keyPair, exclusive = true } = {}
10
9
  ) {
11
10
  const result = {
@@ -95,12 +94,7 @@ module.exports = async function boot(
95
94
  await result.bootstrap.setUserData('autobee/local', result.local.key)
96
95
  await result.local.setUserData('referrer', result.key)
97
96
 
98
- result.migration = await migrateWithFallback(
99
- corestore,
100
- result.local,
101
- result.bootstrap,
102
- legacyViews
103
- )
97
+ result.migration = await migrateWithFallback(corestore, result.local, result.bootstrap)
104
98
 
105
99
  if (result.migration) {
106
100
  await result.local.setUserData(
@@ -142,9 +136,9 @@ module.exports = async function boot(
142
136
  // the bootstrap pointer names the rotated-to writer, whose legacy boot record
143
137
  // can be unusable. the namespace local still carries the pre-rotation record,
144
138
  // which points at the last system core that was actually signed
145
- async function migrateWithFallback(corestore, local, bootstrap, legacyViews) {
139
+ async function migrateWithFallback(corestore, local, bootstrap) {
146
140
  try {
147
- return await checkAutobaseMigration(corestore, local, bootstrap, legacyViews)
141
+ return await checkAutobaseMigration(corestore, local, bootstrap)
148
142
  } catch (err) {
149
143
  const prev = corestore.get({ name: 'local', active: false })
150
144
  await prev.ready()
@@ -152,7 +146,7 @@ async function migrateWithFallback(corestore, local, bootstrap, legacyViews) {
152
146
  if (b4a.equals(prev.key, local.key)) throw err
153
147
  if (!(await prev.getUserData('autobase/boot'))) throw err
154
148
 
155
- return checkAutobaseMigration(corestore, prev, bootstrap, legacyViews)
149
+ return checkAutobaseMigration(corestore, prev, bootstrap)
156
150
  }
157
151
  }
158
152
 
package/lib/encoding.js CHANGED
@@ -12,19 +12,16 @@ const SystemInfo = getEncoding('@autobee/system-info')
12
12
  const SystemWriter = getEncoding('@autobee/system-writer')
13
13
  const ManifestData = getEncoding('@autobee/manifest-data')
14
14
  const AutobaseBootRecord = getEncoding('@autobase-compat/boot-record')
15
- const MigratedHead = getEncoding('@autobee/migrated-head')
16
15
 
17
16
  exports.OPLOG_VERSION = OPLOG_VERSION
18
17
 
19
18
  exports.Oplog = Oplog
19
+
20
20
  exports.ManifestData = ManifestData
21
21
 
22
22
  exports.encodeBootRecord = encodeBootRecord
23
23
  exports.decodeBootRecord = decodeBootRecord
24
24
 
25
- exports.encodeMigratedHead = encodeMigratedHead
26
- exports.decodeMigratedHead = decodeMigratedHead
27
-
28
25
  exports.encodePreviousDrain = encodePreviousDrain
29
26
  exports.decodePreviousDrain = decodePreviousDrain
30
27
 
@@ -50,14 +47,6 @@ function decodeBootRecord(value) {
50
47
  return c.decode(Link, value)
51
48
  }
52
49
 
53
- function encodeMigratedHead(m) {
54
- return c.encode(MigratedHead, m)
55
- }
56
-
57
- function decodeMigratedHead(value) {
58
- return c.decode(MigratedHead, value)
59
- }
60
-
61
50
  function encodePreviousDrain(d) {
62
51
  return c.encode(c.uint, d)
63
52
  }