autobee 2.0.0-rc.17 → 2.0.0-rc.19
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 +45 -69
- package/lib/fast-forward.js +1 -12
- package/lib/system.js +0 -102
- package/lib/topo.js +5 -1
- package/lib/updates.js +1 -38
- package/lib/writers.js +63 -6
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -235,6 +235,8 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
235
235
|
await this._draining
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
if (this._updating) await this._updating
|
|
239
|
+
|
|
238
240
|
// let in-flight writer adds finish
|
|
239
241
|
try {
|
|
240
242
|
await this._bootingAll
|
|
@@ -263,25 +265,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
263
265
|
if (this.fastForwarding) this.fastForwarding.clearRequests(closingError)
|
|
264
266
|
}
|
|
265
267
|
|
|
266
|
-
async changesFrom({ system } = {}) {
|
|
267
|
-
const info = await this.system.getInfo()
|
|
268
|
-
|
|
269
|
-
const current = {
|
|
270
|
-
flushes: info ? info.flushes : 0,
|
|
271
|
-
system: this.system.bee.head(),
|
|
272
|
-
view: info ? info.view : EMPTY_HEAD
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// no common ancestor (null) -> flushes -1 never matches, forcing a full diff
|
|
276
|
-
const shared = (await this.system.commonAncestor(system || EMPTY_HEAD)) || {
|
|
277
|
-
flushes: -1,
|
|
278
|
-
system: EMPTY_HEAD,
|
|
279
|
-
view: EMPTY_HEAD
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return UpdateChanges.from(shared, current)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
268
|
replicate(...args) {
|
|
286
269
|
const stream = this.store.replicate(...args)
|
|
287
270
|
this._wakeup.addStream(stream)
|
|
@@ -529,9 +512,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
529
512
|
else if (head) await this._bootFromHead(head, bootCondition)
|
|
530
513
|
}
|
|
531
514
|
|
|
532
|
-
// tracked across drain iteration
|
|
533
|
-
this.system.shared = null
|
|
534
|
-
|
|
535
515
|
this._ackRequired = false
|
|
536
516
|
|
|
537
517
|
const changes = this._hasUpdate ? new UpdateChanges(this) : null
|
|
@@ -566,6 +546,8 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
566
546
|
}
|
|
567
547
|
|
|
568
548
|
await this._flushLocal()
|
|
549
|
+
|
|
550
|
+
if (!this._interrupting) await this.writers.refresh()
|
|
569
551
|
} finally {
|
|
570
552
|
if (this.bumping === 1) this.bumping = 0
|
|
571
553
|
else this.bumping = 1
|
|
@@ -575,16 +557,15 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
575
557
|
this._draining = null
|
|
576
558
|
if (this._interrupting) return
|
|
577
559
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
this._updating = updating.promise
|
|
560
|
+
const updating = rrp()
|
|
561
|
+
this._updating = updating.promise
|
|
581
562
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
563
|
+
try {
|
|
564
|
+
if (this._needsUpdate) await this._update(changes)
|
|
565
|
+
await this._storeBoot()
|
|
566
|
+
} finally {
|
|
567
|
+
this._updating = null
|
|
568
|
+
updating.resolve()
|
|
588
569
|
}
|
|
589
570
|
}
|
|
590
571
|
|
|
@@ -617,18 +598,28 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
617
598
|
}
|
|
618
599
|
}
|
|
619
600
|
|
|
620
|
-
|
|
601
|
+
// wakeup hints keep arriving asynchronously
|
|
602
|
+
async _applyWakeupHints() {
|
|
621
603
|
const hints = this._wakeup.flush()
|
|
622
|
-
if (!hints.size) return
|
|
604
|
+
if (!hints.size) return hints
|
|
623
605
|
|
|
624
606
|
this.previousDrain = Date.now()
|
|
625
607
|
|
|
626
608
|
for (const [hex, length] of hints) {
|
|
627
609
|
const key = b4a.from(hex, 'hex')
|
|
628
|
-
|
|
610
|
+
// wakeup() itself no-ops the add for an already-active writer, but still
|
|
611
|
+
// needs to run so it can hint() the announced length - skipping it here
|
|
612
|
+
// for active writers left gc with no hint to protect them
|
|
629
613
|
await this.writers.wakeup(key, length)
|
|
630
614
|
}
|
|
631
615
|
|
|
616
|
+
return hints
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
async _flushWakeup() {
|
|
620
|
+
const hints = await this._applyWakeupHints()
|
|
621
|
+
if (!hints.size) return
|
|
622
|
+
|
|
632
623
|
// a scheduled fast-forward is applied by the drain before we look again
|
|
633
624
|
if (this.fastForwardTo !== null || this.fastForwarding !== null) return
|
|
634
625
|
if (this._interrupting || this.closing || this.bootFrom) return
|
|
@@ -918,41 +909,33 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
918
909
|
this._catchupMigratedNodes = null
|
|
919
910
|
}
|
|
920
911
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
912
|
+
// apply the best next node to keep the prefix stable
|
|
913
|
+
const next = await this.writers.nextPendingNode()
|
|
914
|
+
if (next === null) return false
|
|
924
915
|
|
|
925
|
-
|
|
926
|
-
const w = pending[i]
|
|
916
|
+
const { writer: w, batch } = next
|
|
927
917
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
w.notify(batch)
|
|
934
|
-
updated = true
|
|
935
|
-
continue
|
|
936
|
-
}
|
|
918
|
+
if (w.isAdded || (w.isRemoved && w.hasReferrals())) {
|
|
919
|
+
await this._processBatch(batch)
|
|
920
|
+
w.notify(batch)
|
|
921
|
+
return true
|
|
922
|
+
}
|
|
937
923
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
}
|
|
943
|
-
w.notify(batch)
|
|
944
|
-
updated = true
|
|
945
|
-
continue
|
|
924
|
+
if (this.optimistic && !w.isRemoved && batch[0].optimistic) {
|
|
925
|
+
if (!(await this._optimisticBatch(batch))) {
|
|
926
|
+
w.removePending()
|
|
927
|
+
return true
|
|
946
928
|
}
|
|
929
|
+
w.notify(batch)
|
|
930
|
+
return true
|
|
947
931
|
}
|
|
948
932
|
|
|
949
|
-
return
|
|
933
|
+
return true
|
|
950
934
|
}
|
|
951
935
|
|
|
952
936
|
async _optimisticBatch(batch) {
|
|
953
937
|
const rollbackSystem = this.system.bee.head()
|
|
954
938
|
const rollbackView = this._workingBee.head()
|
|
955
|
-
const rollbackShared = this.system.shared
|
|
956
939
|
const rollbackAttestations = this.writers.attestations.length
|
|
957
940
|
|
|
958
941
|
const t = await this.prepareBatch(batch)
|
|
@@ -975,7 +958,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
975
958
|
this._workingBee.move(rollbackView)
|
|
976
959
|
this.system.bee.move(rollbackSystem)
|
|
977
960
|
await this.system.reset()
|
|
978
|
-
this.system.shared = rollbackShared
|
|
979
961
|
// don't attest grants that were just undone
|
|
980
962
|
this.writers.attestations.length = rollbackAttestations
|
|
981
963
|
return false
|
|
@@ -1064,8 +1046,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1064
1046
|
|
|
1065
1047
|
this.writers.attest(witnessed)
|
|
1066
1048
|
|
|
1067
|
-
await this._storeBoot()
|
|
1068
|
-
|
|
1069
1049
|
for (const { key, added, isAnchor } of changed) {
|
|
1070
1050
|
if (isAnchor) {
|
|
1071
1051
|
// no Writer/ActiveWriters tracking, but still wake anything linked to it
|
|
@@ -1327,12 +1307,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1327
1307
|
this.system.bee.move(head)
|
|
1328
1308
|
await this.system.reset()
|
|
1329
1309
|
|
|
1330
|
-
this.system.shared = this.fastForwardTo.shared || {
|
|
1331
|
-
flushes: -1,
|
|
1332
|
-
view: EMPTY_HEAD,
|
|
1333
|
-
system: EMPTY_HEAD
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
1310
|
// migrate is set when fast-forwarding from a legacy head
|
|
1337
1311
|
if (migrate) {
|
|
1338
1312
|
const view = (await this._handlers.migrate(migrate)) || EMPTY_HEAD
|
|
@@ -1345,14 +1319,16 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1345
1319
|
}
|
|
1346
1320
|
|
|
1347
1321
|
this.fastForwardTo = null
|
|
1322
|
+
|
|
1323
|
+
// process any wakeup while fast-forward itself was in flight
|
|
1324
|
+
await this._applyWakeupHints()
|
|
1348
1325
|
await this.writers.refresh()
|
|
1349
1326
|
|
|
1350
1327
|
// we moved, so ask our peers to tell us their heads again
|
|
1351
1328
|
this._requestWakeup()
|
|
1352
1329
|
|
|
1353
|
-
await this._storeBoot()
|
|
1354
|
-
|
|
1355
1330
|
await this._update(changes)
|
|
1331
|
+
await this._storeBoot()
|
|
1356
1332
|
|
|
1357
1333
|
this.stats.fastForwards++
|
|
1358
1334
|
this.emit('move-to', to, from)
|
package/lib/fast-forward.js
CHANGED
|
@@ -261,23 +261,12 @@ module.exports = class FastForward {
|
|
|
261
261
|
await Promise.all(promises)
|
|
262
262
|
if (this.destroyed) return null
|
|
263
263
|
|
|
264
|
-
const shared = await this._commonAncestor()
|
|
265
264
|
if (this.destroyed) return null
|
|
266
265
|
|
|
267
266
|
return {
|
|
268
267
|
head: this.head,
|
|
269
268
|
tip: this.tip,
|
|
270
|
-
migrate: null
|
|
271
|
-
shared
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
async _commonAncestor() {
|
|
276
|
-
try {
|
|
277
|
-
return await this.auto.system.commonAncestor(this.head)
|
|
278
|
-
} catch (err) {
|
|
279
|
-
safetyCatch(err)
|
|
280
|
-
return null
|
|
269
|
+
migrate: null
|
|
281
270
|
}
|
|
282
271
|
}
|
|
283
272
|
|
package/lib/system.js
CHANGED
|
@@ -46,7 +46,6 @@ module.exports = class Systembee {
|
|
|
46
46
|
this.indexers = null // legacy
|
|
47
47
|
this.updates = new Map()
|
|
48
48
|
this.writers = new Map()
|
|
49
|
-
this.shared = null
|
|
50
49
|
this.encrypted = opts.encrypted === true
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -115,60 +114,6 @@ module.exports = class Systembee {
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
async commonAncestor(head) {
|
|
119
|
-
const snap = this.bee.checkout(head)
|
|
120
|
-
|
|
121
|
-
const ca = infoIterator(this.bee)
|
|
122
|
-
const cb = infoIterator(snap)
|
|
123
|
-
|
|
124
|
-
let l = await next(ca)
|
|
125
|
-
let r = await next(cb)
|
|
126
|
-
|
|
127
|
-
while (l && r) {
|
|
128
|
-
if (l.legacy && r.legacy) {
|
|
129
|
-
// legacy nodes just compare head
|
|
130
|
-
if (sameHead(l.node.head, r.node.head)) {
|
|
131
|
-
return {
|
|
132
|
-
flushes: l.info.flushes,
|
|
133
|
-
system: l.node.head,
|
|
134
|
-
view: l.info.view || EMPTY_HEAD
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// different legacy cores never converge - genuinely unrelated history
|
|
139
|
-
if (!b4a.equals(l.node.head.key, r.node.head.key)) return null
|
|
140
|
-
|
|
141
|
-
if (l.node.head.length >= r.node.head.length) l = await next(ca)
|
|
142
|
-
else r = await next(cb)
|
|
143
|
-
continue
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (l.legacy !== r.legacy) {
|
|
147
|
-
// current-format history sits above the legacy band: descend it
|
|
148
|
-
if (!l.legacy) l = await next(ca)
|
|
149
|
-
else r = await next(cb)
|
|
150
|
-
continue
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (l.info.flushes === r.info.flushes && sameHead(l.info.view, r.info.view)) {
|
|
154
|
-
return {
|
|
155
|
-
flushes: l.info.flushes,
|
|
156
|
-
system: l.node.head,
|
|
157
|
-
view: l.info.view || EMPTY_HEAD
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const lf = l.info.flushes
|
|
162
|
-
const rf = r.info.flushes
|
|
163
|
-
|
|
164
|
-
if (lf >= rf) l = await next(ca)
|
|
165
|
-
if (rf >= lf) r = await next(cb)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// no common ancestor
|
|
169
|
-
return null
|
|
170
|
-
}
|
|
171
|
-
|
|
172
117
|
async reset({ timeout } = {}) {
|
|
173
118
|
const info = await this.getInfo({ timeout })
|
|
174
119
|
|
|
@@ -178,7 +123,6 @@ module.exports = class Systembee {
|
|
|
178
123
|
this.timestamp = info ? info.timestamp : 0
|
|
179
124
|
this.flushes = info ? info.flushes : 0
|
|
180
125
|
this.indexers = info ? info.indexers : null
|
|
181
|
-
this.shared = null
|
|
182
126
|
this.updates.clear()
|
|
183
127
|
this.writers.clear()
|
|
184
128
|
|
|
@@ -200,16 +144,8 @@ module.exports = class Systembee {
|
|
|
200
144
|
async undo(head) {
|
|
201
145
|
this.bee.move(head)
|
|
202
146
|
|
|
203
|
-
const shared = this.shared
|
|
204
147
|
await this.reset()
|
|
205
148
|
|
|
206
|
-
// deepest rewind since last mark - everything at or below it is untouched
|
|
207
|
-
if (shared === null || this.flushes < shared.flushes) {
|
|
208
|
-
this.shared = { flushes: this.flushes, view: this.view || EMPTY_HEAD, system: head }
|
|
209
|
-
} else if (shared) {
|
|
210
|
-
this.shared = shared
|
|
211
|
-
}
|
|
212
|
-
|
|
213
149
|
return this.view || EMPTY_HEAD
|
|
214
150
|
}
|
|
215
151
|
|
|
@@ -514,41 +450,3 @@ function getInfo(data) {
|
|
|
514
450
|
|
|
515
451
|
return null
|
|
516
452
|
}
|
|
517
|
-
|
|
518
|
-
function sameHead(a, b) {
|
|
519
|
-
if (!a || !b || a.length !== b.length) return false
|
|
520
|
-
if (!a.key || !b.key) return a.key === b.key
|
|
521
|
-
return b4a.equals(a.key, b.key)
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
async function* infoIterator(bee) {
|
|
525
|
-
try {
|
|
526
|
-
for await (const node of bee.createChangesStream()) {
|
|
527
|
-
const legacy = topo.isLegacyNode(node)
|
|
528
|
-
const info = legacy ? getLegacyInfo(node) : getInfo(node)
|
|
529
|
-
if (info) yield { node, info, legacy }
|
|
530
|
-
}
|
|
531
|
-
} catch {
|
|
532
|
-
// fall through
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
async function next(it) {
|
|
537
|
-
const res = await it.next()
|
|
538
|
-
return res.done ? null : res.value
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function getLegacyInfo(data) {
|
|
542
|
-
for (const { keys } of data.batch) {
|
|
543
|
-
for (const k of keys) {
|
|
544
|
-
if (b4a.equals(k.key, INFO_LEGACY_KEY)) {
|
|
545
|
-
try {
|
|
546
|
-
return encoding.decodeSystemInfo(k.value)
|
|
547
|
-
} catch {
|
|
548
|
-
return null
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
return null
|
|
554
|
-
}
|
package/lib/topo.js
CHANGED
|
@@ -6,6 +6,8 @@ const asserts = require('./asserts.js')
|
|
|
6
6
|
const EMPTY_HEAD = { length: 0, key: null }
|
|
7
7
|
const INFO_LEGACY_KEY = b4a.concat([b4a.from([0, 0]), b4a.from('info')])
|
|
8
8
|
|
|
9
|
+
const DEFAULT_OP_TIMEOUT = 5000
|
|
10
|
+
|
|
9
11
|
exports.sort = sort
|
|
10
12
|
exports.rollback = rollback
|
|
11
13
|
exports.replay = replay
|
|
@@ -229,6 +231,8 @@ function cmp(a, b) {
|
|
|
229
231
|
}
|
|
230
232
|
|
|
231
233
|
async function inflate(auto, reorder, opts = null) {
|
|
234
|
+
const readOpts = opts === null ? { timeout: DEFAULT_OP_TIMEOUT } : opts
|
|
235
|
+
|
|
232
236
|
const promises = []
|
|
233
237
|
const sessions = new Map()
|
|
234
238
|
|
|
@@ -257,7 +261,7 @@ async function inflate(auto, reorder, opts = null) {
|
|
|
257
261
|
sessions.set(id, core)
|
|
258
262
|
}
|
|
259
263
|
|
|
260
|
-
promises.push(getOplogBatch(ref.undo, core, ref.oplog.length, weight, i,
|
|
264
|
+
promises.push(getOplogBatch(ref.undo, core, ref.oplog.length, weight, i, readOpts))
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
try {
|
package/lib/updates.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const b4a = require('b4a')
|
|
2
|
-
|
|
3
1
|
module.exports = class UpdateChanges {
|
|
4
2
|
constructor(auto) {
|
|
5
3
|
this.auto = auto
|
|
@@ -21,57 +19,22 @@ module.exports = class UpdateChanges {
|
|
|
21
19
|
flushes: this.auto.system.flushes,
|
|
22
20
|
from: this.tracking[k].head(),
|
|
23
21
|
to: null,
|
|
24
|
-
|
|
22
|
+
incremental: false
|
|
25
23
|
})
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
finalise() {
|
|
30
|
-
const floor = this.auto.system.shared
|
|
31
|
-
|
|
32
28
|
// flush count at `to` - safe to read live, state is frozen for the hook
|
|
33
29
|
const flushes = this.auto.system.flushes
|
|
34
30
|
|
|
35
31
|
for (const k in this.tracking) {
|
|
36
32
|
const tracked = this.byName.get(k)
|
|
37
|
-
const startFlushes = tracked.flushes
|
|
38
33
|
|
|
39
34
|
tracked.to = this.tracking[k].head()
|
|
40
35
|
tracked.flushes = flushes
|
|
41
|
-
|
|
42
|
-
if (floor && floor.flushes < startFlushes) {
|
|
43
|
-
tracked.shared = k === 'view' ? floor.view : floor.system
|
|
44
|
-
} else {
|
|
45
|
-
tracked.shared = tracked.from
|
|
46
|
-
}
|
|
47
36
|
}
|
|
48
37
|
|
|
49
38
|
return this.byName
|
|
50
39
|
}
|
|
51
|
-
|
|
52
|
-
static from(shared, current) {
|
|
53
|
-
if (
|
|
54
|
-
shared.flushes === current.flushes &&
|
|
55
|
-
shared.system.length === current.system.length &&
|
|
56
|
-
(shared.system.key === current.system.key ||
|
|
57
|
-
(shared.system.key &&
|
|
58
|
-
current.system.key &&
|
|
59
|
-
b4a.equals(shared.system.key, current.system.key)))
|
|
60
|
-
) {
|
|
61
|
-
return null
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const byName = new Map()
|
|
65
|
-
const flushes = current.flushes
|
|
66
|
-
|
|
67
|
-
byName.set('view', { from: shared.view, shared: shared.view, to: current.view, flushes })
|
|
68
|
-
byName.set('system', {
|
|
69
|
-
from: shared.system,
|
|
70
|
-
shared: shared.system,
|
|
71
|
-
to: current.system,
|
|
72
|
-
flushes
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
return byName
|
|
76
|
-
}
|
|
77
40
|
}
|
package/lib/writers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const encoding = require('./encoding.js')
|
|
3
3
|
const Triggers = require('./triggers.js')
|
|
4
|
-
const { signAttestation } = require('./witness.js')
|
|
4
|
+
const { signAttestation, resolveWeight } = require('./witness.js')
|
|
5
5
|
const { WriterEncryption } = require('autobee-encryption')
|
|
6
6
|
|
|
7
7
|
const WRITER_PREFETCH = 10
|
|
@@ -25,6 +25,8 @@ class ActiveWriters {
|
|
|
25
25
|
this.witnesses = []
|
|
26
26
|
this.attestations = []
|
|
27
27
|
|
|
28
|
+
this._pendingBuffer = new Map()
|
|
29
|
+
|
|
28
30
|
this.localWriter.attach()
|
|
29
31
|
|
|
30
32
|
this._bootstrap()
|
|
@@ -62,13 +64,15 @@ class ActiveWriters {
|
|
|
62
64
|
const info = await this.auto.system.get(key, { unflushed: true })
|
|
63
65
|
if (info && info.isAnchor) return
|
|
64
66
|
w = await this.add(key)
|
|
67
|
+
if (!w) return
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
// remember the announced length so gc doesn't drop a writer we haven't caught up on
|
|
71
|
+
w.hint(length)
|
|
72
|
+
|
|
67
73
|
// prefetch the oplog head for the fast-forward that runs right after us
|
|
68
74
|
if (w && length > 0) w.core.get(length - 1).catch(noop)
|
|
69
75
|
|
|
70
|
-
// TODO: signal that we've seen length on w for gc
|
|
71
|
-
|
|
72
76
|
this.bump()
|
|
73
77
|
}
|
|
74
78
|
|
|
@@ -113,6 +117,9 @@ class ActiveWriters {
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
async refresh() {
|
|
120
|
+
// peeked batches were read against the pre-reboot system, drop them
|
|
121
|
+
this._pendingBuffer.clear()
|
|
122
|
+
|
|
116
123
|
if (this.localWriter && !this.localWriter.isClosed) {
|
|
117
124
|
await this.updateLocalState()
|
|
118
125
|
}
|
|
@@ -127,6 +134,33 @@ class ActiveWriters {
|
|
|
127
134
|
await this.localWriter.update()
|
|
128
135
|
}
|
|
129
136
|
|
|
137
|
+
async nextPendingNode() {
|
|
138
|
+
for (const w of this.pending.slice()) {
|
|
139
|
+
if (this._pendingBuffer.has(w) || w.isFrozen) continue
|
|
140
|
+
|
|
141
|
+
const batch = await w.next()
|
|
142
|
+
if (batch !== null) this._pendingBuffer.set(w, batch)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (this._pendingBuffer.size === 0) return null
|
|
146
|
+
|
|
147
|
+
let writer = null
|
|
148
|
+
let best = null
|
|
149
|
+
|
|
150
|
+
for (const [w, batch] of this._pendingBuffer) {
|
|
151
|
+
const head = batch[0]
|
|
152
|
+
// -1 marks a node next() has not resolved a sort weight for yet
|
|
153
|
+
if (head.weight < 0) head.weight = await resolveWeight(this.auto, head)
|
|
154
|
+
if (best === null || sortsBefore(head, best[0])) {
|
|
155
|
+
writer = w
|
|
156
|
+
best = batch
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this._pendingBuffer.delete(writer)
|
|
161
|
+
return { writer, batch: best }
|
|
162
|
+
}
|
|
163
|
+
|
|
130
164
|
_updateLocalState() {
|
|
131
165
|
const w = this.localWriter
|
|
132
166
|
|
|
@@ -215,6 +249,7 @@ class ActiveWriters {
|
|
|
215
249
|
const id = b4a.toString(bootstrap.key, 'hex')
|
|
216
250
|
let w = this.active.get(id)
|
|
217
251
|
if (w) {
|
|
252
|
+
w.isBootstrap = true
|
|
218
253
|
w.couple() // couple if we weren't ready first time
|
|
219
254
|
return
|
|
220
255
|
}
|
|
@@ -223,6 +258,7 @@ class ActiveWriters {
|
|
|
223
258
|
session.setEncryption(this.auto._getEncryptionProvider())
|
|
224
259
|
|
|
225
260
|
w = new Writer(this, this.auto.system, false, session, id)
|
|
261
|
+
w.isBootstrap = true // the root of the autobase, never gc'd
|
|
226
262
|
w.attach()
|
|
227
263
|
w.couple()
|
|
228
264
|
}
|
|
@@ -253,6 +289,7 @@ class Writer {
|
|
|
253
289
|
this.appendLength = this.core.length + 1
|
|
254
290
|
this.waiting = null
|
|
255
291
|
this.download = null
|
|
292
|
+
this.hintedLength = 0
|
|
256
293
|
|
|
257
294
|
this.isPending = false
|
|
258
295
|
this.isClosed = false
|
|
@@ -261,6 +298,7 @@ class Writer {
|
|
|
261
298
|
this.isRemoved = false
|
|
262
299
|
this.isCoupled = false
|
|
263
300
|
this.isFrozen = false
|
|
301
|
+
this.isBootstrap = false
|
|
264
302
|
|
|
265
303
|
this._onchangeBound = this._onchange.bind(this)
|
|
266
304
|
|
|
@@ -308,6 +346,8 @@ class Writer {
|
|
|
308
346
|
if (!this.isPending) return
|
|
309
347
|
this.isPending = false
|
|
310
348
|
|
|
349
|
+
this.writers._pendingBuffer.delete(this)
|
|
350
|
+
|
|
311
351
|
const head = this.writers.pending.pop()
|
|
312
352
|
if (head !== this) {
|
|
313
353
|
this.writers.pending[this.index] = head
|
|
@@ -315,6 +355,11 @@ class Writer {
|
|
|
315
355
|
}
|
|
316
356
|
}
|
|
317
357
|
|
|
358
|
+
// a peer announced this length for us, so we're not caught up until we've seen it
|
|
359
|
+
hint(length) {
|
|
360
|
+
if (length > this.hintedLength) this.hintedLength = length
|
|
361
|
+
}
|
|
362
|
+
|
|
318
363
|
_onchange() {
|
|
319
364
|
if (this.waiting === null) this.bump()
|
|
320
365
|
}
|
|
@@ -352,10 +397,10 @@ class Writer {
|
|
|
352
397
|
|
|
353
398
|
const length = await this.update()
|
|
354
399
|
|
|
355
|
-
// don't drop any local nodes left in buffer
|
|
356
|
-
if (length < this.core.length || this.appending) {
|
|
400
|
+
// don't drop any local nodes left in buffer, or work a peer told us about
|
|
401
|
+
if (length < this.core.length || length < this.hintedLength || this.appending) {
|
|
357
402
|
this.addPending()
|
|
358
|
-
} else if (this !== this.writers.localWriter && !this.isIndexer) {
|
|
403
|
+
} else if (this !== this.writers.localWriter && !this.isIndexer && !this.isBootstrap) {
|
|
359
404
|
await this.detachAndClose()
|
|
360
405
|
}
|
|
361
406
|
}
|
|
@@ -375,6 +420,7 @@ class Writer {
|
|
|
375
420
|
|
|
376
421
|
detachAndClose() {
|
|
377
422
|
this.detach()
|
|
423
|
+
this.decouple()
|
|
378
424
|
return this.close()
|
|
379
425
|
}
|
|
380
426
|
|
|
@@ -690,6 +736,17 @@ class Writer {
|
|
|
690
736
|
}
|
|
691
737
|
}
|
|
692
738
|
|
|
739
|
+
// mirrors topo.cmp - kept local because topo.js requires this module
|
|
740
|
+
function sortsBefore(a, b) {
|
|
741
|
+
if (a.weight !== b.weight) return a.weight > b.weight
|
|
742
|
+
if (a.timestamp !== b.timestamp) return a.timestamp < b.timestamp
|
|
743
|
+
|
|
744
|
+
const c = b4a.compare(a.key, b.key)
|
|
745
|
+
if (c !== 0) return c < 0
|
|
746
|
+
|
|
747
|
+
return a.length < b.length
|
|
748
|
+
}
|
|
749
|
+
|
|
693
750
|
exports.ActiveWriters = ActiveWriters
|
|
694
751
|
exports.Writer = Writer
|
|
695
752
|
exports.createNode = createNode
|