autobee 2.0.0-rc.14 → 2.0.0-rc.15

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
@@ -14,7 +14,7 @@ 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 Reboot = require('./lib/reboot.js')
17
+ const FastForward = require('./lib/fast-forward.js')
18
18
  const System = require('./lib/system.js')
19
19
  const ApplyCalls = require('./lib/apply-calls.js')
20
20
  const topo = require('./lib/topo.js')
@@ -85,9 +85,9 @@ module.exports = class Autobee extends ReadyResource {
85
85
  // conservative (default on): only fast-forward onto a head someone can serve whole
86
86
  this._conservativeFF = fastForward.conservative !== false
87
87
 
88
- this.reboot = null
89
- this.rebooting = null
90
- this.rebootTo = null
88
+ this.ff = null
89
+ this.fastForwarding = null
90
+ this.fastForwardTo = null
91
91
 
92
92
  this._workingBee = bee
93
93
  this._workingView = handlers.open ? handlers.open(this._workingBee, this) : this._workingBee
@@ -250,6 +250,8 @@ module.exports = class Autobee extends ReadyResource {
250
250
  session.clearRequests(session.activeRequests, closingError)
251
251
  }
252
252
  }
253
+
254
+ if (this.fastForwarding) this.fastForwarding.clearRequests(closingError)
253
255
  }
254
256
 
