autobee 2.0.0-rc.27 → 2.0.0-rc.28

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
@@ -1182,7 +1182,7 @@ module.exports = class Autobee extends ReadyResource {
1182
1182
  // legacy: ungated boot straight onto a system head
1183
1183
  async _bootFromSystem(system) {
1184
1184
  try {
1185
- const ff = new FastForward(this, system, null, { timeout: FastForward.DEFAULT_TIMEOUT })
1185
+ const ff = new FastForward(this, system, { timeout: FastForward.DEFAULT_TIMEOUT })
1186
1186
  return await this._runFastForward(ff)
1187
1187
  } catch (err) {
1188
1188
  safetyCatch(err)
@@ -1322,7 +1322,7 @@ module.exports = class Autobee extends ReadyResource {
1322
1322
  const changes = this._hasUpdate ? new UpdateChanges(this) : null
1323
1323
  if (changes) changes.track()
1324
1324
 
1325
- const { head, tip, migrate } = this.fastForwardTo
1325
+ const { head, migrate } = this.fastForwardTo
1326
1326
 
1327
1327
  const from = this.system.bee.head()
1328
1328
  const to = head
@@ -1362,28 +1362,6 @@ module.exports = class Autobee extends ReadyResource {
1362
1362
  this.stats.fastForwards++
1363
1363
  this.emit('move-to', to, from)
1364
1364
  this.ff.resolve({ to, from })
1365
-
1366
- // tip is null during boot
1367
- if (!tip) return
1368
-
1369
- try {
1370
- await this._reapply(tip)
1371
- } catch (err) {
1372
- throw err
1373
- }
1374
- }
1375
-
1376
- async _reapply({ system, verified }) {
1377
- const changes = this._hasUpdate ? new UpdateChanges(this) : null
1378
- if (changes) changes.track()
1379
-
1380
- const sys = this.system.bee.checkout(system)
1381
- const t = await topo.rollback(this, sys, verified)
1382
- await sys.close()
1383
-
1384
- if (t !== null) await this.applyBacklog(t.tip)
1385
-
1386
- return this._update(changes)
1387
1365
  }
1388
1366
 
1389
1367
  replay() {
package/lib/boot.js CHANGED
@@ -1,3 +1,4 @@
1
+ const b4a = require('b4a')
1
2
  const encoding = require('./encoding.js')
2
3
  const { checkAutobaseMigration } = require('./migrations.js')
3
4
 
@@ -94,7 +95,7 @@ module.exports = async function boot(
94
95
  await result.bootstrap.setUserData('autobee/local', result.local.key)
95
96
  await result.local.setUserData('referrer', result.key)
96
97
 
97
- result.migration = await checkAutobaseMigration(
98
+ result.migration = await migrateWithFallback(
98
99
  corestore,
99
100
  result.local,
100
101
  result.bootstrap,
@@ -138,6 +139,23 @@ module.exports = async function boot(
138
139
  return result
139
140
  }
140
141
 
142
+ // the bootstrap pointer names the rotated-to writer, whose legacy boot record
143
+ // can be unusable. the namespace local still carries the pre-rotation record,
144
+ // which points at the last system core that was actually signed
145
+ async function migrateWithFallback(corestore, local, bootstrap, legacyViews) {
146
+ try {
147
+ return await checkAutobaseMigration(corestore, local, bootstrap, legacyViews)
148
+ } catch (err) {
149
+ const prev = corestore.get({ name: 'local', active: false })
150
+ await prev.ready()
151
+
152
+ if (b4a.equals(prev.key, local.key)) throw err
153
+ if (!(await prev.getUserData('autobase/boot'))) throw err
154
+
155
+ return checkAutobaseMigration(corestore, prev, bootstrap, legacyViews)
156
+ }
157
+ }
158
+
141
159
  async function getLocalKey(bootstrap) {
142
160
  const [legacy, current] = await Promise.all([
143
161
  bootstrap.getUserData('autobase/local'),
@@ -14,7 +14,7 @@ const MIN_FF_GAP = 32
14
14
  const MAX_FLUSH_SEARCH = 64
15
15
 
16
16
  module.exports = class FastForward {
17
- constructor(auto, head, tip, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
17
+ constructor(auto, head, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
18
18
  this.auto = auto
19
19
 
20
20
  this.activeRequests = []
@@ -30,7 +30,6 @@ module.exports = class FastForward {
30
30
  // length -1 marks an uninitialised head: we know the key but not the length
31
31
  this.head = { key: head.key, length: head.length ?? 0 }
32
32
  this.timeout = timeout
33
- this.tip = tip || null
34
33
  this.destroyed = false
35
34
  this.running = null
36
35
  this.failed = false
@@ -68,15 +67,7 @@ module.exports = class FastForward {
68
67
  return null
69
68
  }
70
69
 
71
- const tip = {
72
- system: batchToHead(oplog.op.views.system),
73
- verified: {
74
- op: { key: verified.key, length: verified.length },
75
- flushes: verified.op.views.flushes
76
- }
77
- }
78
-
79
- return new FastForward(auto, batchToHead(verified.op.views.system), tip)
70
+ return new FastForward(auto, batchToHead(verified.op.views.system))
80
71
  }
81
72
 
82
73
  static async fromHeads(
@@ -246,7 +237,7 @@ module.exports = class FastForward {
246
237
  } catch (err) {
247
238
  safetyCatch(err)
248
239
  this.head = result.head
249
- return { head: result.head, tip: null, migrate: result.migrate }
240
+ return { head: result.head, migrate: result.migrate }
250
241
  }
251
242
 
252
243
  // fall through to the plain fast-forward below
@@ -278,7 +269,6 @@ module.exports = class FastForward {
278
269
 
279
270
  return {
280
271
  head: this.head,
281
- tip: this.tip,
282
272
  migrate: null
283
273
  }
284
274
  }
@@ -331,7 +321,6 @@ module.exports = class FastForward {
331
321
 
332
322
  return {
333
323
  head,
334
- tip: null,
335
324
  migrate: views,
336
325
  autobee: autobee === null ? null : batchToHead(autobee.op.views.system)
337
326
  }
package/lib/migrations.js CHANGED
@@ -37,23 +37,36 @@ async function checkAutobaseMigration(store, local, bootstrap, legacyViews) {
37
37
  const bootRecord = await local.getUserData('autobase/boot')
38
38
  if (!bootRecord) return null
39
39
 
40
- const { key } = encoding.decodeAutobaseBootRecord(bootRecord)
40
+ const { key, systemLength } = encoding.decodeAutobaseBootRecord(bootRecord)
41
41
  const core = store.get({ key: key, encryption: null })
42
42
  await core.ready()
43
43
 
44
- const system = { key, length: core.length }
44
+ const length = await findLocalSystemLength(core, systemLength)
45
+ assert(length > 0, 'Expected system block to exist locally')
46
+
47
+ const system = { key, length }
45
48
 
46
49
  // setup encryption
47
50
  const encryptionKey = await local.getUserData('autobase/encryption')
48
51
  await AutobeeEncryption.setSystemEncryption(bootstrap.key, encryptionKey, core)
49
52
 
50
- const catchup = await getCatchupHeads(core)
53
+ const session = core.session({ name: 'batch' })
54
+ await session.ready()
55
+
56
+ let catchup
57
+
58
+ try {
59
+ catchup = await getCatchupHeads(session, length)
60
+ } finally {
61
+ await session.close()
62
+ }
63
+
64
+ const info = await readLegacySystemInfo(core, length, { wait: false })
65
+
51
66
  const nodes = await Promise.all(
52
67
  catchup.map((n) => getWriterBatch(store, n, bootstrap.key, encryptionKey))
53
68
  )
54
69
 
55
- const info = await readLegacySystemInfo(core, core.length)
56
-
57
70
  const indexerManifests = await Promise.all(
58
71
  info.indexers.map((idx) => storeCoreManifest(store, idx.key))
59
72
  )
@@ -89,8 +102,11 @@ async function checkAutobaseMigration(store, local, bootstrap, legacyViews) {
89
102
 
90
103
  // decode the legacy system info recorded in the system core's block at length
91
104
  async function readLegacySystemInfo(core, length, opts = null) {
105
+ const block = await core.get(length - 1, opts)
106
+ assert(block !== null, 'Expected system block to exist locally')
107
+
92
108
  // Decode hyperbee block using hyperbee2 compat
93
- const node = decodeBlock(await core.get(length - 1, opts))
109
+ const node = decodeBlock(block)
94
110
  assert(node.keys.length > 0, 'bad system block')
95
111
 
96
112
  // Decode system info from hyperbee block using autobee compat
@@ -259,6 +275,15 @@ function deriveNamespace(name, bootstrap, entropy, encryptionKey) {
259
275
  ])
260
276
  }
261
277
 
278
+ // the record's length can run past the signed core into the local batch
279
+ // session, and the signed core can carry verified-but-undownloaded blocks -
280
+ // scan back for the newest block the signed core actually has locally
281
+ async function findLocalSystemLength(core, systemLength) {
282
+ let length = Math.min(core.length, systemLength)
283
+ while (length > 0 && !(await core.has(length - 1))) length--
284
+ return length
285
+ }
286
+
262
287
  function coreLength(core, timeout) {
263
288
  if (core.length) return core.length
264
289
 
@@ -355,18 +380,18 @@ async function getPersistedView(store, view) {
355
380
  const core = store.get(view.key)
356
381
  await core.ready()
357
382
 
358
- const length = core.length
383
+ // core.length can run past what was downloaded - the system's recorded
384
+ // length is the last one we know is local
385
+ const length = Math.min(core.length, view.length)
359
386
  await core.close()
360
387
 
361
388
  return { key: view.key, length }
362
389
  }
363
390
 
364
- async function getCatchupHeads(core) {
365
- const session = core.session({ name: 'batch' })
366
- await session.ready()
367
-
391
+ async function getCatchupHeads(session, from) {
368
392
  const nodes = []
369
- for (let i = core.length; i < session.length; i++) {
393
+ // block 0 is the bee header, never a node
394
+ for (let i = Math.max(from, 1); i < session.length; i++) {
370
395
  const node = await session.get(i, { wait: false })
371
396
  if (!node) throw new Error('Expect nodes to exist locally')
372
397
 
@@ -378,8 +403,6 @@ async function getCatchupHeads(core) {
378
403
  nodes.push({ key, length })
379
404
  }
380
405
 
381
- await session.close()
382
-
383
406
  return nodes
384
407
  }
385
408
 
package/lib/topo.js CHANGED
@@ -10,7 +10,6 @@ const INFO_LEGACY_KEY = b4a.concat([b4a.from([0, 0]), b4a.from('info')])
10
10
  const DEFAULT_OP_TIMEOUT = 5000
11
11
 
12
12
  exports.sort = sort
13
- exports.rollback = rollback
14
13
  exports.replay = replay
15
14
  exports.getMostRecentHead = getMostRecentHead
16
15
 
@@ -49,46 +48,6 @@ async function getMostRecentHead(auto, system) {
49
48
  return null
50
49
  }
51
50
 
52
- async function rollback(auto, system, verified) {
53
- if (!(await isApplied(system, verified.op))) return null
54
-
55
- const tip = []
56
- const ch = system.createChangesStream()
57
-
58
- let found = false
59
-
60
- for await (const data of ch) {
61
- const { ref, info } = getOplog(data)
62
-
63
- if (sameNode(ref.oplog, verified.op)) {
64
- // stale views.flushes - the verified head describes a prefix this system
65
- // does not share, so nothing above it is ours to reapply
66
- if (info.flushes === verified.flushes) found = true
67
- break
68
- }
69
-
70
- // walking back, a writer's lengths only decrease - once this writer is
71
- // behind the verified head the marker cannot be further down
72
- if (b4a.equals(ref.oplog.key, verified.op.key) && ref.oplog.length < verified.op.length) {
73
- break
74
- }
75
-
76
- tip.push(ref)
77
- }
78
-
79
- if (!found) return null
80
-
81
- const inflated = await inflate(auto, tip.reverse())
82
-
83
- // @todo: verify sort
84
-
85
- return {
86
- undo: null,
87
- view: null,
88
- tip: inflated.map(getBatch)
89
- }
90
- }
91
-
92
51
  // nodes we cannot resolve are returned as null rather than dropped, so callers
93
52
  // can tell an incomplete replay from a short one
94
53
  async function replay(auto) {
@@ -109,14 +68,6 @@ async function replay(auto) {
109
68
  return inflated.flatMap((b) => (b === null ? [null] : b.batch))
110
69
  }
111
70
 
112
- async function isApplied(system, node) {
113
- const entry = await system.get(encoding.encodeSystemWriterKey(node.key))
114
- if (entry === null) return false
115
-
116
- const info = encoding.decodeSystemWriter(entry.key, entry.value)
117
- return info.length >= node.length
118
- }
119
-
120
71
  async function sort(auto, batch) {
121
72
  const node = batch[0]
122
73
  const ch = auto.system.bee.createChangesStream()
@@ -125,7 +76,7 @@ async function sort(auto, batch) {
125
76
  for await (const data of ch) {
126
77
  if (isLegacyNode(data)) break
127
78
 
128
- const { ref, info } = getOplog(data)
79
+ const { ref } = getOplog(data)
129
80
  const link = {
130
81
  key: ref.oplog.key,
131
82
  length: ref.oplog.length,
@@ -448,11 +399,3 @@ function isLegacyNode(data) {
448
399
  }
449
400
  return false
450
401
  }
451
-
452
- function sameNode(a, b) {
453
- return b4a.equals(a.key, b.key) && a.length === b.length
454
- }
455
-
456
- function getBatch(b) {
457
- return b.batch
458
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.0.0-rc.27",
3
+ "version": "2.0.0-rc.28",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",