autobee 2.0.0-rc.26 → 2.0.0-rc.28
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 +4 -30
- package/lib/boot.js +19 -1
- package/lib/fast-forward.js +3 -14
- package/lib/migrations.js +37 -14
- package/lib/system.js +2 -2
- package/lib/topo.js +17 -59
- package/lib/writers.js +16 -5
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -177,6 +177,7 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
177
177
|
this._bootingAll.catch(safetyCatch)
|
|
178
178
|
|
|
179
179
|
await this.bee.ready()
|
|
180
|
+
await this._workingBee.ready()
|
|
180
181
|
await this._bootingState
|
|
181
182
|
|
|
182
183
|
this._localSystemStart = this.system.bee.context.local.length
|
|
@@ -1042,12 +1043,7 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1042
1043
|
|
|
1043
1044
|
this.writers.attest(witnessed)
|
|
1044
1045
|
|
|
1045
|
-
for (const { key, added
|
|
1046
|
-
if (isAnchor) {
|
|
1047
|
-
// no Writer/ActiveWriters tracking, but still wake anything linked to it
|
|
1048
|
-
this.writers.triggers.trigger(b4a.toString(key, 'hex'), 1)
|
|
1049
|
-
continue
|
|
1050
|
-
}
|
|
1046
|
+
for (const { key, added } of changed) {
|
|
1051
1047
|
if (added) await this.writers.add(key)
|
|
1052
1048
|
else await this.writers.remove(key)
|
|
1053
1049
|
}
|
|
@@ -1186,7 +1182,7 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1186
1182
|
// legacy: ungated boot straight onto a system head
|
|
1187
1183
|
async _bootFromSystem(system) {
|
|
1188
1184
|
try {
|
|
1189
|
-
const ff = new FastForward(this, system,
|
|
1185
|
+
const ff = new FastForward(this, system, { timeout: FastForward.DEFAULT_TIMEOUT })
|
|
1190
1186
|
return await this._runFastForward(ff)
|
|
1191
1187
|
} catch (err) {
|
|
1192
1188
|
safetyCatch(err)
|
|
@@ -1326,7 +1322,7 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1326
1322
|
const changes = this._hasUpdate ? new UpdateChanges(this) : null
|
|
1327
1323
|
if (changes) changes.track()
|
|
1328
1324
|
|
|
1329
|
-
const { head,
|
|
1325
|
+
const { head, migrate } = this.fastForwardTo
|
|
1330
1326
|
|
|
1331
1327
|
const from = this.system.bee.head()
|
|
1332
1328
|
const to = head
|
|
@@ -1366,28 +1362,6 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1366
1362
|
this.stats.fastForwards++
|
|
1367
1363
|
this.emit('move-to', to, from)
|
|
1368
1364
|
this.ff.resolve({ to, from })
|
|
1369
|
-
|
|
1370
|
-
// tip is null during boot
|
|
1371
|
-
if (!tip) return
|
|
1372
|
-
|
|
1373
|
-
try {
|
|
1374
|
-
await this._reapply(tip)
|
|
1375
|
-
} catch (err) {
|
|
1376
|
-
throw err
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
async _reapply({ system, verified }) {
|
|
1381
|
-
const changes = this._hasUpdate ? new UpdateChanges(this) : null
|
|
1382
|
-
if (changes) changes.track()
|
|
1383
|
-
|
|
1384
|
-
const sys = this.system.bee.checkout(system)
|
|
1385
|
-
const t = await topo.rollback(this, sys, verified)
|
|
1386
|
-
await sys.close()
|
|
1387
|
-
|
|
1388
|
-
if (t !== null) await this.applyBacklog(t.tip)
|
|
1389
|
-
|
|
1390
|
-
return this._update(changes)
|
|
1391
1365
|
}
|
|
1392
1366
|
|
|
1393
1367
|
replay() {
|
package/lib/boot.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const b4a = require('b4a')
|
|
1
2
|
const encoding = require('./encoding.js')
|
|
2
3
|
const { checkAutobaseMigration } = require('./migrations.js')
|
|
3
4
|
|
|
@@ -94,7 +95,7 @@ module.exports = async function boot(
|
|
|
94
95
|
await result.bootstrap.setUserData('autobee/local', result.local.key)
|
|
95
96
|
await result.local.setUserData('referrer', result.key)
|
|
96
97
|
|
|
97
|
-
result.migration = await
|
|
98
|
+
result.migration = await migrateWithFallback(
|
|
98
99
|
corestore,
|
|
99
100
|
result.local,
|
|
100
101
|
result.bootstrap,
|
|
@@ -138,6 +139,23 @@ module.exports = async function boot(
|
|
|
138
139
|
return result
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
// the bootstrap pointer names the rotated-to writer, whose legacy boot record
|
|
143
|
+
// can be unusable. the namespace local still carries the pre-rotation record,
|
|
144
|
+
// which points at the last system core that was actually signed
|
|
145
|
+
async function migrateWithFallback(corestore, local, bootstrap, legacyViews) {
|
|
146
|
+
try {
|
|
147
|
+
return await checkAutobaseMigration(corestore, local, bootstrap, legacyViews)
|
|
148
|
+
} catch (err) {
|
|
149
|
+
const prev = corestore.get({ name: 'local', active: false })
|
|
150
|
+
await prev.ready()
|
|
151
|
+
|
|
152
|
+
if (b4a.equals(prev.key, local.key)) throw err
|
|
153
|
+
if (!(await prev.getUserData('autobase/boot'))) throw err
|
|
154
|
+
|
|
155
|
+
return checkAutobaseMigration(corestore, prev, bootstrap, legacyViews)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
141
159
|
async function getLocalKey(bootstrap) {
|
|
142
160
|
const [legacy, current] = await Promise.all([
|
|
143
161
|
bootstrap.getUserData('autobase/local'),
|
package/lib/fast-forward.js
CHANGED
|
@@ -14,7 +14,7 @@ const MIN_FF_GAP = 32
|
|
|
14
14
|
const MAX_FLUSH_SEARCH = 64
|
|
15
15
|
|
|
16
16
|
module.exports = class FastForward {
|
|
17
|
-
constructor(auto, head,
|
|
17
|
+
constructor(auto, head, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
|
|
18
18
|
this.auto = auto
|
|
19
19
|
|
|
20
20
|
this.activeRequests = []
|
|
@@ -30,7 +30,6 @@ module.exports = class FastForward {
|
|
|
30
30
|
// length -1 marks an uninitialised head: we know the key but not the length
|
|
31
31
|
this.head = { key: head.key, length: head.length ?? 0 }
|
|
32
32
|
this.timeout = timeout
|
|
33
|
-
this.tip = tip || null
|
|
34
33
|
this.destroyed = false
|
|
35
34
|
this.running = null
|
|
36
35
|
this.failed = false
|
|
@@ -68,15 +67,7 @@ module.exports = class FastForward {
|
|
|
68
67
|
return null
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
system: batchToHead(oplog.op.views.system),
|
|
73
|
-
verified: {
|
|
74
|
-
op: { key: verified.key, length: verified.length },
|
|
75
|
-
flushes: verified.op.views.flushes
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return new FastForward(auto, batchToHead(verified.op.views.system), tip)
|
|
70
|
+
return new FastForward(auto, batchToHead(verified.op.views.system))
|
|
80
71
|
}
|
|
81
72
|
|
|
82
73
|
static async fromHeads(
|
|
@@ -246,7 +237,7 @@ module.exports = class FastForward {
|
|
|
246
237
|
} catch (err) {
|
|
247
238
|
safetyCatch(err)
|
|
248
239
|
this.head = result.head
|
|
249
|
-
return { head: result.head,
|
|
240
|
+
return { head: result.head, migrate: result.migrate }
|
|
250
241
|
}
|
|
251
242
|
|
|
252
243
|
// fall through to the plain fast-forward below
|
|
@@ -278,7 +269,6 @@ module.exports = class FastForward {
|
|
|
278
269
|
|
|
279
270
|
return {
|
|
280
271
|
head: this.head,
|
|
281
|
-
tip: this.tip,
|
|
282
272
|
migrate: null
|
|
283
273
|
}
|
|
284
274
|
}
|
|
@@ -331,7 +321,6 @@ module.exports = class FastForward {
|
|
|
331
321
|
|
|
332
322
|
return {
|
|
333
323
|
head,
|
|
334
|
-
tip: null,
|
|
335
324
|
migrate: views,
|
|
336
325
|
autobee: autobee === null ? null : batchToHead(autobee.op.views.system)
|
|
337
326
|
}
|
package/lib/migrations.js
CHANGED
|
@@ -37,23 +37,36 @@ async function checkAutobaseMigration(store, local, bootstrap, legacyViews) {
|
|
|
37
37
|
const bootRecord = await local.getUserData('autobase/boot')
|
|
38
38
|
if (!bootRecord) return null
|
|
39
39
|
|
|
40
|
-
const { key } = encoding.decodeAutobaseBootRecord(bootRecord)
|
|
40
|
+
const { key, systemLength } = encoding.decodeAutobaseBootRecord(bootRecord)
|
|
41
41
|
const core = store.get({ key: key, encryption: null })
|
|
42
42
|
await core.ready()
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const length = await findLocalSystemLength(core, systemLength)
|
|
45
|
+
assert(length > 0, 'Expected system block to exist locally')
|
|
46
|
+
|
|
47
|
+
const system = { key, length }
|
|
45
48
|
|
|
46
49
|
// setup encryption
|
|
47
50
|
const encryptionKey = await local.getUserData('autobase/encryption')
|
|
48
51
|
await AutobeeEncryption.setSystemEncryption(bootstrap.key, encryptionKey, core)
|
|
49
52
|
|
|
50
|
-
const
|
|
53
|
+
const session = core.session({ name: 'batch' })
|
|
54
|
+
await session.ready()
|
|
55
|
+
|
|
56
|
+
let catchup
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
catchup = await getCatchupHeads(session, length)
|
|
60
|
+
} finally {
|
|
61
|
+
await session.close()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const info = await readLegacySystemInfo(core, length, { wait: false })
|
|
65
|
+
|
|
51
66
|
const nodes = await Promise.all(
|
|
52
67
|
catchup.map((n) => getWriterBatch(store, n, bootstrap.key, encryptionKey))
|
|
53
68
|
)
|
|
54
69
|
|
|
55
|
-
const info = await readLegacySystemInfo(core, core.length)
|
|
56
|
-
|
|
57
70
|
const indexerManifests = await Promise.all(
|
|
58
71
|
info.indexers.map((idx) => storeCoreManifest(store, idx.key))
|
|
59
72
|
)
|
|
@@ -89,8 +102,11 @@ async function checkAutobaseMigration(store, local, bootstrap, legacyViews) {
|
|
|
89
102
|
|
|
90
103
|
// decode the legacy system info recorded in the system core's block at length
|
|
91
104
|
async function readLegacySystemInfo(core, length, opts = null) {
|
|
105
|
+
const block = await core.get(length - 1, opts)
|
|
106
|
+
assert(block !== null, 'Expected system block to exist locally')
|
|
107
|
+
|
|
92
108
|
// Decode hyperbee block using hyperbee2 compat
|
|
93
|
-
const node = decodeBlock(
|
|
109
|
+
const node = decodeBlock(block)
|
|
94
110
|
assert(node.keys.length > 0, 'bad system block')
|
|
95
111
|
|
|
96
112
|
// Decode system info from hyperbee block using autobee compat
|
|
@@ -259,6 +275,15 @@ function deriveNamespace(name, bootstrap, entropy, encryptionKey) {
|
|
|
259
275
|
])
|
|
260
276
|
}
|
|
261
277
|
|
|
278
|
+
// the record's length can run past the signed core into the local batch
|
|
279
|
+
// session, and the signed core can carry verified-but-undownloaded blocks -
|
|
280
|
+
// scan back for the newest block the signed core actually has locally
|
|
281
|
+
async function findLocalSystemLength(core, systemLength) {
|
|
282
|
+
let length = Math.min(core.length, systemLength)
|
|
283
|
+
while (length > 0 && !(await core.has(length - 1))) length--
|
|
284
|
+
return length
|
|
285
|
+
}
|
|
286
|
+
|
|
262
287
|
function coreLength(core, timeout) {
|
|
263
288
|
if (core.length) return core.length
|
|
264
289
|
|
|
@@ -355,18 +380,18 @@ async function getPersistedView(store, view) {
|
|
|
355
380
|
const core = store.get(view.key)
|
|
356
381
|
await core.ready()
|
|
357
382
|
|
|
358
|
-
|
|
383
|
+
// core.length can run past what was downloaded - the system's recorded
|
|
384
|
+
// length is the last one we know is local
|
|
385
|
+
const length = Math.min(core.length, view.length)
|
|
359
386
|
await core.close()
|
|
360
387
|
|
|
361
388
|
return { key: view.key, length }
|
|
362
389
|
}
|
|
363
390
|
|
|
364
|
-
async function getCatchupHeads(
|
|
365
|
-
const session = core.session({ name: 'batch' })
|
|
366
|
-
await session.ready()
|
|
367
|
-
|
|
391
|
+
async function getCatchupHeads(session, from) {
|
|
368
392
|
const nodes = []
|
|
369
|
-
|
|
393
|
+
// block 0 is the bee header, never a node
|
|
394
|
+
for (let i = Math.max(from, 1); i < session.length; i++) {
|
|
370
395
|
const node = await session.get(i, { wait: false })
|
|
371
396
|
if (!node) throw new Error('Expect nodes to exist locally')
|
|
372
397
|
|
|
@@ -378,8 +403,6 @@ async function getCatchupHeads(core) {
|
|
|
378
403
|
nodes.push({ key, length })
|
|
379
404
|
}
|
|
380
405
|
|
|
381
|
-
await session.close()
|
|
382
|
-
|
|
383
406
|
return nodes
|
|
384
407
|
}
|
|
385
408
|
|
package/lib/system.js
CHANGED
|
@@ -85,8 +85,8 @@ module.exports = class Systembee {
|
|
|
85
85
|
this.update(key, length, -1, 0, false, false, true, false, false, -1, false)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
addAnchor(key
|
|
89
|
-
this.update(key,
|
|
88
|
+
addAnchor(key) {
|
|
89
|
+
this.update(key, 0, -1, -1, false, true, false, false, true, -1, false)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
isGenesis() {
|
package/lib/topo.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const encoding = require('./encoding.js')
|
|
3
|
-
const { createNode } = require('./writers.js')
|
|
3
|
+
const { createNode, finalizeNodeBatch } = require('./writers.js')
|
|
4
4
|
const asserts = require('./asserts.js')
|
|
5
|
+
const { LEGACY_OPLOG_VERSION } = require('./constants.js')
|
|
5
6
|
|
|
6
7
|
const EMPTY_HEAD = { length: 0, key: null }
|
|
7
8
|
const INFO_LEGACY_KEY = b4a.concat([b4a.from([0, 0]), b4a.from('info')])
|
|
@@ -9,7 +10,6 @@ const INFO_LEGACY_KEY = b4a.concat([b4a.from([0, 0]), b4a.from('info')])
|
|
|
9
10
|
const DEFAULT_OP_TIMEOUT = 5000
|
|
10
11
|
|
|
11
12
|
exports.sort = sort
|
|
12
|
-
exports.rollback = rollback
|
|
13
13
|
exports.replay = replay
|
|
14
14
|
exports.getMostRecentHead = getMostRecentHead
|
|
15
15
|
|
|
@@ -48,46 +48,6 @@ async function getMostRecentHead(auto, system) {
|
|
|
48
48
|
return null
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
async function rollback(auto, system, verified) {
|
|
52
|
-
if (!(await isApplied(system, verified.op))) return null
|
|
53
|
-
|
|
54
|
-
const tip = []
|
|
55
|
-
const ch = system.createChangesStream()
|
|
56
|
-
|
|
57
|
-
let found = false
|
|
58
|
-
|
|
59
|
-
for await (const data of ch) {
|
|
60
|
-
const { ref, info } = getOplog(data)
|
|
61
|
-
|
|
62
|
-
if (sameNode(ref.oplog, verified.op)) {
|
|
63
|
-
// stale views.flushes - the verified head describes a prefix this system
|
|
64
|
-
// does not share, so nothing above it is ours to reapply
|
|
65
|
-
if (info.flushes === verified.flushes) found = true
|
|
66
|
-
break
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// walking back, a writer's lengths only decrease - once this writer is
|
|
70
|
-
// behind the verified head the marker cannot be further down
|
|
71
|
-
if (b4a.equals(ref.oplog.key, verified.op.key) && ref.oplog.length < verified.op.length) {
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
tip.push(ref)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (!found) return null
|
|
79
|
-
|
|
80
|
-
const inflated = await inflate(auto, tip.reverse())
|
|
81
|
-
|
|
82
|
-
// @todo: verify sort
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
undo: null,
|
|
86
|
-
view: null,
|
|
87
|
-
tip: inflated.map(getBatch)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
51
|
// nodes we cannot resolve are returned as null rather than dropped, so callers
|
|
92
52
|
// can tell an incomplete replay from a short one
|
|
93
53
|
async function replay(auto) {
|
|
@@ -108,14 +68,6 @@ async function replay(auto) {
|
|
|
108
68
|
return inflated.flatMap((b) => (b === null ? [null] : b.batch))
|
|
109
69
|
}
|
|
110
70
|
|
|
111
|
-
async function isApplied(system, node) {
|
|
112
|
-
const entry = await system.get(encoding.encodeSystemWriterKey(node.key))
|
|
113
|
-
if (entry === null) return false
|
|
114
|
-
|
|
115
|
-
const info = encoding.decodeSystemWriter(entry.key, entry.value)
|
|
116
|
-
return info.length >= node.length
|
|
117
|
-
}
|
|
118
|
-
|
|
119
71
|
async function sort(auto, batch) {
|
|
120
72
|
const node = batch[0]
|
|
121
73
|
const ch = auto.system.bee.createChangesStream()
|
|
@@ -124,7 +76,7 @@ async function sort(auto, batch) {
|
|
|
124
76
|
for await (const data of ch) {
|
|
125
77
|
if (isLegacyNode(data)) break
|
|
126
78
|
|
|
127
|
-
const { ref
|
|
79
|
+
const { ref } = getOplog(data)
|
|
128
80
|
const link = {
|
|
129
81
|
key: ref.oplog.key,
|
|
130
82
|
length: ref.oplog.length,
|
|
@@ -275,8 +227,20 @@ async function getOplogBatch(undo, core, length, weight, index, opts = null) {
|
|
|
275
227
|
const seq = length - 1
|
|
276
228
|
const block = await core.get(seq, opts)
|
|
277
229
|
if (block === null) return null
|
|
230
|
+
|
|
278
231
|
const oplog = encoding.decodeOplog(block)
|
|
279
232
|
const head = createNode(core, length, weight, oplog)
|
|
233
|
+
const legacy = oplog.version <= LEGACY_OPLOG_VERSION
|
|
234
|
+
|
|
235
|
+
// if this is legacy the start pointer is always 0, fix
|
|
236
|
+
if (legacy) {
|
|
237
|
+
for (let i = length - 1; i > 0; i--) {
|
|
238
|
+
const block = await core.get(i - 1, opts)
|
|
239
|
+
const oplog = encoding.decodeOplog(block)
|
|
240
|
+
if (oplog.batch.end === 0) break
|
|
241
|
+
head.batch.start++
|
|
242
|
+
}
|
|
243
|
+
}
|
|
280
244
|
|
|
281
245
|
const batch = []
|
|
282
246
|
const result = { undo, batch, index }
|
|
@@ -304,6 +268,8 @@ async function getOplogBatch(undo, core, length, weight, index, opts = null) {
|
|
|
304
268
|
}
|
|
305
269
|
|
|
306
270
|
batch.push(head)
|
|
271
|
+
finalizeNodeBatch(batch)
|
|
272
|
+
|
|
307
273
|
return result
|
|
308
274
|
}
|
|
309
275
|
|
|
@@ -433,11 +399,3 @@ function isLegacyNode(data) {
|
|
|
433
399
|
}
|
|
434
400
|
return false
|
|
435
401
|
}
|
|
436
|
-
|
|
437
|
-
function sameNode(a, b) {
|
|
438
|
-
return b4a.equals(a.key, b.key) && a.length === b.length
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function getBatch(b) {
|
|
442
|
-
return b.batch
|
|
443
|
-
}
|
package/lib/writers.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const encoding = require('./encoding.js')
|
|
3
3
|
const Triggers = require('./triggers.js')
|
|
4
|
+
const { LEGACY_OPLOG_VERSION } = require('./constants.js')
|
|
4
5
|
const { signAttestation, resolveWeight } = require('./witness.js')
|
|
5
6
|
const { WriterEncryption } = require('autobee-encryption')
|
|
6
7
|
|
|
@@ -62,9 +63,6 @@ class ActiveWriters {
|
|
|
62
63
|
|
|
63
64
|
let added = false
|
|
64
65
|
if (!w) {
|
|
65
|
-
// anchors are local-only and never replicated - nothing to fetch
|
|
66
|
-
const info = await this.auto.system.get(key, { unflushed: true })
|
|
67
|
-
if (info && info.isAnchor) return
|
|
68
66
|
w = await this.add(key)
|
|
69
67
|
if (!w) return
|
|
70
68
|
added = true
|
|
@@ -453,7 +451,9 @@ class Writer {
|
|
|
453
451
|
async update() {
|
|
454
452
|
const info = await this.system.get(this.core.key)
|
|
455
453
|
|
|
456
|
-
|
|
454
|
+
// an anchor core is prologue-only, so once its single node has applied
|
|
455
|
+
// nothing more can ever come - retire the writer
|
|
456
|
+
if (info && info.isAnchor && info.length > 0 && this !== this.writers.localWriter) {
|
|
457
457
|
await this.detachAndClose()
|
|
458
458
|
return info.length
|
|
459
459
|
}
|
|
@@ -572,7 +572,7 @@ class Writer {
|
|
|
572
572
|
}
|
|
573
573
|
|
|
574
574
|
batch.push(node)
|
|
575
|
-
if (b.end === 0) return batch
|
|
575
|
+
if (b.end === 0) return finalizeNodeBatch(batch)
|
|
576
576
|
|
|
577
577
|
// TODO: mark as dead
|
|
578
578
|
if (batch.length >= BATCH_WRITER_SANITY) return null
|
|
@@ -763,6 +763,7 @@ function sortsBefore(a, b) {
|
|
|
763
763
|
exports.ActiveWriters = ActiveWriters
|
|
764
764
|
exports.Writer = Writer
|
|
765
765
|
exports.createNode = createNode
|
|
766
|
+
exports.finalizeNodeBatch = finalizeNodeBatch
|
|
766
767
|
|
|
767
768
|
function createNode(core, length, weight, oplog) {
|
|
768
769
|
return {
|
|
@@ -784,4 +785,14 @@ function createNode(core, length, weight, oplog) {
|
|
|
784
785
|
}
|
|
785
786
|
}
|
|
786
787
|
|
|
788
|
+
function finalizeNodeBatch(batch) {
|
|
789
|
+
if (batch.length === 0 || batch[0].version > LEGACY_OPLOG_VERSION) return batch
|
|
790
|
+
|
|
791
|
+
for (let i = 0; i < batch.length; i++) {
|
|
792
|
+
batch[i].batch.start = i
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return batch
|
|
796
|
+
}
|
|
797
|
+
|
|
787
798
|
function noop() {}
|