autobee 2.0.0-rc.7 → 2.0.0-rc.9
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/encoding/compat.js +1 -1
- package/encoding/legacy.js +3 -3
- package/encoding/spec/autobee/index.js +27 -19
- package/encoding/spec/autobee/schema.json +11 -1
- package/index.js +52 -34
- package/lib/topo.js +79 -12
- package/package.json +2 -2
package/encoding/compat.js
CHANGED
package/encoding/legacy.js
CHANGED
|
@@ -153,18 +153,18 @@ const Node = {
|
|
|
153
153
|
preencode(state, m) {
|
|
154
154
|
Clock.preencode(state, m.heads)
|
|
155
155
|
c.uint.preencode(state, m.batch)
|
|
156
|
-
c.
|
|
156
|
+
c.optionalBuffer.preencode(state, m.value)
|
|
157
157
|
},
|
|
158
158
|
encode(state, m) {
|
|
159
159
|
Clock.encode(state, m.heads)
|
|
160
160
|
c.uint.encode(state, m.batch)
|
|
161
|
-
c.
|
|
161
|
+
c.optionalBuffer.encode(state, m.value)
|
|
162
162
|
},
|
|
163
163
|
decode(state, m) {
|
|
164
164
|
return {
|
|
165
165
|
heads: Clock.decode(state),
|
|
166
166
|
batch: c.uint.decode(state),
|
|
167
|
-
value: c.
|
|
167
|
+
value: c.optionalBuffer.decode(state)
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -97,8 +97,9 @@ const encoding4 = {
|
|
|
97
97
|
// @autobase-compat/boot-record
|
|
98
98
|
const encoding5 = {
|
|
99
99
|
preencode(state, m) {
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const v = m.version ?? 3
|
|
101
|
+
c.uint.preencode(state, v)
|
|
102
|
+
switch (v) {
|
|
102
103
|
case 0:
|
|
103
104
|
encoding3.preencode(state, m)
|
|
104
105
|
break
|
|
@@ -112,8 +113,9 @@ const encoding5 = {
|
|
|
112
113
|
}
|
|
113
114
|
},
|
|
114
115
|
encode(state, m) {
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
const v = m.version ?? 3
|
|
117
|
+
c.uint.encode(state, v)
|
|
118
|
+
switch (v) {
|
|
117
119
|
case 0:
|
|
118
120
|
encoding3.encode(state, m)
|
|
119
121
|
break
|
|
@@ -236,17 +238,17 @@ const encoding9 = {
|
|
|
236
238
|
preencode(state, m) {
|
|
237
239
|
encoding1.preencode(state, m.heads)
|
|
238
240
|
c.uint.preencode(state, m.batch)
|
|
239
|
-
c.
|
|
241
|
+
c.optionalBuffer.preencode(state, m.value)
|
|
240
242
|
},
|
|
241
243
|
encode(state, m) {
|
|
242
244
|
encoding1.encode(state, m.heads)
|
|
243
245
|
c.uint.encode(state, m.batch)
|
|
244
|
-
c.
|
|
246
|
+
c.optionalBuffer.encode(state, m.value)
|
|
245
247
|
},
|
|
246
248
|
decode(state) {
|
|
247
249
|
const r0 = encoding1.decode(state)
|
|
248
250
|
const r1 = c.uint.decode(state)
|
|
249
|
-
const r2 = c.
|
|
251
|
+
const r2 = c.optionalBuffer.decode(state)
|
|
250
252
|
|
|
251
253
|
return {
|
|
252
254
|
heads: r0,
|
|
@@ -496,8 +498,9 @@ const encoding18 = {
|
|
|
496
498
|
// @autobee/system-info
|
|
497
499
|
const encoding19 = {
|
|
498
500
|
preencode(state, m) {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
+
const v = m.version ?? 3
|
|
502
|
+
c.uint.preencode(state, v)
|
|
503
|
+
switch (v) {
|
|
501
504
|
case 0:
|
|
502
505
|
case 1:
|
|
503
506
|
encoding15.preencode(state, m)
|
|
@@ -513,8 +516,9 @@ const encoding19 = {
|
|
|
513
516
|
}
|
|
514
517
|
},
|
|
515
518
|
encode(state, m) {
|
|
516
|
-
|
|
517
|
-
|
|
519
|
+
const v = m.version ?? 3
|
|
520
|
+
c.uint.encode(state, v)
|
|
521
|
+
switch (v) {
|
|
518
522
|
case 0:
|
|
519
523
|
case 1:
|
|
520
524
|
encoding15.encode(state, m)
|
|
@@ -603,8 +607,9 @@ const encoding20 = {
|
|
|
603
607
|
// @autobee/system-writer
|
|
604
608
|
const encoding21 = {
|
|
605
609
|
preencode(state, m) {
|
|
606
|
-
|
|
607
|
-
|
|
610
|
+
const v = m.version ?? 4
|
|
611
|
+
c.uint.preencode(state, v)
|
|
612
|
+
switch (v) {
|
|
608
613
|
case 0:
|
|
609
614
|
case 1:
|
|
610
615
|
case 2:
|
|
@@ -619,8 +624,9 @@ const encoding21 = {
|
|
|
619
624
|
}
|
|
620
625
|
},
|
|
621
626
|
encode(state, m) {
|
|
622
|
-
|
|
623
|
-
|
|
627
|
+
const v = m.version ?? 4
|
|
628
|
+
c.uint.encode(state, v)
|
|
629
|
+
switch (v) {
|
|
624
630
|
case 0:
|
|
625
631
|
case 1:
|
|
626
632
|
case 2:
|
|
@@ -905,8 +911,9 @@ const encoding29 = {
|
|
|
905
911
|
// @autobee/oplog
|
|
906
912
|
const encoding30 = {
|
|
907
913
|
preencode(state, m) {
|
|
908
|
-
|
|
909
|
-
|
|
914
|
+
const v = m.version ?? 3
|
|
915
|
+
c.uint.preencode(state, v)
|
|
916
|
+
switch (v) {
|
|
910
917
|
case 0:
|
|
911
918
|
encoding12.preencode(state, m)
|
|
912
919
|
break
|
|
@@ -924,8 +931,9 @@ const encoding30 = {
|
|
|
924
931
|
}
|
|
925
932
|
},
|
|
926
933
|
encode(state, m) {
|
|
927
|
-
|
|
928
|
-
|
|
934
|
+
const v = m.version ?? 3
|
|
935
|
+
c.uint.encode(state, v)
|
|
936
|
+
switch (v) {
|
|
929
937
|
case 0:
|
|
930
938
|
encoding12.encode(state, m)
|
|
931
939
|
break
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
{
|
|
195
195
|
"name": "value",
|
|
196
196
|
"required": true,
|
|
197
|
-
"type": "
|
|
197
|
+
"type": "optionalBuffer",
|
|
198
198
|
"version": 1
|
|
199
199
|
}
|
|
200
200
|
]
|
|
@@ -805,5 +805,15 @@
|
|
|
805
805
|
}
|
|
806
806
|
]
|
|
807
807
|
}
|
|
808
|
+
],
|
|
809
|
+
"namespaces": [
|
|
810
|
+
{
|
|
811
|
+
"name": "autobee",
|
|
812
|
+
"external": "../../legacy.js"
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"name": "autobase-compat",
|
|
816
|
+
"external": "../../legacy.js"
|
|
817
|
+
}
|
|
808
818
|
]
|
|
809
819
|
}
|
package/index.js
CHANGED
|
@@ -589,58 +589,74 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
589
589
|
}
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
-
async _getOplog(key, length) {
|
|
592
|
+
async _getOplog(key, length, opts = null) {
|
|
593
593
|
const core = this.openCore(key)
|
|
594
|
-
await core.ready()
|
|
595
594
|
|
|
596
|
-
|
|
597
|
-
|
|
595
|
+
try {
|
|
596
|
+
await core.ready()
|
|
598
597
|
|
|
599
|
-
|
|
600
|
-
|
|
598
|
+
const target = length >= 0 ? length : core.length
|
|
599
|
+
if (target === 0) return null
|
|
601
600
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
601
|
+
const buf = await core.get(target - 1, opts)
|
|
602
|
+
if (buf === null) return null
|
|
605
603
|
|
|
606
|
-
|
|
604
|
+
let op = encoding.decodeOplog(buf)
|
|
607
605
|
|
|
608
|
-
|
|
606
|
+
// legacy nodes always inflate, but only indexers carry views - callers
|
|
607
|
+
// that need system info must check op.views
|
|
608
|
+
if (op.version < 3) {
|
|
609
|
+
op = await this._inflateLegacyOplog(buf, core, target - 1, opts)
|
|
610
|
+
}
|
|
609
611
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
612
|
+
return {
|
|
613
|
+
key,
|
|
614
|
+
length: target,
|
|
615
|
+
op
|
|
616
|
+
}
|
|
617
|
+
} finally {
|
|
618
|
+
await core.close()
|
|
614
619
|
}
|
|
615
620
|
}
|
|
616
621
|
|
|
617
|
-
async _inflateLegacyOplog(buf, core, seq) {
|
|
622
|
+
async _inflateLegacyOplog(buf, core, seq, opts = null) {
|
|
618
623
|
const m = encoding.decodeRawOplog(buf)
|
|
624
|
+
|
|
625
|
+
const op = {
|
|
626
|
+
version: m.version,
|
|
627
|
+
timestamp: 0,
|
|
628
|
+
links: m.node.heads,
|
|
629
|
+
batch: { start: 0, end: m.node.batch - 1 },
|
|
630
|
+
views: null,
|
|
631
|
+
optimistic: !!m.optimistic,
|
|
632
|
+
value: m.node.value
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (m.digest === null || m.checkpoint === null) return op
|
|
636
|
+
|
|
619
637
|
const fetches = []
|
|
620
638
|
|
|
621
|
-
fetches.push(m.digest.pointer ? core.get(seq - m.digest.pointer) : buf)
|
|
622
|
-
fetches.push(
|
|
639
|
+
fetches.push(m.digest.pointer ? core.get(seq - m.digest.pointer, opts) : buf)
|
|
640
|
+
fetches.push(
|
|
641
|
+
m.checkpoint.pointer ? core.get(seq - m.checkpoint.system.checkpointer, opts) : buf
|
|
642
|
+
)
|
|
623
643
|
|
|
624
644
|
const [digestNode, checkpointNode] = await Promise.all(fetches)
|
|
645
|
+
// a wait: false get we could not satisfy locally
|
|
646
|
+
if (digestNode === null || checkpointNode === null) return op
|
|
625
647
|
|
|
626
648
|
const { digest } = encoding.decodeRawOplog(digestNode)
|
|
627
649
|
const { checkpoint } = encoding.decodeRawOplog(checkpointNode)
|
|
628
650
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
batch: { start: 0, end: m.node.batch - 1 },
|
|
634
|
-
views: {
|
|
635
|
-
system: {
|
|
636
|
-
key: digest.key,
|
|
637
|
-
length: checkpoint.system.checkpoint.length
|
|
638
|
-
},
|
|
639
|
-
flushes: seq
|
|
651
|
+
op.views = {
|
|
652
|
+
system: {
|
|
653
|
+
key: digest.key,
|
|
654
|
+
length: checkpoint.system.checkpoint.length
|
|
640
655
|
},
|
|
641
|
-
|
|
642
|
-
value: m.node.value
|
|
656
|
+
flushes: seq
|
|
643
657
|
}
|
|
658
|
+
|
|
659
|
+
return op
|
|
644
660
|
}
|
|
645
661
|
|
|
646
662
|
async _update(changes) {
|
|
@@ -1129,14 +1145,16 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1129
1145
|
|
|
1130
1146
|
async _rebootFromHead(head, trusted, { force = false, wait = true } = {}) {
|
|
1131
1147
|
const oplog = await this._getOplog(head.key, head.length)
|
|
1132
|
-
if (!oplog) return
|
|
1148
|
+
if (!oplog) return null
|
|
1133
1149
|
|
|
1134
1150
|
const verified = trusted ? await this._getOplog(trusted.key, trusted.length) : oplog
|
|
1151
|
+
if (!verified) return null
|
|
1135
1152
|
|
|
1136
|
-
|
|
1153
|
+
// legacy nodes from non-indexers have no system info to reboot from
|
|
1154
|
+
if (!oplog.op.views || !verified.op.views) return null
|
|
1137
1155
|
|
|
1138
1156
|
if (!force && verified.op.views.flushes - this.system.flushes < MIN_FF_GAP) {
|
|
1139
|
-
return
|
|
1157
|
+
return null
|
|
1140
1158
|
}
|
|
1141
1159
|
|
|
1142
1160
|
const moved = await this._moveTo(batchToHead(verified.op.views.system), {
|
package/lib/topo.js
CHANGED
|
@@ -66,17 +66,66 @@ async function rollback(auto, system, verified) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// nodes we cannot resolve are returned as null rather than dropped, so callers
|
|
70
|
+
// can tell an incomplete replay from a short one
|
|
69
71
|
async function replay(auto) {
|
|
70
|
-
const ch = auto.system.bee.createChangesStream()
|
|
71
72
|
const replay = []
|
|
72
73
|
|
|
73
|
-
for await (const
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
for await (const entry of oplogIterator(auto.system.bee)) {
|
|
75
|
+
// the walk was cut short, nothing below this point is reachable
|
|
76
|
+
if (entry === null) {
|
|
77
|
+
replay.push(null)
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const ref of entry.refs) replay.push(ref)
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
// same reasoning as the iterator - never block on what we do not have
|
|
85
|
+
const inflated = await inflate(auto, replay.reverse(), { wait: false })
|
|
86
|
+
return inflated.flatMap((b) => (b === null ? [null] : b.batch))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// tolerates legacy nodes instead of asserting
|
|
90
|
+
async function* oplogIterator(bee) {
|
|
91
|
+
try {
|
|
92
|
+
for await (const node of bee.createChangesStream({ wait: false })) {
|
|
93
|
+
const legacy = isLegacyNode(node)
|
|
94
|
+
const refs = getOplogs(node)
|
|
95
|
+
if (refs.length) yield { node, refs, legacy }
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
yield null // signal data not available
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// legacy nodes carry no isOplog flag so we cannot tell which writer update is
|
|
103
|
+
// the oplog to fetch - yield every candidate until we have a better signal
|
|
104
|
+
function getOplogs(data) {
|
|
105
|
+
const undo = data.tail || EMPTY_HEAD
|
|
106
|
+
const oplogs = []
|
|
107
|
+
const candidates = []
|
|
108
|
+
|
|
109
|
+
for (const { keys } of data.batch) {
|
|
110
|
+
for (const k of keys) {
|
|
111
|
+
if (k.key[0] !== 1) continue
|
|
112
|
+
|
|
113
|
+
let value = null
|
|
114
|
+
try {
|
|
115
|
+
value = encoding.decodeSystemWriter(k.key, k.value)
|
|
116
|
+
} catch {
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// writer was just added, nothing to fetch
|
|
121
|
+
if (!value.length) continue
|
|
122
|
+
|
|
123
|
+
if (value.isOplog) oplogs.push({ undo, oplog: value })
|
|
124
|
+
else candidates.push({ undo, oplog: value })
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return oplogs.length ? oplogs : candidates
|
|
80
129
|
}
|
|
81
130
|
|
|
82
131
|
async function sort(auto, batch) {
|
|
@@ -193,12 +242,19 @@ function cmp(a, b) {
|
|
|
193
242
|
return a.length < b.length ? -1 : 1
|
|
194
243
|
}
|
|
195
244
|
|
|
196
|
-
async function inflate(auto, reorder) {
|
|
245
|
+
async function inflate(auto, reorder, opts = null) {
|
|
197
246
|
const promises = []
|
|
198
247
|
const sessions = new Map()
|
|
199
248
|
|
|
200
249
|
for (let i = 0; i < reorder.length; i++) {
|
|
201
250
|
const ref = reorder[i]
|
|
251
|
+
|
|
252
|
+
// marker for a node we could not resolve, pass it through
|
|
253
|
+
if (ref === null) {
|
|
254
|
+
promises.push(null)
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
|
|
202
258
|
const id = b4a.toString(ref.oplog.key, 'hex')
|
|
203
259
|
// only seeds the node - reapplication re-resolves from the witness
|
|
204
260
|
const weight = ref.oplog.weight
|
|
@@ -215,7 +271,7 @@ async function inflate(auto, reorder) {
|
|
|
215
271
|
sessions.set(id, core)
|
|
216
272
|
}
|
|
217
273
|
|
|
218
|
-
promises.push(getOplogBatch(ref.undo, core, ref.oplog.length, weight, i))
|
|
274
|
+
promises.push(getOplogBatch(ref.undo, core, ref.oplog.length, weight, i, opts))
|
|
219
275
|
}
|
|
220
276
|
|
|
221
277
|
try {
|
|
@@ -225,9 +281,10 @@ async function inflate(auto, reorder) {
|
|
|
225
281
|
}
|
|
226
282
|
}
|
|
227
283
|
|
|
228
|
-
async function getOplogBatch(undo, core, length, weight, index) {
|
|
284
|
+
async function getOplogBatch(undo, core, length, weight, index, opts = null) {
|
|
229
285
|
const seq = length - 1
|
|
230
|
-
const block = await core.get(seq)
|
|
286
|
+
const block = await core.get(seq, opts)
|
|
287
|
+
if (block === null) return null
|
|
231
288
|
const oplog = encoding.decodeOplog(block)
|
|
232
289
|
const head = createNode(core, length, weight, oplog)
|
|
233
290
|
|
|
@@ -239,12 +296,18 @@ async function getOplogBatch(undo, core, length, weight, index) {
|
|
|
239
296
|
const end = seq + head.batch.end // skip head, we have it
|
|
240
297
|
|
|
241
298
|
for (let i = start; i < end; i++) {
|
|
242
|
-
remaining.push(core.get(i))
|
|
299
|
+
remaining.push(core.get(i, opts))
|
|
243
300
|
}
|
|
244
301
|
|
|
245
302
|
if (remaining.length) {
|
|
246
303
|
const blocks = await Promise.all(remaining)
|
|
247
304
|
for (let i = 0; i < blocks.length; i++) {
|
|
305
|
+
// keep the hole so the gap in the batch stays visible
|
|
306
|
+
if (blocks[i] === null) {
|
|
307
|
+
batch.push(null)
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
|
|
248
311
|
const oplog = encoding.decodeOplog(blocks[i])
|
|
249
312
|
batch.push(createNode(core, i + start + 1, weight, oplog))
|
|
250
313
|
}
|
|
@@ -255,8 +318,12 @@ async function getOplogBatch(undo, core, length, weight, index) {
|
|
|
255
318
|
}
|
|
256
319
|
|
|
257
320
|
function getLocalBatch(undo, writer, length, weight, index) {
|
|
321
|
+
if (writer.core.closed) throw new Error('Writer core is closed')
|
|
322
|
+
|
|
258
323
|
if (length > writer.core.length) {
|
|
259
|
-
if (!writer.pending)
|
|
324
|
+
if (!writer.pending) {
|
|
325
|
+
throw new Error(`No local nodes: looking for ${length}, found ${writer.core.length}`)
|
|
326
|
+
}
|
|
260
327
|
|
|
261
328
|
let i = 0
|
|
262
329
|
while (i < writer.pending.length) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autobee",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.9",
|
|
4
4
|
"description": "Bee based autobase",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Holepunch Inc.",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"autobee-encryption": "^0.1.2",
|
|
27
27
|
"autobee-wakeup": "^1.1.3",
|
|
28
28
|
"b4a": "^1.8.0",
|
|
29
|
-
"compact-encoding": "^
|
|
29
|
+
"compact-encoding": "^3.3.0",
|
|
30
30
|
"hyperbee2": "^2.10.3",
|
|
31
31
|
"hypercore": "^11.33.2",
|
|
32
32
|
"hypercore-id-encoding": "^1.3.0",
|