255
257
  async changesFrom({ system } = {}) {
@@ -441,8 +443,6 @@ module.exports = class Autobee extends ReadyResource {
441
443
  async _bump() {
442
444
  if (!(await this._bootReady())) return
443
445
 
444
- await this._flushWakeup()
445
-
446
446
  this.bumping++
447
447
 
448
448
  if (!this._draining) {
@@ -525,8 +525,11 @@ module.exports = class Autobee extends ReadyResource {
525
525
 
526
526
  try {
527
527
  while (!this._interrupting) {
528
- if (this.rebootTo !== null) {
529
- await this._applyReboot()
528
+ await this._flushWakeup()
529
+ if (this._interrupting) break
530
+
531
+ if (this.fastForwardTo !== null) {
532
+ await this._applyFastForward()
530
533
  break // revaluate conditions...
531
534
  }
532
535
 
@@ -593,15 +596,28 @@ module.exports = class Autobee extends ReadyResource {
593
596
 
594
597
  async _flushWakeup() {
595
598
  const hints = this._wakeup.flush()
599
+ if (!hints.size) return
596
600
 
597
601
  this.previousDrain = Date.now()
598
- this.rebootFromHeads(hints).catch(noop)
599
602
 
600
603
  for (const [hex, length] of hints) {
601
604
  const key = b4a.from(hex, 'hex')
602
605
  if (this.writers.has(hex)) continue
603
606
  await this.writers.wakeup(key, length)
604
607
  }
608
+
609
+ // a scheduled fast-forward is applied by the drain before we look again
610
+ if (this.fastForwardTo !== null) return
611
+ if (this._interrupting || this.closing) return
612
+
613
+ try {
614
+ await this.fastForwardFromHeads(hints, { wait: false, timeout: FastForward.DEFAULT_TIMEOUT })
615
+ } catch (err) {
616
+ safetyCatch(err)
617
+ return false
618
+ }
619
+
620
+ return true
605
621
  }
606
622
 
607
623
  async _getOplog(key, length, opts = null) {
@@ -734,7 +750,7 @@ module.exports = class Autobee extends ReadyResource {
734
750
  await this.bootstrap.setUserData('autobee/local', this.local.key)
735
751
  await oldLocal.close()
736
752
 
737
- // done, soft reboot
753
+ // done, soft restart
738
754
  this.emit('rotate-local-writer')
739
755
  }
740
756
 
@@ -1119,10 +1135,10 @@ module.exports = class Autobee extends ReadyResource {
1119
1135
  this._localViewLength = 0
1120
1136
  }
1121
1137
 
1122
- async rebootFromHeads(hints, { force = false } = {}) {
1138
+ async fastForwardFromHeads(hints, { wait = true, timeout = 0 } = {}) {
1123
1139
  if (!this._handlers.onwakeup) return false
1124
1140
  if (this._interrupting) return false
1125
- if (!hints.size || this.rebooting || this.rebootTo || this.bootFrom) {
1141
+ if (!hints.size || this.fastForwarding || this.fastForwardTo || this.bootFrom) {
1126
1142
  return false
1127
1143
  }
1128
1144
 
@@ -1132,11 +1148,11 @@ module.exports = class Autobee extends ReadyResource {
1132
1148
  if (length === 0) continue
1133
1149
  const key = b4a.from(hex, 'hex')
1134
1150
  heads.push({ key, length })
1135
- promises.push(this._getOplog(key, length))
1151
+ promises.push(this._getOplog(key, length, timeout ? { timeout } : null))
1136
1152
  }
1137
1153
 
1138
1154
  const ops = await Promise.all(promises)
1139
- if (this.rebooting || this.rebootTo) return false
1155
+ if (this.fastForwarding || this.fastForwardTo) return false
1140
1156
 
1141
1157
  let best = null
1142
1158
  let bestFlushes = -1
@@ -1151,10 +1167,6 @@ module.exports = class Autobee extends ReadyResource {
1151
1167
  }
1152
1168
  }
1153
1169
 
1154
- if (best && force) {
1155
- return this._rebootFromHead(best, null, { force: true })
1156
- }
1157
-
1158
1170
  if (best === null || bestFlushes - this.system.flushes < MIN_FF_GAP) return false
1159
1171
 
1160
1172
  const head = { key: best.key, length: best.length }
@@ -1165,24 +1177,30 @@ module.exports = class Autobee extends ReadyResource {
1165
1177
  let trusted = null
1166
1178
  try {
1167
1179
  trusted = await this._handlers.onwakeup(head, view, this)
1168
- if (!trusted || this.rebooting || this.rebootTo) return false
1180
+ if (!trusted || this.fastForwarding || this.fastForwardTo) return false
1169
1181
  } finally {
1170
1182
  view.close()
1171
1183
  }
1172
1184
 
1173
- return this._rebootFromHead(best, trusted)
1185
+ return this._fastForwardFromHead(best, trusted, { wait, timeout })
1174
1186
  }
1175
1187
 
1176
- async _rebootFromHead(head, trusted, { force = false, wait = true } = {}) {
1188
+ async _fastForwardFromHead(head, trusted, { force = false, wait = true, timeout = 0 } = {}) {
1177
1189
  const conservative = !force && this._conservativeFF
1190
+ const timeoutOpts = timeout ? { timeout } : null
1178
1191
 
1179
- const oplog = await this._getOplog(head.key, head.length, { conservative })
1192
+ const oplog = await this._getOplog(head.key, head.length, {
1193
+ conservative,
1194
+ ...timeoutOpts
1195
+ })
1180
1196
  if (!oplog) return null
1181
1197
 
1182
- const verified = trusted ? await this._getOplog(trusted.key, trusted.length) : oplog
1198
+ const verified = trusted
1199
+ ? await this._getOplog(trusted.key, trusted.length, timeoutOpts)
1200
+ : oplog
1183
1201
  if (!verified) return null
1184
1202
 
1185
- // legacy nodes from non-indexers have no system info to reboot from
1203
+ // legacy nodes from non-indexers have no system info to fast-forward from
1186
1204
  if (!oplog.op.views || !verified.op.views) return null
1187
1205
 
1188
1206
  if (!force && verified.op.views.flushes - this.system.flushes < MIN_FF_GAP) {
@@ -1199,7 +1217,7 @@ module.exports = class Autobee extends ReadyResource {
1199
1217
 
1200
1218
  const moved = await this._moveTo(batchToHead(verified.op.views.system), tip)
1201
1219
 
1202
- if (moved && wait) return this.reboot.promise
1220
+ if (moved && wait) return this.ff.promise
1203
1221
 
1204
1222
  return null
1205
1223
  }
@@ -1208,46 +1226,46 @@ module.exports = class Autobee extends ReadyResource {
1208
1226
  _initFromHead(head, tip) {
1209
1227
  if (!head.length) {
1210
1228
  // legacy fastForward boot
1211
- return this._runReboot(new Reboot(this, head, tip))
1229
+ return this._runFastForward(new FastForward(this, head, tip))
1212
1230
  }
1213
1231
 
1214
- return this._rebootFromHead(head, null, { force: true, wait: false })
1232
+ return this._fastForwardFromHead(head, null, { force: true, wait: false })
1215
1233
  }
1216
1234
 
1217
- // head is a system head; reboots onto it, migrating in place if it's a legacy version
1235
+ // head is a system head; fast-forwards onto it, migrating in place if it's a legacy version
1218
1236
  async _moveTo(systemHead, tip) {
1219
- if (this.rebootTo !== null || this.rebooting !== null) return false
1237
+ if (this.fastForwardTo !== null || this.fastForwarding !== null) return false
1220
1238
 
1221
- if (await this._runReboot(new Reboot(this, systemHead, tip))) {
1239
+ if (await this._runFastForward(new FastForward(this, systemHead, tip))) {
1222
1240
  return true
1223
1241
  }
1224
1242
 
1225
1243
  return false
1226
1244
  }
1227
1245
 
1228
- async _runReboot(reboot) {
1229
- this.rebooting = reboot
1246
+ async _runFastForward(ff) {
1247
+ this.fastForwarding = ff
1230
1248
 
1231
- const result = await reboot.run()
1232
- await reboot.close()
1249
+ const result = await ff.run()
1250
+ await ff.close()
1233
1251
 
1234
- if (this.rebooting === reboot) this.rebooting = null
1252
+ if (this.fastForwarding === ff) this.fastForwarding = null
1235
1253
 
1236
1254
  if (!result) return false
1237
1255
 
1238
- this.rebootTo = result
1239
- this.reboot = rrp()
1256
+ this.fastForwardTo = result
1257
+ this.ff = rrp()
1240
1258
 
1241
1259
  this.bumpSoon()
1242
1260
 
1243
1261
  return true
1244
1262
  }
1245
1263
 
1246
- async _applyReboot() {
1264
+ async _applyFastForward() {
1247
1265
  const changes = this._hasUpdate ? new UpdateChanges(this) : null
1248
1266
  if (changes) changes.track()
1249
1267
 
1250
- const { head, tip, migrate } = this.rebootTo
1268
+ const { head, tip, migrate } = this.fastForwardTo
1251
1269
 
1252
1270
  const from = this.system.bee.head()
1253
1271
  const to = head
@@ -1255,7 +1273,7 @@ module.exports = class Autobee extends ReadyResource {
1255
1273
  this.system.bee.move(head)
1256
1274
  await this.system.reset()
1257
1275
 
1258
- this.system.shared = this.rebootTo.shared || {
1276
+ this.system.shared = this.fastForwardTo.shared || {
1259
1277
  flushes: -1,
1260
1278
  view: EMPTY_HEAD,
1261
1279
  system: EMPTY_HEAD
@@ -1272,7 +1290,7 @@ module.exports = class Autobee extends ReadyResource {
1272
1290
  this._workingBee.move(this.system.view)
1273
1291
  }
1274
1292
 
1275
- this.rebootTo = null
1293
+ this.fastForwardTo = null
1276
1294
  await this.writers.refresh()
1277
1295
 
1278
1296
  await this._storeBoot()
@@ -1281,7 +1299,7 @@ module.exports = class Autobee extends ReadyResource {
1281
1299
 
1282
1300
  this.stats.fastForwards++
1283
1301
  this.emit('move-to', to, from)
1284
- this.reboot.resolve({ to, from })
1302
+ this.ff.resolve({ to, from })
1285
1303
 
1286
1304
  // tip is null during boot
1287
1305
  if (!tip) return
package/lib/boot.js CHANGED
@@ -242,6 +242,7 @@ async function getCatchupHeads(core) {
242
242
  if (entry.key[0] !== 0x01) continue
243
243
 
244
244
  const { key, length } = encoding.decodeSystemWriter(entry.key, entry.value)
245
+ if (!length) continue
245
246
  nodes.push({ key, length })
246
247
  }
247
248
 
@@ -1,5 +1,6 @@
1
1
  const safetyCatch = require('safety-catch')
2
2
  const b4a = require('b4a')
3
+ const Hypercore = require('hypercore')
3
4
  const c = require('compact-encoding')
4
5
  const crypto = require('hypercore-crypto')
5
6
 
@@ -19,12 +20,18 @@ const [NS_SIGNER_NAMESPACE] = crypto.namespace('autobase', 1)
19
20
  const EMPTY = b4a.alloc(0)
20
21
  const DEFAULT_OP_TIMEOUT = 5_000
21
22
 
22
- module.exports = class Reboot {
23
+ module.exports = class FastForward {
23
24
  constructor(auto, head, tip, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
24
25
  this.auto = auto
26
+
27
+ this.activeRequests = []
28
+
29
+ // 'reboot' is a storage namespace: renaming it would derive a different
30
+ // scratch core on disk, so it stays as is
25
31
  this.system = new System(auto.store.namespace('reboot'), null, {
26
32
  getEncryptionProvider: this.auto.getSystemEncryption,
27
- encrypted: this.encrypted
33
+ encrypted: this.encrypted,
34
+ activeRequests: this.activeRequests
28
35
  })
29
36
 
30
37
  // length -1 marks an uninitialised head: we know the key but not the length
@@ -37,6 +44,8 @@ module.exports = class Reboot {
37
44
  this.cores = []
38
45
  }
39
46
 
47
+ static DEFAULT_TIMEOUT = DEFAULT_OP_TIMEOUT
48
+
40
49
  async run() {
41
50
  try {
42
51
  if (!this.running) this.running = this._run()
@@ -77,7 +86,12 @@ module.exports = class Reboot {
77
86
  const view = this.auto.store.get({ key: this.system.view.key, active: true })
78
87
  this.cores.push(view)
79
88
 
80
- promises.push(view.get(this.system.view.length - 1, { timeout: this.timeout }))
89
+ promises.push(
90
+ view.get(this.system.view.length - 1, {
91
+ timeout: this.timeout,
92
+ activeRequests: this.activeRequests
93
+ })
94
+ )
81
95
 
82
96
  for (const head of this.system.heads) {
83
97
  promises.push(this.system.get(head.key, { timeout: this.timeout }))
@@ -118,7 +132,12 @@ module.exports = class Reboot {
118
132
  await core.setEncryption(this.auto.getSystemEncryption())
119
133
 
120
134
  // Decode hyperbee block using hyperbee2 compat
121
- const node = decodeBlock(await core.get(this.head.length - 1, { timeout: this.timeout }))
135
+ const node = decodeBlock(
136
+ await core.get(this.head.length - 1, {
137
+ timeout: this.timeout,
138
+ activeRequests: this.activeRequests
139
+ })
140
+ )
122
141
  assert(node.keys.length > 0, 'bad system block')
123
142
 
124
143
  // Decode system info from hyperbee block using autobee compat
@@ -163,7 +182,12 @@ module.exports = class Reboot {
163
182
  await core.ready()
164
183
 
165
184
  try {
166
- if (!core.manifest) await core.get(length - 1, { timeout: this.timeout })
185
+ if (!core.manifest) {
186
+ await core.get(length - 1, {
187
+ timeout: this.timeout,
188
+ activeRequests: this.activeRequests
189
+ })
190
+ }
167
191
  } catch {
168
192
  return null
169
193
  }
@@ -191,8 +215,14 @@ module.exports = class Reboot {
191
215
  }
192
216
  }
193
217
 
218
+ // cancels every read in flight and fails any that come after
219
+ clearRequests(err = null) {
220
+ Hypercore.destroyRequests(this.activeRequests, err)
221
+ }
222
+
194
223
  async close() {
195
224
  this.destroyed = true
225
+ this.clearRequests()
196
226
  if (this.system) await this.system.close()
197
227
  for (const core of this.cores) await core.close()
198
228
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.0.0-rc.14",
3
+ "version": "2.0.0-rc.15",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",