autobee 2.11.8 → 2.12.1

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
@@ -285,10 +285,12 @@ Pass one of `head` or `legacy`, not both.
285
285
 
286
286
  #### `fastForward.conservative`
287
287
 
288
- Defaults to `true`: only fast-forward once the system and view lengths the head stamps are held whole, either locally or by a connected peer.
288
+ Defaults to `false`. When `true`, only fast-forward once the system and view lengths the head stamps are held whole, either locally or by a connected peer.
289
289
 
290
290
  The check runs at the end of the fast-forward, after the system has booted and the view head has been fetched, so a sparse view nobody can serve is skipped instead of landed on.
291
291
 
292
+ A node that landed via fast-forward holds its cores sparse, so it never satisfies the check for others. Leave it off unless every peer is warmed up to hold the view whole.
293
+
292
294
  #### `await db.moveTo(head, [options])`
293
295
 
294
296
  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.
package/index.js CHANGED
@@ -110,8 +110,8 @@ module.exports = class Autobee extends ReadyResource {
110
110
  // oplog head to boot from: migrates or fast-forwards depending on its version
111
111
  this.bootFrom = (fastForward && fastForward.boot) || null
112
112
 
113
- // conservative (default on): only fast-forward onto a system and view held whole
114
- this._conservativeFF = fastForward === null || fastForward.conservative !== false
113
+ // conservative (default off): only fast-forward onto a system and view held whole
114
+ this._conservativeFF = fastForward !== null && fastForward.conservative === true
115
115
 
116
116
  this.trusted = new TrustedPeers(this, handlers)
117
117
 
@@ -508,10 +508,14 @@ module.exports = class Autobee extends ReadyResource {
508
508
  this.bootFrom = getBootOption(await this.bootFrom)
509
509
  }
510
510
 
511
- return boot(this.store, this.key, {
511
+ const result = await boot(this.store, this.key, {
512
512
  encryptionKey: this.encryptionKey,
513
513
  keyPair: this.keyPair
514
514
  })
515
+
516
+ if (result.encryptionKey) this.encrypted = true
517
+
518
+ return result
515
519
  }
516
520
 
517
521
  async _boot() {
@@ -987,15 +991,7 @@ module.exports = class Autobee extends ReadyResource {
987
991
  for (const res of ops) {
988
992
  if (res === null) continue
989
993
 
990
- // the head we were woken on is a candidate in its own right
991
994
  heads.push({ key: res.key, length: res.length })
992
-
993
- if (!res.op.trusted) continue
994
-
995
- for (const trusted of res.op.trusted) {
996
- const head = this.trusted.read(trusted)
997
- if (head !== null) heads.push(head)
998
- }
999
995
  }
1000
996
 
1001
997
  return heads
@@ -45,16 +45,24 @@ class FastForward {
45
45
  trusted,
46
46
  { force = false, skipGap = false, rewind = false, timeout = 0 } = {}
47
47
  ) {
48
- const conservative = !force && auto._conservativeFF
49
-
50
48
  const oplog = await FastForward.flushHead(auto, head, { timeout })
51
49
  if (!oplog) return null
52
50
 
53
51
  const verified = trusted ? await FastForward.flushHead(auto, trusted, { timeout }) : oplog
54
52
  if (!verified) return null
55
53
 
54
+ return FastForward.fromVerified(auto, verified, { force, skipGap, rewind, timeout })
55
+ }
56
+
57
+ static fromVerified(
58
+ auto,
59
+ verified,
60
+ { force = false, skipGap = false, rewind = false, timeout = 0 } = {}
61
+ ) {
62
+ const conservative = !force && auto._conservativeFF
63
+
56
64
  // legacy nodes from non-indexers have no system info to fast-forward from
57
- if (!oplog.op.views || !verified.op.views) return null
65
+ if (!verified.op.views) return null
58
66
 
59
67
  // never move backwards, even when forced or the gap is skipped
60
68
  if (!rewind && verified.op.views.flushes < auto.system.flushes) return null
@@ -71,69 +79,95 @@ class FastForward {
71
79
 
72
80
  static async fromHeads(auto, heads, { force = false, skipGap = false, timeout = 0 } = {}) {
73
81
  const reference = auto._workingView.view
82
+ const opTimeout = timeout || DEFAULT_OP_TIMEOUT
74
83
 
75
- const promises = []
76
-
77
- for (const head of heads) {
78
- if (head.length === 0) continue
79
- promises.push(FastForward.flushHead(auto, head, { timeout }))
80
- }
81
-
82
- const ops = await FastForward.reindexedCandidates(auto, await Promise.all(promises), {
83
- timeout: timeout || DEFAULT_OP_TIMEOUT
84
- })
84
+ const hints = await FastForward.flushHeads(auto, heads, timeout)
85
85
  if (auto.fastForwarding || auto.fastForwardTo) return null
86
86
 
87
- const trust = await Promise.all(ops.map((res) => auto.trusted.isTrusted(res.key, reference)))
87
+ const trust = await Promise.all(hints.map((res) => auto.trusted.isTrusted(res.key, reference)))
88
88
  if (auto.fastForwarding || auto.fastForwardTo) return null
89
89
 
90
- const candidates = []
91
-
92
- let bestTrusted = null
93
- let bestTrustedFlushes = -1
90
+ const landings = new Map()
91
+ const unverified = new Map()
94
92
 
95
- for (let i = 0; i < ops.length; i++) {
96
- const res = ops[i]
97
- if (res.op.views.flushes < auto.system.flushes) continue
98
- if (!force && !skipGap && res.op.views.flushes - auto.system.flushes < MIN_FF_GAP) continue
93
+ const promises = []
99
94
 
100
- if (!trust[i]) {
101
- candidates.push(res)
95
+ for (let i = 0; i < hints.length; i++) {
96
+ if (trust[i]) {
97
+ addOplog(landings, hints[i])
102
98
  continue
103
99
  }
104
100
 
105
- if (res.op.views.flushes > bestTrustedFlushes) {
106
- bestTrustedFlushes = res.op.views.flushes
107
- bestTrusted = res
108
- }
101
+ promises.push(
102
+ FastForward.signalled(auto, hints[i], reference, timeout).then((sig) => {
103
+ for (const { oplog, trusted } of sig) addOplog(trusted ? landings : unverified, oplog)
104
+ })
105
+ )
109
106
  }
110
107
 
111
- if (bestTrusted !== null) {
112
- return FastForward.fromHead(auto, bestTrusted, null, { force, skipGap, timeout })
108
+ await Promise.all(promises)
109
+ if (auto.fastForwarding || auto.fastForwardTo) return null
110
+
111
+ const ranked = [...landings.values()].sort(byFlushes)
112
+
113
+ const reindexed = ranked.map((res) => FastForward.isReindexed(auto, res, opTimeout))
114
+
115
+ for (let i = 0; i < ranked.length; i++) {
116
+ if (!(await reindexed[i])) continue
117
+ if (auto.fastForwarding || auto.fastForwardTo) return null
118
+
119
+ const ff = FastForward.fromVerified(auto, ranked[i], { force, skipGap, timeout })
120
+ if (ff !== null) return ff
113
121
  }
114
122
 
115
- candidates.sort((a, b) => b.op.views.flushes - a.op.views.flushes)
123
+ for (const sig of [...unverified.values()].sort(byFlushes)) {
124
+ if (sig.op.views.flushes < auto.system.flushes) continue
125
+ if (!force && !skipGap && sig.op.views.flushes - auto.system.flushes < MIN_FF_GAP) continue
116
126
 
117
- for (const res of candidates) {
118
- const trusted = await FastForward.mostRecentTrusted(auto, res, reference, timeout)
119
- if (trusted === null) continue
127
+ const head = await FastForward.mostRecentTrusted(auto, sig, reference, timeout)
128
+ if (head === null) continue
120
129
  if (auto.fastForwarding || auto.fastForwardTo) return null
121
130
 
122
- const ff = await FastForward.fromHead(auto, res, trusted, { force, skipGap, timeout })
131
+ const verified = await FastForward.flushHead(auto, head, { timeout })
132
+ if (verified === null) continue
133
+ if (!(await FastForward.isReindexed(auto, verified, opTimeout))) continue
134
+ if (auto.fastForwarding || auto.fastForwardTo) return null
135
+
136
+ const ff = FastForward.fromVerified(auto, verified, { force, skipGap, timeout })
123
137
  if (ff !== null) return ff
124
138
  }
125
139
 
126
140
  return null
127
141
  }
128
142
 
129
- static async reindexedCandidates(auto, ops, { timeout }) {
130
- const withViews = ops.filter((res) => res !== null && res.op.views)
131
- const reindexed = await Promise.all(
132
- withViews.map((res) => FastForward.isReindexed(auto, res, timeout))
143
+ static async flushHeads(auto, heads, timeout) {
144
+ const promises = []
145
+
146
+ for (const head of heads) {
147
+ if (head.length === 0) continue
148
+ promises.push(FastForward.flushHead(auto, head, { timeout }))
149
+ }
150
+
151
+ return (await Promise.all(promises)).filter((res) => res !== null)
152
+ }
153
+
154
+ static async signalled(auto, res, reference, timeout) {
155
+ const heads = []
156
+
157
+ for (const entry of res.op.trusted || []) {
158
+ const head = auto.trusted.read(entry)
159
+ if (head !== null && head.length > 0) heads.push(head)
160
+ }
161
+
162
+ const oplogs = await FastForward.flushHeads(auto, heads, timeout)
163
+ const trust = await Promise.all(
164
+ oplogs.map((oplog) => auto.trusted.isTrusted(oplog.key, reference))
133
165
  )
134
- return withViews.filter((res, i) => reindexed[i])
166
+
167
+ return oplogs.map((oplog, i) => ({ oplog, trusted: !!trust[i] }))
135
168
  }
136
169
 
170
+ // never rejects, the search may return before awaiting every check
137
171
  static async isReindexed(auto, res, timeout) {
138
172
  const { system, view } = res.op.views
139
173
  const checks = [system, view]
@@ -144,8 +178,13 @@ class FastForward {
144
178
  return auto._shouldReindex(key, { unknown: true, length, timeout })
145
179
  })
146
180
 
147
- for (const shouldReindex of await Promise.all(checks)) {
148
- if (shouldReindex) return false
181
+ try {
182
+ for (const shouldReindex of await Promise.all(checks)) {
183
+ if (shouldReindex) return false
184
+ }
185
+ } catch (err) {
186
+ safetyCatch(err)
187
+ return false
149
188
  }
150
189
 
151
190
  return true
@@ -404,6 +443,14 @@ function isContiguous(core, length) {
404
443
  return core.contiguousLength >= length || core.remoteContiguousLength >= length
405
444
  }
406
445
 
446
+ function byFlushes(a, b) {
447
+ return b.op.views.flushes - a.op.views.flushes
448
+ }
449
+
450
+ function addOplog(map, oplog) {
451
+ map.set(b4a.toString(oplog.key, 'hex') + ':' + oplog.length, oplog)
452
+ }
453
+
407
454
  function batchToHead(b) {
408
455
  return {
409
456
  key: b.key,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.11.8",
3
+ "version": "2.12.1",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",