autobee 2.0.0-rc.16 → 2.0.0-rc.18
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 +29 -46
- package/lib/fast-forward.js +1 -12
- package/lib/system.js +0 -102
- package/lib/topo.js +11 -4
- package/lib/updates.js +1 -38
- package/lib/writers.js +25 -9
- 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
|
|
@@ -952,7 +943,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
952
943
|
async _optimisticBatch(batch) {
|
|
953
944
|
const rollbackSystem = this.system.bee.head()
|
|
954
945
|
const rollbackView = this._workingBee.head()
|
|
955
|
-
const rollbackShared = this.system.shared
|
|
956
946
|
const rollbackAttestations = this.writers.attestations.length
|
|
957
947
|
|
|
958
948
|
const t = await this.prepareBatch(batch)
|
|
@@ -975,7 +965,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
975
965
|
this._workingBee.move(rollbackView)
|
|
976
966
|
this.system.bee.move(rollbackSystem)
|
|
977
967
|
await this.system.reset()
|
|
978
|
-
this.system.shared = rollbackShared
|
|
979
968
|
// don't attest grants that were just undone
|
|
980
969
|
this.writers.attestations.length = rollbackAttestations
|
|
981
970
|
return false
|
|
@@ -1064,8 +1053,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1064
1053
|
|
|
1065
1054
|
this.writers.attest(witnessed)
|
|
1066
1055
|
|
|
1067
|
-
await this._storeBoot()
|
|
1068
|
-
|
|
1069
1056
|
for (const { key, added, isAnchor } of changed) {
|
|
1070
1057
|
if (isAnchor) {
|
|
1071
1058
|
// no Writer/ActiveWriters tracking, but still wake anything linked to it
|
|
@@ -1327,12 +1314,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1327
1314
|
this.system.bee.move(head)
|
|
1328
1315
|
await this.system.reset()
|
|
1329
1316
|
|
|
1330
|
-
this.system.shared = this.fastForwardTo.shared || {
|
|
1331
|
-
flushes: -1,
|
|
1332
|
-
view: EMPTY_HEAD,
|
|
1333
|
-
system: EMPTY_HEAD
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
1317
|
// migrate is set when fast-forwarding from a legacy head
|
|
1337
1318
|
if (migrate) {
|
|
1338
1319
|
const view = (await this._handlers.migrate(migrate)) || EMPTY_HEAD
|
|
@@ -1345,14 +1326,16 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1345
1326
|
}
|
|
1346
1327
|
|
|
1347
1328
|
this.fastForwardTo = null
|
|
1329
|
+
|
|
1330
|
+
// process any wakeup while fast-forward itself was in flight
|
|
1331
|
+
await this._applyWakeupHints()
|
|
1348
1332
|
await this.writers.refresh()
|
|
1349
1333
|
|
|
1350
1334
|
// we moved, so ask our peers to tell us their heads again
|
|
1351
1335
|
this._requestWakeup()
|
|
1352
1336
|
|
|
1353
|
-
await this._storeBoot()
|
|
1354
|
-
|
|
1355
1337
|
await this._update(changes)
|
|
1338
|
+
await this._storeBoot()
|
|
1356
1339
|
|
|
1357
1340
|
this.stats.fastForwards++
|
|
1358
1341
|
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 {
|
|
@@ -308,7 +312,9 @@ function getLocalBatch(undo, writer, length, weight, index) {
|
|
|
308
312
|
|
|
309
313
|
if (length > writer.core.length) {
|
|
310
314
|
if (!writer.pending) {
|
|
311
|
-
throw new Error(
|
|
315
|
+
throw new Error(
|
|
316
|
+
`No local nodes: looking for ${length}, found ${writer.core.length}@${writer.core.id}`
|
|
317
|
+
)
|
|
312
318
|
}
|
|
313
319
|
|
|
314
320
|
let i = 0
|
|
@@ -318,9 +324,10 @@ function getLocalBatch(undo, writer, length, weight, index) {
|
|
|
318
324
|
}
|
|
319
325
|
|
|
320
326
|
const head = writer.pending[i]
|
|
327
|
+
const headBatch = head.batch || { start: 0, end: 0 }
|
|
321
328
|
|
|
322
|
-
const start = i -
|
|
323
|
-
const end = i +
|
|
329
|
+
const start = i - headBatch.start
|
|
330
|
+
const end = i + headBatch.end + 1 // include head
|
|
324
331
|
|
|
325
332
|
const batch = writer.pending.slice(start, end)
|
|
326
333
|
return { undo, batch, index }
|
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
|
@@ -62,13 +62,15 @@ class ActiveWriters {
|
|
|
62
62
|
const info = await this.auto.system.get(key, { unflushed: true })
|
|
63
63
|
if (info && info.isAnchor) return
|
|
64
64
|
w = await this.add(key)
|
|
65
|
+
if (!w) return
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
// remember the announced length so gc doesn't drop a writer we haven't caught up on
|
|
69
|
+
w.hint(length)
|
|
70
|
+
|
|
67
71
|
// prefetch the oplog head for the fast-forward that runs right after us
|
|
68
72
|
if (w && length > 0) w.core.get(length - 1).catch(noop)
|
|
69
73
|
|
|
70
|
-
// TODO: signal that we've seen length on w for gc
|
|
71
|
-
|
|
72
74
|
this.bump()
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -215,6 +217,7 @@ class ActiveWriters {
|
|
|
215
217
|
const id = b4a.toString(bootstrap.key, 'hex')
|
|
216
218
|
let w = this.active.get(id)
|
|
217
219
|
if (w) {
|
|
220
|
+
w.isBootstrap = true
|
|
218
221
|
w.couple() // couple if we weren't ready first time
|
|
219
222
|
return
|
|
220
223
|
}
|
|
@@ -223,6 +226,7 @@ class ActiveWriters {
|
|
|
223
226
|
session.setEncryption(this.auto._getEncryptionProvider())
|
|
224
227
|
|
|
225
228
|
w = new Writer(this, this.auto.system, false, session, id)
|
|
229
|
+
w.isBootstrap = true // the root of the autobase, never gc'd
|
|
226
230
|
w.attach()
|
|
227
231
|
w.couple()
|
|
228
232
|
}
|
|
@@ -253,6 +257,7 @@ class Writer {
|
|
|
253
257
|
this.appendLength = this.core.length + 1
|
|
254
258
|
this.waiting = null
|
|
255
259
|
this.download = null
|
|
260
|
+
this.hintedLength = 0
|
|
256
261
|
|
|
257
262
|
this.isPending = false
|
|
258
263
|
this.isClosed = false
|
|
@@ -261,6 +266,7 @@ class Writer {
|
|
|
261
266
|
this.isRemoved = false
|
|
262
267
|
this.isCoupled = false
|
|
263
268
|
this.isFrozen = false
|
|
269
|
+
this.isBootstrap = false
|
|
264
270
|
|
|
265
271
|
this._onchangeBound = this._onchange.bind(this)
|
|
266
272
|
|
|
@@ -315,6 +321,11 @@ class Writer {
|
|
|
315
321
|
}
|
|
316
322
|
}
|
|
317
323
|
|
|
324
|
+
// a peer announced this length for us, so we're not caught up until we've seen it
|
|
325
|
+
hint(length) {
|
|
326
|
+
if (length > this.hintedLength) this.hintedLength = length
|
|
327
|
+
}
|
|
328
|
+
|
|
318
329
|
_onchange() {
|
|
319
330
|
if (this.waiting === null) this.bump()
|
|
320
331
|
}
|
|
@@ -352,10 +363,10 @@ class Writer {
|
|
|
352
363
|
|
|
353
364
|
const length = await this.update()
|
|
354
365
|
|
|
355
|
-
// don't drop any local nodes left in buffer
|
|
356
|
-
if (length < this.core.length || this.appending) {
|
|
366
|
+
// don't drop any local nodes left in buffer, or work a peer told us about
|
|
367
|
+
if (length < this.core.length || length < this.hintedLength || this.appending) {
|
|
357
368
|
this.addPending()
|
|
358
|
-
} else if (this !== this.writers.localWriter && !this.isIndexer) {
|
|
369
|
+
} else if (this !== this.writers.localWriter && !this.isIndexer && !this.isBootstrap) {
|
|
359
370
|
await this.detachAndClose()
|
|
360
371
|
}
|
|
361
372
|
}
|
|
@@ -375,6 +386,7 @@ class Writer {
|
|
|
375
386
|
|
|
376
387
|
detachAndClose() {
|
|
377
388
|
this.detach()
|
|
389
|
+
this.decouple()
|
|
378
390
|
return this.close()
|
|
379
391
|
}
|
|
380
392
|
|
|
@@ -652,8 +664,8 @@ class Writer {
|
|
|
652
664
|
this.processed = 0
|
|
653
665
|
}
|
|
654
666
|
|
|
655
|
-
flush(views, queued, trusted) {
|
|
656
|
-
if (this.pending === null || this.processed === 0) return
|
|
667
|
+
async flush(views, queued, trusted) {
|
|
668
|
+
if (this.pending === null || this.processed === 0) return
|
|
657
669
|
|
|
658
670
|
const head = this.pending[this.processed - 1]
|
|
659
671
|
head.views = views
|
|
@@ -681,8 +693,12 @@ class Writer {
|
|
|
681
693
|
for (let i = 0; i < this.processed; i++) {
|
|
682
694
|
buffers.push(encoding.encodeOplog(this.pending[i]))
|
|
683
695
|
}
|
|
684
|
-
|
|
685
|
-
|
|
696
|
+
|
|
697
|
+
try {
|
|
698
|
+
await this.core.append(buffers)
|
|
699
|
+
} finally {
|
|
700
|
+
this.clear()
|
|
701
|
+
}
|
|
686
702
|
}
|
|
687
703
|
}
|
|
688
704
|
|