hyperbee2 1.1.2 → 2.0.0
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 +161 -22
- package/lib/cache.js +7 -0
- package/lib/{changes-stream.js → changes.js} +5 -2
- package/lib/compat.js +196 -13
- package/lib/compression.js +163 -0
- package/lib/context.js +50 -9
- package/lib/diff.js +142 -0
- package/lib/encoding.js +136 -6
- package/lib/ranges.js +144 -0
- package/lib/tree.js +97 -48
- package/lib/write.js +270 -101
- package/package.json +4 -2
- package/spec/hyperschema/index.js +356 -47
- package/spec/hyperschema/schema.json +208 -5
- package/lib/key-value-stream.js +0 -134
package/lib/write.js
CHANGED
|
@@ -1,33 +1,58 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const c = require('compact-encoding')
|
|
3
|
-
const {
|
|
3
|
+
const { OP_COHORT } = require('./compression.js')
|
|
4
|
+
const { encodeBlock, TYPE_COMPAT, TYPE_LATEST } = require('./encoding.js')
|
|
4
5
|
const {
|
|
6
|
+
Pointer,
|
|
7
|
+
KeyPointer,
|
|
8
|
+
ValuePointer,
|
|
5
9
|
TreeNode,
|
|
6
10
|
TreeNodePointer,
|
|
7
11
|
MIN_KEYS,
|
|
8
|
-
|
|
9
|
-
CHANGED,
|
|
12
|
+
INSERTED,
|
|
10
13
|
NEEDS_SPLIT
|
|
11
14
|
} = require('./tree.js')
|
|
12
15
|
|
|
16
|
+
const PREFERRED_BLOCK_SIZE = 4096
|
|
17
|
+
const INLINE_VALUE_SIZE = 4096
|
|
18
|
+
const ESTIMATED_POINTER_SIZE = 12 // conversative, seq=8b, offset=2b, core=2b
|
|
19
|
+
|
|
13
20
|
module.exports = class WriteBatch {
|
|
14
|
-
constructor(tree,
|
|
21
|
+
constructor(tree, opts = {}) {
|
|
22
|
+
const {
|
|
23
|
+
length = -1,
|
|
24
|
+
key = null,
|
|
25
|
+
autoUpdate = true,
|
|
26
|
+
compat = false,
|
|
27
|
+
type = compat ? TYPE_COMPAT : TYPE_LATEST,
|
|
28
|
+
deltaMax = supportsCompression(type) ? 16 : 0,
|
|
29
|
+
deltaMin = supportsCompression(type) ? 1 : Infinity,
|
|
30
|
+
inlineValueSize = INLINE_VALUE_SIZE,
|
|
31
|
+
preferredBlockSize = PREFERRED_BLOCK_SIZE
|
|
32
|
+
} = opts
|
|
33
|
+
|
|
15
34
|
this.tree = tree
|
|
35
|
+
this.deltaMax = deltaMax
|
|
36
|
+
this.deltaMin = deltaMin
|
|
37
|
+
this.inlineValueSize = inlineValueSize
|
|
38
|
+
this.preferredBlockSize = preferredBlockSize
|
|
16
39
|
this.snapshot = tree.snapshot()
|
|
17
40
|
this.autoUpdate = autoUpdate
|
|
18
41
|
this.length = length
|
|
19
42
|
this.key = key
|
|
43
|
+
this.type = type
|
|
20
44
|
this.closed = false
|
|
45
|
+
this.applied = 0
|
|
21
46
|
this.root = null
|
|
22
47
|
this.ops = []
|
|
23
48
|
}
|
|
24
49
|
|
|
25
50
|
tryPut(key, value) {
|
|
26
|
-
this.ops.push({ put: true, key, value })
|
|
51
|
+
this.ops.push({ put: true, applied: false, key, value })
|
|
27
52
|
}
|
|
28
53
|
|
|
29
54
|
tryDelete(key) {
|
|
30
|
-
this.ops.push({ put: false, key, value: null })
|
|
55
|
+
this.ops.push({ put: false, applied: false, key, value: null })
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
tryClear() {
|
|
@@ -51,7 +76,6 @@ module.exports = class WriteBatch {
|
|
|
51
76
|
|
|
52
77
|
try {
|
|
53
78
|
const ops = this.ops
|
|
54
|
-
this.ops = []
|
|
55
79
|
|
|
56
80
|
const root = await this.tree.bootstrap()
|
|
57
81
|
|
|
@@ -60,20 +84,15 @@ module.exports = class WriteBatch {
|
|
|
60
84
|
|
|
61
85
|
const changed = length === 0
|
|
62
86
|
const seq = length === 0 ? 0 : length - 1
|
|
87
|
+
const value = changed ? new TreeNode([], []) : null
|
|
63
88
|
|
|
64
89
|
this.length = length
|
|
65
|
-
this.root = new TreeNodePointer(
|
|
66
|
-
context,
|
|
67
|
-
0,
|
|
68
|
-
seq,
|
|
69
|
-
0,
|
|
70
|
-
changed,
|
|
71
|
-
changed ? new TreeNode([], []) : null
|
|
72
|
-
)
|
|
90
|
+
this.root = new TreeNodePointer(context, 0, seq, 0, changed, value)
|
|
73
91
|
|
|
74
92
|
for (const op of ops) {
|
|
75
|
-
if (op.put) await this._put(op.key, op.value)
|
|
76
|
-
else await this._delete(op.key)
|
|
93
|
+
if (op.put) op.applied = await this._put(op.key, op.value)
|
|
94
|
+
else op.applied = await this._delete(op.key)
|
|
95
|
+
if (op.applied) this.applied++
|
|
77
96
|
}
|
|
78
97
|
|
|
79
98
|
await this._flush()
|
|
@@ -110,15 +129,16 @@ module.exports = class WriteBatch {
|
|
|
110
129
|
|
|
111
130
|
while (s < e) {
|
|
112
131
|
const mid = (s + e) >> 1
|
|
113
|
-
const m = v.keys
|
|
132
|
+
const m = v.keys.get(mid)
|
|
114
133
|
|
|
115
134
|
c = b4a.compare(target, m.key)
|
|
116
135
|
|
|
117
136
|
if (c === 0) {
|
|
118
|
-
|
|
137
|
+
const existing = await this.snapshot.inflateValue(m)
|
|
138
|
+
if (b4a.equals(existing, value)) return false
|
|
119
139
|
v.setValue(this.tree.context, mid, value)
|
|
120
140
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
121
|
-
return
|
|
141
|
+
return true
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
if (c < 0) e = mid
|
|
@@ -126,16 +146,21 @@ module.exports = class WriteBatch {
|
|
|
126
146
|
}
|
|
127
147
|
|
|
128
148
|
const i = c < 0 ? e : s
|
|
129
|
-
ptr = v.children
|
|
149
|
+
ptr = v.children.get(i)
|
|
130
150
|
}
|
|
131
151
|
|
|
132
152
|
const v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
133
|
-
let status = v.
|
|
134
|
-
|
|
135
|
-
if (status
|
|
153
|
+
let status = v.insertLeaf(this.tree.context, target, value)
|
|
154
|
+
|
|
155
|
+
if (status >= 0) {
|
|
156
|
+
// already exists, upsert if changed
|
|
157
|
+
const m = v.keys.get(status)
|
|
158
|
+
const existing = await this.snapshot.inflateValue(m)
|
|
159
|
+
if (b4a.equals(existing, value)) return false
|
|
160
|
+
v.setValue(this.tree.context, status, value)
|
|
161
|
+
}
|
|
136
162
|
|
|
137
163
|
ptr.changed = true
|
|
138
|
-
|
|
139
164
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
140
165
|
|
|
141
166
|
while (status === NEEDS_SPLIT) {
|
|
@@ -145,16 +170,19 @@ module.exports = class WriteBatch {
|
|
|
145
170
|
|
|
146
171
|
if (parent) {
|
|
147
172
|
const p = parent.value ? this.snapshot.bump(parent) : await this.snapshot.inflate(parent)
|
|
148
|
-
status = p.
|
|
173
|
+
status = p.insertNode(this.tree.context, median, right)
|
|
149
174
|
ptr = parent
|
|
150
175
|
} else {
|
|
151
176
|
this.root = new TreeNodePointer(this.tree.context, 0, 0, 0, true, new TreeNode([], []))
|
|
152
177
|
this.root.value.keys.push(median)
|
|
153
|
-
this.root.value.children.push(ptr
|
|
178
|
+
this.root.value.children.push(ptr)
|
|
179
|
+
this.root.value.children.push(right)
|
|
154
180
|
this.snapshot.bump(this.root)
|
|
155
|
-
status =
|
|
181
|
+
status = INSERTED
|
|
156
182
|
}
|
|
157
183
|
}
|
|
184
|
+
|
|
185
|
+
return true
|
|
158
186
|
}
|
|
159
187
|
|
|
160
188
|
async _delete(key) {
|
|
@@ -172,32 +200,33 @@ module.exports = class WriteBatch {
|
|
|
172
200
|
|
|
173
201
|
while (s < e) {
|
|
174
202
|
const mid = (s + e) >> 1
|
|
175
|
-
c = b4a.compare(key, v.keys
|
|
203
|
+
c = b4a.compare(key, v.keys.get(mid).key)
|
|
176
204
|
|
|
177
205
|
if (c === 0) {
|
|
178
206
|
if (v.children.length) await this._setKeyToNearestLeaf(v, mid, stack)
|
|
179
207
|
else v.removeKey(mid)
|
|
180
|
-
|
|
181
208
|
// we mark these as changed late, so we don't rewrite them if it is a 404
|
|
182
209
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
183
210
|
this.root = await this._rebalance(stack)
|
|
184
|
-
return
|
|
211
|
+
return true
|
|
185
212
|
}
|
|
186
213
|
|
|
187
214
|
if (c < 0) e = mid
|
|
188
215
|
else s = mid + 1
|
|
189
216
|
}
|
|
190
217
|
|
|
191
|
-
if (!v.children.length) return
|
|
218
|
+
if (!v.children.length) return false
|
|
192
219
|
|
|
193
220
|
const i = c < 0 ? e : s
|
|
194
|
-
ptr = v.children
|
|
221
|
+
ptr = v.children.get(i)
|
|
195
222
|
}
|
|
223
|
+
|
|
224
|
+
return false
|
|
196
225
|
}
|
|
197
226
|
|
|
198
227
|
async _setKeyToNearestLeaf(v, index, stack) {
|
|
199
|
-
let left = v.children
|
|
200
|
-
let right = v.children
|
|
228
|
+
let left = v.children.get(index)
|
|
229
|
+
let right = v.children.get(index + 1)
|
|
201
230
|
|
|
202
231
|
const [ls, rs] = await Promise.all([this._leafSize(left, false), this._leafSize(right, true)])
|
|
203
232
|
|
|
@@ -206,28 +235,28 @@ module.exports = class WriteBatch {
|
|
|
206
235
|
stack.push(right)
|
|
207
236
|
let r = right.value ? this.snapshot.bump(right) : await this.snapshot.inflate(right)
|
|
208
237
|
while (r.children.length) {
|
|
209
|
-
right = r.children
|
|
238
|
+
right = r.children.get(0)
|
|
210
239
|
stack.push(right)
|
|
211
240
|
r = right.value ? this.snapshot.bump(right) : await this.snapshot.inflate(right)
|
|
212
241
|
}
|
|
213
|
-
v.keys
|
|
242
|
+
v.keys.set(index, r.keys.shift())
|
|
214
243
|
} else {
|
|
215
244
|
// if fewer leaves on the right
|
|
216
245
|
stack.push(left)
|
|
217
246
|
let l = left.value ? this.snapshot.bump(left) : await this.snapshot.inflate(left)
|
|
218
247
|
while (l.children.length) {
|
|
219
|
-
left = l.children
|
|
248
|
+
left = l.children.get(l.children.length - 1)
|
|
220
249
|
stack.push(left)
|
|
221
250
|
l = left.value ? this.snapshot.bump(left) : await this.snapshot.inflate(left)
|
|
222
251
|
}
|
|
223
|
-
v.keys
|
|
252
|
+
v.keys.set(index, l.keys.pop())
|
|
224
253
|
}
|
|
225
254
|
}
|
|
226
255
|
|
|
227
256
|
async _leafSize(ptr, goLeft) {
|
|
228
257
|
let v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
229
258
|
while (v.children.length) {
|
|
230
|
-
ptr = v.children
|
|
259
|
+
ptr = v.children.get(goLeft ? 0 : v.children.length - 1)
|
|
231
260
|
v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
232
261
|
}
|
|
233
262
|
return v.keys.length
|
|
@@ -235,6 +264,7 @@ module.exports = class WriteBatch {
|
|
|
235
264
|
|
|
236
265
|
async _rebalance(stack) {
|
|
237
266
|
const root = stack[0]
|
|
267
|
+
const minKeys = this.tree.context.minKeys
|
|
238
268
|
|
|
239
269
|
while (stack.length > 1) {
|
|
240
270
|
const ptr = stack.pop()
|
|
@@ -251,11 +281,11 @@ module.exports = class WriteBatch {
|
|
|
251
281
|
let l = left && (left.value ? this.snapshot.bump(left) : await this.snapshot.inflate(left))
|
|
252
282
|
|
|
253
283
|
// maybe borrow from left sibling?
|
|
254
|
-
if (l && l.keys.length >
|
|
284
|
+
if (l && l.keys.length > minKeys) {
|
|
255
285
|
left.changed = true
|
|
256
|
-
v.keys.unshift(p.keys
|
|
286
|
+
v.keys.unshift(p.keys.get(index - 1))
|
|
257
287
|
if (l.children.length) v.children.unshift(l.children.pop())
|
|
258
|
-
p.keys
|
|
288
|
+
p.keys.set(index - 1, l.keys.pop())
|
|
259
289
|
return root
|
|
260
290
|
}
|
|
261
291
|
|
|
@@ -263,11 +293,11 @@ module.exports = class WriteBatch {
|
|
|
263
293
|
right && (right.value ? this.snapshot.bump(right) : await this.snapshot.inflate(right))
|
|
264
294
|
|
|
265
295
|
// maybe borrow from right sibling?
|
|
266
|
-
if (r && r.keys.length >
|
|
296
|
+
if (r && r.keys.length > minKeys) {
|
|
267
297
|
right.changed = true
|
|
268
|
-
v.keys.push(p.keys
|
|
298
|
+
v.keys.push(p.keys.get(index))
|
|
269
299
|
if (r.children.length) v.children.push(r.children.shift())
|
|
270
|
-
p.keys
|
|
300
|
+
p.keys.set(index, r.keys.shift())
|
|
271
301
|
return root
|
|
272
302
|
}
|
|
273
303
|
|
|
@@ -282,7 +312,7 @@ module.exports = class WriteBatch {
|
|
|
282
312
|
}
|
|
283
313
|
|
|
284
314
|
left.changed = true
|
|
285
|
-
l.merge(r, p.keys
|
|
315
|
+
l.merge(r, p.keys.get(index))
|
|
286
316
|
|
|
287
317
|
parent.changed = true
|
|
288
318
|
p.removeKey(index)
|
|
@@ -290,70 +320,101 @@ module.exports = class WriteBatch {
|
|
|
290
320
|
|
|
291
321
|
const r = root.value ? this.snapshot.bump(root) : await this.snapshot.inflate(root)
|
|
292
322
|
// check if the tree shrunk
|
|
293
|
-
if (!r.keys.length && r.children.length) return r.children
|
|
323
|
+
if (!r.keys.length && r.children.length) return r.children.get(0)
|
|
294
324
|
return root
|
|
295
325
|
}
|
|
296
326
|
|
|
327
|
+
_shouldInlineValue(k) {
|
|
328
|
+
if (!k.value || supportsCompression(this.type)) return true
|
|
329
|
+
if (k.valuePointer) return true
|
|
330
|
+
return k.value.byteLength <= this.inlineValueSize
|
|
331
|
+
}
|
|
332
|
+
|
|
297
333
|
async _flush() {
|
|
298
|
-
if (!this.root || !this.root.changed)
|
|
334
|
+
if (!this.root || !this.root.changed) {
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!this.root.value) await this.snapshot.inflate(this.root)
|
|
339
|
+
|
|
340
|
+
let update = { size: 0, nodes: [], keys: [], values: [] }
|
|
341
|
+
let minValue = -1
|
|
299
342
|
|
|
300
|
-
const update = { node: [], keys: [] }
|
|
301
343
|
const batch = [update]
|
|
302
|
-
const stack = [
|
|
344
|
+
const stack = [this.root]
|
|
345
|
+
const values = []
|
|
346
|
+
|
|
303
347
|
const context = this.tree.context.getLocalContext()
|
|
348
|
+
const activeRequests = this.tree.activeRequests
|
|
304
349
|
|
|
305
|
-
await context.update(
|
|
350
|
+
await context.update(activeRequests)
|
|
306
351
|
|
|
307
352
|
while (stack.length > 0) {
|
|
308
|
-
const
|
|
353
|
+
const node = stack.pop()
|
|
309
354
|
|
|
310
|
-
|
|
311
|
-
|
|
355
|
+
if (this.type !== TYPE_COMPAT && update.size >= this.preferredBlockSize) {
|
|
356
|
+
update = { size: 0, nodes: [], keys: [], values: [] }
|
|
357
|
+
batch.push(update)
|
|
358
|
+
}
|
|
312
359
|
|
|
313
|
-
|
|
314
|
-
|
|
360
|
+
update.nodes.push(node)
|
|
361
|
+
update.size += getEstimatedNodeSize(node.value)
|
|
315
362
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
363
|
+
const keys = node.value.keys
|
|
364
|
+
const children = node.value.children
|
|
365
|
+
|
|
366
|
+
for (let i = 0; i < keys.entries.length; i++) {
|
|
367
|
+
const k = keys.entries[i]
|
|
368
|
+
if (!k.changed) continue
|
|
369
|
+
|
|
370
|
+
if (!this._shouldInlineValue(k)) {
|
|
371
|
+
values.push(k)
|
|
372
|
+
k.valuePointer = new ValuePointer(context, 0, 0, 0, 0)
|
|
373
|
+
|
|
374
|
+
if (minValue === -1 || minValue < k.value.byteLength) {
|
|
375
|
+
minValue = k.value.byteLength
|
|
376
|
+
}
|
|
320
377
|
}
|
|
321
378
|
|
|
322
|
-
k.changed = false
|
|
323
379
|
update.keys.push(k)
|
|
380
|
+
update.size += getEstimatedKeySize(k)
|
|
324
381
|
}
|
|
325
382
|
|
|
326
|
-
let
|
|
383
|
+
for (let i = 0; i < children.entries.length; i++) {
|
|
384
|
+
const c = children.entries[i]
|
|
385
|
+
if (!c.value || !c.changed) continue
|
|
386
|
+
children.touch(i)
|
|
387
|
+
stack.push(c)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
327
390
|
|
|
328
|
-
|
|
329
|
-
const n = node.value.children[i]
|
|
391
|
+
if (this.type === TYPE_COMPAT) await toCompatType(context, batch, this.ops)
|
|
330
392
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
393
|
+
const length = context.core.length
|
|
394
|
+
|
|
395
|
+
// if noop and not genesis, bail early
|
|
396
|
+
if (this.applied === 0 && length > 0) return
|
|
397
|
+
|
|
398
|
+
if (minValue > -1 && minValue + update.size < this.preferredBlockSize) {
|
|
399
|
+
// TODO: repack the value into the block
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (values.length) {
|
|
403
|
+
update = { size: 0, nodes: [], keys: [], values: [] }
|
|
404
|
+
|
|
405
|
+
for (let i = 0; i < values.length; i++) {
|
|
406
|
+
const k = values[i]
|
|
336
407
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const update = { node: [], keys: [] }
|
|
408
|
+
update.size += getEstimatedValueSize(k)
|
|
409
|
+
update.values.push(k)
|
|
410
|
+
|
|
411
|
+
if (i === values.length - 1 || update.size >= this.preferredBlockSize) {
|
|
342
412
|
batch.push(update)
|
|
343
|
-
|
|
413
|
+
update = { size: 0, nodes: [], keys: [], values: [] }
|
|
344
414
|
}
|
|
345
415
|
}
|
|
346
416
|
}
|
|
347
417
|
|
|
348
|
-
// if only the root was marked dirty and is === current bootstrap the batch is a noop - skip
|
|
349
|
-
if (update.node.length === 1 && update.keys.length === 0) {
|
|
350
|
-
const b = await this.tree.bootstrap()
|
|
351
|
-
const n = update.node[0]
|
|
352
|
-
if (b && b.context === n.context && b.core === n.core && b.seq === n.seq) return
|
|
353
|
-
if (!b && n.value.isEmpty()) return
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const length = context.core.length
|
|
357
418
|
const blocks = new Array(batch.length)
|
|
358
419
|
|
|
359
420
|
for (let i = 0; i < batch.length; i++) {
|
|
@@ -361,33 +422,63 @@ module.exports = class WriteBatch {
|
|
|
361
422
|
const seq = length + batch.length - i - 1
|
|
362
423
|
|
|
363
424
|
const block = {
|
|
364
|
-
type:
|
|
425
|
+
type: this.type,
|
|
365
426
|
checkpoint: 0,
|
|
366
427
|
batch: { start: batch.length - 1 - i, end: i },
|
|
367
428
|
previous: null,
|
|
429
|
+
metadata: null,
|
|
368
430
|
tree: null,
|
|
369
|
-
|
|
370
|
-
|
|
431
|
+
keys: null,
|
|
432
|
+
values: null,
|
|
433
|
+
cohorts: null
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
for (const k of update.values) {
|
|
437
|
+
if (block.values === null) block.values = []
|
|
438
|
+
const ptr = k.valuePointer
|
|
439
|
+
|
|
440
|
+
ptr.core = 0
|
|
441
|
+
ptr.context = context
|
|
442
|
+
ptr.seq = seq
|
|
443
|
+
ptr.offset = block.values.length
|
|
444
|
+
ptr.split = 0
|
|
445
|
+
|
|
446
|
+
block.values.push(k.value)
|
|
447
|
+
k.value = null // unlinked
|
|
371
448
|
}
|
|
372
449
|
|
|
373
450
|
for (const k of update.keys) {
|
|
374
|
-
if (block.
|
|
451
|
+
if (block.keys === null) block.keys = []
|
|
375
452
|
|
|
376
453
|
k.core = 0
|
|
377
454
|
k.context = context
|
|
378
455
|
k.seq = seq
|
|
379
|
-
k.offset = block.
|
|
380
|
-
|
|
456
|
+
k.offset = block.keys.length
|
|
457
|
+
k.changed = false
|
|
458
|
+
|
|
459
|
+
if (k.valuePointer) updateValuePointerContext(k.valuePointer, context)
|
|
460
|
+
|
|
461
|
+
block.keys.push(k)
|
|
381
462
|
}
|
|
382
463
|
|
|
383
|
-
for (const n of update.
|
|
464
|
+
for (const n of update.nodes) {
|
|
384
465
|
if (block.tree === null) block.tree = []
|
|
385
466
|
|
|
386
467
|
n.core = 0
|
|
387
468
|
n.context = context
|
|
388
469
|
n.seq = seq
|
|
389
470
|
n.offset = block.tree.length
|
|
390
|
-
|
|
471
|
+
n.changed = false
|
|
472
|
+
|
|
473
|
+
const treeDelta = {
|
|
474
|
+
keys: n.value.keys.flush(this.deltaMax, this.deltaMin),
|
|
475
|
+
children: n.value.children.flush(this.deltaMax, this.deltaMin)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
prepareCohorts(context, block, seq, treeDelta.keys, supportsCompression)
|
|
479
|
+
prepareCohorts(context, block, seq, treeDelta.children, false)
|
|
480
|
+
|
|
481
|
+
block.tree.push(treeDelta)
|
|
391
482
|
}
|
|
392
483
|
|
|
393
484
|
blocks[seq - length] = block
|
|
@@ -396,22 +487,19 @@ module.exports = class WriteBatch {
|
|
|
396
487
|
const buffers = new Array(blocks.length)
|
|
397
488
|
|
|
398
489
|
if (blocks.length > 0 && this.length > 0) {
|
|
399
|
-
const core = this.key
|
|
400
|
-
? await context.getCoreOffsetByKey(this.key, this.tree.activeRequests)
|
|
401
|
-
: 0
|
|
490
|
+
const core = this.key ? await context.getCoreOffsetByKey(this.key, activeRequests) : 0
|
|
402
491
|
blocks[blocks.length - 1].previous = { core, seq: this.length - 1 }
|
|
403
492
|
}
|
|
404
493
|
|
|
405
494
|
// TODO: make this transaction safe
|
|
406
495
|
if (context.changed) {
|
|
407
|
-
context.changed = false
|
|
408
496
|
context.checkpoint = context.core.length + blocks.length
|
|
409
|
-
blocks[blocks.length - 1].
|
|
497
|
+
blocks[blocks.length - 1].metadata = context.flush()
|
|
410
498
|
}
|
|
411
499
|
|
|
412
500
|
for (let i = 0; i < blocks.length; i++) {
|
|
413
501
|
blocks[i].checkpoint = context.checkpoint
|
|
414
|
-
buffers[i] =
|
|
502
|
+
buffers[i] = encodeBlock(blocks[i])
|
|
415
503
|
}
|
|
416
504
|
|
|
417
505
|
if (this.closed) {
|
|
@@ -423,9 +511,90 @@ module.exports = class WriteBatch {
|
|
|
423
511
|
for (let i = 0; i < batch.length; i++) {
|
|
424
512
|
const update = batch[i]
|
|
425
513
|
|
|
426
|
-
for (let j = 0; j < update.
|
|
427
|
-
|
|
514
|
+
for (let j = 0; j < update.nodes.length; j++) {
|
|
515
|
+
const node = update.nodes[j]
|
|
516
|
+
this.snapshot.bump(node)
|
|
428
517
|
}
|
|
429
518
|
}
|
|
430
519
|
}
|
|
431
520
|
}
|
|
521
|
+
|
|
522
|
+
async function toCompatType(context, batch, ops) {
|
|
523
|
+
const map = new Map()
|
|
524
|
+
let index = 0
|
|
525
|
+
|
|
526
|
+
for (const k of batch[0].keys) {
|
|
527
|
+
map.set(b4a.toString(k.key, 'hex'), k)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
531
|
+
const op = ops[i]
|
|
532
|
+
if (!op.put && !op.applied) continue
|
|
533
|
+
|
|
534
|
+
const k = map.get(b4a.toString(op.key, 'hex'))
|
|
535
|
+
|
|
536
|
+
const j = index++
|
|
537
|
+
if (j === batch.length) batch.push({ size: 0, nodes: [], keys: [], values: [] })
|
|
538
|
+
batch[j].keys = [k || new KeyPointer(context, 0, 0, 0, false, op.key, op.value, null)]
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// compat doesnt support block 0
|
|
542
|
+
if (context.core.length > 0) return
|
|
543
|
+
|
|
544
|
+
const header = b4a.from('0a086879706572626565', 'hex')
|
|
545
|
+
await context.core.append(header)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function updateValuePointerContext(valuePointer, context) {
|
|
549
|
+
valuePointer.core = context.getCoreOffsetLocal(valuePointer.context, valuePointer.core)
|
|
550
|
+
valuePointer.context = context
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// TODO: this isnt right anymore as we compress the delta post flush, but prob ok...
|
|
554
|
+
function getEstimatedNodeSize(n) {
|
|
555
|
+
return (
|
|
556
|
+
n.keys.delta.length * ESTIMATED_POINTER_SIZE + n.children.delta.length * ESTIMATED_POINTER_SIZE
|
|
557
|
+
)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function getEstimatedKeySize(k) {
|
|
561
|
+
return k.key.byteLength + (k.valuePointer ? ESTIMATED_POINTER_SIZE : 0)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function getEstimatedValueSize(k) {
|
|
565
|
+
return k.value.byteLength
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function prepareCohorts(context, block, seq, deltas, keys) {
|
|
569
|
+
for (const d of deltas) {
|
|
570
|
+
// same below but the delta might be a noop (ie add and the delete) - handle that
|
|
571
|
+
if (keys && d.changed && d.pointer && d.pointer.changed) d.pointer = null
|
|
572
|
+
|
|
573
|
+
if (d.pointer && d.pointer.context !== context) {
|
|
574
|
+
const p = d.pointer
|
|
575
|
+
p.core = context.getCoreOffsetLocal(p.context, p.core)
|
|
576
|
+
p.context = context
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (d.type !== OP_COHORT || !d.changed) continue
|
|
580
|
+
if (block.cohorts === null) block.cohorts = []
|
|
581
|
+
|
|
582
|
+
d.changed = false
|
|
583
|
+
d.pointer = new Pointer(context, 0, seq, block.cohorts.length)
|
|
584
|
+
|
|
585
|
+
for (const dd of d.deltas) {
|
|
586
|
+
if (keys && dd.changed && dd.pointer && dd.pointer.changed) dd.pointer = null
|
|
587
|
+
dd.changed = false
|
|
588
|
+
const p = dd.pointer
|
|
589
|
+
if (!p) continue
|
|
590
|
+
p.core = context.getCoreOffsetLocal(p.context, p.core)
|
|
591
|
+
p.context = context
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
block.cohorts.push(d.deltas)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function supportsCompression(type) {
|
|
599
|
+
return type !== TYPE_COMPAT && type !== 0
|
|
600
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperbee2",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "btree",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
12
|
"format": "prettier . --write",
|
|
13
|
-
"
|
|
13
|
+
"lint": "lunte && prettier . --check",
|
|
14
|
+
"test": "node test/all.js",
|
|
14
15
|
"test:bare": "bare test/all.js",
|
|
15
16
|
"test:generate": "brittle -r test/all.js test/*.js"
|
|
16
17
|
},
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"brittle": "^3.18.0",
|
|
28
29
|
"corestore": "^7.4.5",
|
|
30
|
+
"lunte": "^1.4.0",
|
|
29
31
|
"prettier": "^3.6.2",
|
|
30
32
|
"prettier-config-holepunch": "^2.0.0"
|
|
31
33
|
},
|