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 +60 -42
- package/lib/boot.js +1 -0
- package/lib/{reboot.js → fast-forward.js} +35 -5
- package/package.json +1 -1
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
|
|
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.
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
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
|
-
|
|
529
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
1180
|
+
if (!trusted || this.fastForwarding || this.fastForwardTo) return false
|
|
1169
1181
|
} finally {
|
|
1170
1182
|
view.close()
|
|
1171
1183
|
}
|
|
1172
1184
|
|
|
1173
|
-
return this.
|
|
1185
|
+
return this._fastForwardFromHead(best, trusted, { wait, timeout })
|
|
1174
1186
|
}
|
|
1175
1187
|
|
|
1176
|
-
async
|
|
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, {
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
1229
|
+
return this._runFastForward(new FastForward(this, head, tip))
|
|
1212
1230
|
}
|
|
1213
1231
|
|
|
1214
|
-
return this.
|
|
1232
|
+
return this._fastForwardFromHead(head, null, { force: true, wait: false })
|
|
1215
1233
|
}
|
|
1216
1234
|
|
|
1217
|
-
// head is a system head;
|
|
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.
|
|
1237
|
+
if (this.fastForwardTo !== null || this.fastForwarding !== null) return false
|
|
1220
1238
|
|
|
1221
|
-
if (await this.
|
|
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
|
|
1229
|
-
this.
|
|
1246
|
+
async _runFastForward(ff) {
|
|
1247
|
+
this.fastForwarding = ff
|
|
1230
1248
|
|
|
1231
|
-
const result = await
|
|
1232
|
-
await
|
|
1249
|
+
const result = await ff.run()
|
|
1250
|
+
await ff.close()
|
|
1233
1251
|
|
|
1234
|
-
if (this.
|
|
1252
|
+
if (this.fastForwarding === ff) this.fastForwarding = null
|
|
1235
1253
|
|
|
1236
1254
|
if (!result) return false
|
|
1237
1255
|
|
|
1238
|
-
this.
|
|
1239
|
-
this.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
@@ -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
|
|
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(
|
|
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(
|
|
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)
|
|
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
|
}
|