hyperbee2 1.2.0 → 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 +156 -22
- package/lib/cache.js +7 -0
- package/lib/compat.js +47 -43
- package/lib/compression.js +163 -0
- package/lib/context.js +38 -9
- package/lib/diff.js +20 -10
- package/lib/encoding.js +132 -7
- package/lib/ranges.js +10 -5
- package/lib/tree.js +97 -48
- package/lib/write.js +239 -98
- package/package.json +4 -2
- package/spec/hyperschema/index.js +356 -47
- package/spec/hyperschema/schema.json +208 -5
package/lib/write.js
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
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 {
|
|
5
|
-
|
|
6
|
+
Pointer,
|
|
7
|
+
KeyPointer,
|
|
8
|
+
ValuePointer,
|
|
6
9
|
TreeNode,
|
|
7
10
|
TreeNodePointer,
|
|
8
11
|
MIN_KEYS,
|
|
9
|
-
|
|
10
|
-
CHANGED,
|
|
12
|
+
INSERTED,
|
|
11
13
|
NEEDS_SPLIT
|
|
12
14
|
} = require('./tree.js')
|
|
13
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
|
+
|
|
14
20
|
module.exports = class WriteBatch {
|
|
15
|
-
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
|
+
|
|
16
34
|
this.tree = tree
|
|
35
|
+
this.deltaMax = deltaMax
|
|
36
|
+
this.deltaMin = deltaMin
|
|
37
|
+
this.inlineValueSize = inlineValueSize
|
|
38
|
+
this.preferredBlockSize = preferredBlockSize
|
|
17
39
|
this.snapshot = tree.snapshot()
|
|
18
40
|
this.autoUpdate = autoUpdate
|
|
19
41
|
this.length = length
|
|
20
42
|
this.key = key
|
|
21
|
-
this.
|
|
43
|
+
this.type = type
|
|
22
44
|
this.closed = false
|
|
23
45
|
this.applied = 0
|
|
24
46
|
this.root = null
|
|
@@ -62,16 +84,10 @@ module.exports = class WriteBatch {
|
|
|
62
84
|
|
|
63
85
|
const changed = length === 0
|
|
64
86
|
const seq = length === 0 ? 0 : length - 1
|
|
87
|
+
const value = changed ? new TreeNode([], []) : null
|
|
65
88
|
|
|
66
89
|
this.length = length
|
|
67
|
-
this.root = new TreeNodePointer(
|
|
68
|
-
context,
|
|
69
|
-
0,
|
|
70
|
-
seq,
|
|
71
|
-
0,
|
|
72
|
-
changed,
|
|
73
|
-
changed ? new TreeNode([], []) : null
|
|
74
|
-
)
|
|
90
|
+
this.root = new TreeNodePointer(context, 0, seq, 0, changed, value)
|
|
75
91
|
|
|
76
92
|
for (const op of ops) {
|
|
77
93
|
if (op.put) op.applied = await this._put(op.key, op.value)
|
|
@@ -113,12 +129,13 @@ module.exports = class WriteBatch {
|
|
|
113
129
|
|
|
114
130
|
while (s < e) {
|
|
115
131
|
const mid = (s + e) >> 1
|
|
116
|
-
const m = v.keys
|
|
132
|
+
const m = v.keys.get(mid)
|
|
117
133
|
|
|
118
134
|
c = b4a.compare(target, m.key)
|
|
119
135
|
|
|
120
136
|
if (c === 0) {
|
|
121
|
-
|
|
137
|
+
const existing = await this.snapshot.inflateValue(m)
|
|
138
|
+
if (b4a.equals(existing, value)) return false
|
|
122
139
|
v.setValue(this.tree.context, mid, value)
|
|
123
140
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
124
141
|
return true
|
|
@@ -129,16 +146,21 @@ module.exports = class WriteBatch {
|
|
|
129
146
|
}
|
|
130
147
|
|
|
131
148
|
const i = c < 0 ? e : s
|
|
132
|
-
ptr = v.children
|
|
149
|
+
ptr = v.children.get(i)
|
|
133
150
|
}
|
|
134
151
|
|
|
135
152
|
const v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
136
|
-
let status = v.
|
|
137
|
-
|
|
138
|
-
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
|
+
}
|
|
139
162
|
|
|
140
163
|
ptr.changed = true
|
|
141
|
-
|
|
142
164
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
143
165
|
|
|
144
166
|
while (status === NEEDS_SPLIT) {
|
|
@@ -148,14 +170,15 @@ module.exports = class WriteBatch {
|
|
|
148
170
|
|
|
149
171
|
if (parent) {
|
|
150
172
|
const p = parent.value ? this.snapshot.bump(parent) : await this.snapshot.inflate(parent)
|
|
151
|
-
status = p.
|
|
173
|
+
status = p.insertNode(this.tree.context, median, right)
|
|
152
174
|
ptr = parent
|
|
153
175
|
} else {
|
|
154
176
|
this.root = new TreeNodePointer(this.tree.context, 0, 0, 0, true, new TreeNode([], []))
|
|
155
177
|
this.root.value.keys.push(median)
|
|
156
|
-
this.root.value.children.push(ptr
|
|
178
|
+
this.root.value.children.push(ptr)
|
|
179
|
+
this.root.value.children.push(right)
|
|
157
180
|
this.snapshot.bump(this.root)
|
|
158
|
-
status =
|
|
181
|
+
status = INSERTED
|
|
159
182
|
}
|
|
160
183
|
}
|
|
161
184
|
|
|
@@ -177,12 +200,11 @@ module.exports = class WriteBatch {
|
|
|
177
200
|
|
|
178
201
|
while (s < e) {
|
|
179
202
|
const mid = (s + e) >> 1
|
|
180
|
-
c = b4a.compare(key, v.keys
|
|
203
|
+
c = b4a.compare(key, v.keys.get(mid).key)
|
|
181
204
|
|
|
182
205
|
if (c === 0) {
|
|
183
206
|
if (v.children.length) await this._setKeyToNearestLeaf(v, mid, stack)
|
|
184
207
|
else v.removeKey(mid)
|
|
185
|
-
|
|
186
208
|
// we mark these as changed late, so we don't rewrite them if it is a 404
|
|
187
209
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
188
210
|
this.root = await this._rebalance(stack)
|
|
@@ -196,15 +218,15 @@ module.exports = class WriteBatch {
|
|
|
196
218
|
if (!v.children.length) return false
|
|
197
219
|
|
|
198
220
|
const i = c < 0 ? e : s
|
|
199
|
-
ptr = v.children
|
|
221
|
+
ptr = v.children.get(i)
|
|
200
222
|
}
|
|
201
223
|
|
|
202
224
|
return false
|
|
203
225
|
}
|
|
204
226
|
|
|
205
227
|
async _setKeyToNearestLeaf(v, index, stack) {
|
|
206
|
-
let left = v.children
|
|
207
|
-
let right = v.children
|
|
228
|
+
let left = v.children.get(index)
|
|
229
|
+
let right = v.children.get(index + 1)
|
|
208
230
|
|
|
209
231
|
const [ls, rs] = await Promise.all([this._leafSize(left, false), this._leafSize(right, true)])
|
|
210
232
|
|
|
@@ -213,28 +235,28 @@ module.exports = class WriteBatch {
|
|
|
213
235
|
stack.push(right)
|
|
214
236
|
let r = right.value ? this.snapshot.bump(right) : await this.snapshot.inflate(right)
|
|
215
237
|
while (r.children.length) {
|
|
216
|
-
right = r.children
|
|
238
|
+
right = r.children.get(0)
|
|
217
239
|
stack.push(right)
|
|
218
240
|
r = right.value ? this.snapshot.bump(right) : await this.snapshot.inflate(right)
|
|
219
241
|
}
|
|
220
|
-
v.keys
|
|
242
|
+
v.keys.set(index, r.keys.shift())
|
|
221
243
|
} else {
|
|
222
244
|
// if fewer leaves on the right
|
|
223
245
|
stack.push(left)
|
|
224
246
|
let l = left.value ? this.snapshot.bump(left) : await this.snapshot.inflate(left)
|
|
225
247
|
while (l.children.length) {
|
|
226
|
-
left = l.children
|
|
248
|
+
left = l.children.get(l.children.length - 1)
|
|
227
249
|
stack.push(left)
|
|
228
250
|
l = left.value ? this.snapshot.bump(left) : await this.snapshot.inflate(left)
|
|
229
251
|
}
|
|
230
|
-
v.keys
|
|
252
|
+
v.keys.set(index, l.keys.pop())
|
|
231
253
|
}
|
|
232
254
|
}
|
|
233
255
|
|
|
234
256
|
async _leafSize(ptr, goLeft) {
|
|
235
257
|
let v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
236
258
|
while (v.children.length) {
|
|
237
|
-
ptr = v.children
|
|
259
|
+
ptr = v.children.get(goLeft ? 0 : v.children.length - 1)
|
|
238
260
|
v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
239
261
|
}
|
|
240
262
|
return v.keys.length
|
|
@@ -242,6 +264,7 @@ module.exports = class WriteBatch {
|
|
|
242
264
|
|
|
243
265
|
async _rebalance(stack) {
|
|
244
266
|
const root = stack[0]
|
|
267
|
+
const minKeys = this.tree.context.minKeys
|
|
245
268
|
|
|
246
269
|
while (stack.length > 1) {
|
|
247
270
|
const ptr = stack.pop()
|
|
@@ -258,11 +281,11 @@ module.exports = class WriteBatch {
|
|
|
258
281
|
let l = left && (left.value ? this.snapshot.bump(left) : await this.snapshot.inflate(left))
|
|
259
282
|
|
|
260
283
|
// maybe borrow from left sibling?
|
|
261
|
-
if (l && l.keys.length >
|
|
284
|
+
if (l && l.keys.length > minKeys) {
|
|
262
285
|
left.changed = true
|
|
263
|
-
v.keys.unshift(p.keys
|
|
286
|
+
v.keys.unshift(p.keys.get(index - 1))
|
|
264
287
|
if (l.children.length) v.children.unshift(l.children.pop())
|
|
265
|
-
p.keys
|
|
288
|
+
p.keys.set(index - 1, l.keys.pop())
|
|
266
289
|
return root
|
|
267
290
|
}
|
|
268
291
|
|
|
@@ -270,11 +293,11 @@ module.exports = class WriteBatch {
|
|
|
270
293
|
right && (right.value ? this.snapshot.bump(right) : await this.snapshot.inflate(right))
|
|
271
294
|
|
|
272
295
|
// maybe borrow from right sibling?
|
|
273
|
-
if (r && r.keys.length >
|
|
296
|
+
if (r && r.keys.length > minKeys) {
|
|
274
297
|
right.changed = true
|
|
275
|
-
v.keys.push(p.keys
|
|
298
|
+
v.keys.push(p.keys.get(index))
|
|
276
299
|
if (r.children.length) v.children.push(r.children.shift())
|
|
277
|
-
p.keys
|
|
300
|
+
p.keys.set(index, r.keys.shift())
|
|
278
301
|
return root
|
|
279
302
|
}
|
|
280
303
|
|
|
@@ -289,7 +312,7 @@ module.exports = class WriteBatch {
|
|
|
289
312
|
}
|
|
290
313
|
|
|
291
314
|
left.changed = true
|
|
292
|
-
l.merge(r, p.keys
|
|
315
|
+
l.merge(r, p.keys.get(index))
|
|
293
316
|
|
|
294
317
|
parent.changed = true
|
|
295
318
|
p.removeKey(index)
|
|
@@ -297,69 +320,100 @@ module.exports = class WriteBatch {
|
|
|
297
320
|
|
|
298
321
|
const r = root.value ? this.snapshot.bump(root) : await this.snapshot.inflate(root)
|
|
299
322
|
// check if the tree shrunk
|
|
300
|
-
if (!r.keys.length && r.children.length) return r.children
|
|
323
|
+
if (!r.keys.length && r.children.length) return r.children.get(0)
|
|
301
324
|
return root
|
|
302
325
|
}
|
|
303
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
|
+
|
|
304
333
|
async _flush() {
|
|
305
|
-
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
|
|
306
342
|
|
|
307
|
-
const update = { node: [], keys: [] }
|
|
308
343
|
const batch = [update]
|
|
309
|
-
const stack = [
|
|
344
|
+
const stack = [this.root]
|
|
345
|
+
const values = []
|
|
346
|
+
|
|
310
347
|
const context = this.tree.context.getLocalContext()
|
|
348
|
+
const activeRequests = this.tree.activeRequests
|
|
311
349
|
|
|
312
|
-
await context.update(
|
|
350
|
+
await context.update(activeRequests)
|
|
313
351
|
|
|
314
352
|
while (stack.length > 0) {
|
|
315
|
-
const
|
|
353
|
+
const node = stack.pop()
|
|
316
354
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const k = node.value.keys[i]
|
|
355
|
+
if (this.type !== TYPE_COMPAT && update.size >= this.preferredBlockSize) {
|
|
356
|
+
update = { size: 0, nodes: [], keys: [], values: [] }
|
|
357
|
+
batch.push(update)
|
|
358
|
+
}
|
|
322
359
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
k.context = context
|
|
326
|
-
continue
|
|
327
|
-
}
|
|
360
|
+
update.nodes.push(node)
|
|
361
|
+
update.size += getEstimatedNodeSize(node.value)
|
|
328
362
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
363
|
+
const keys = node.value.keys
|
|
364
|
+
const children = node.value.children
|
|
332
365
|
|
|
333
|
-
let
|
|
366
|
+
for (let i = 0; i < keys.entries.length; i++) {
|
|
367
|
+
const k = keys.entries[i]
|
|
368
|
+
if (!k.changed) continue
|
|
334
369
|
|
|
335
|
-
|
|
336
|
-
|
|
370
|
+
if (!this._shouldInlineValue(k)) {
|
|
371
|
+
values.push(k)
|
|
372
|
+
k.valuePointer = new ValuePointer(context, 0, 0, 0, 0)
|
|
337
373
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
continue
|
|
374
|
+
if (minValue === -1 || minValue < k.value.byteLength) {
|
|
375
|
+
minValue = k.value.byteLength
|
|
376
|
+
}
|
|
342
377
|
}
|
|
343
378
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
379
|
+
update.keys.push(k)
|
|
380
|
+
update.size += getEstimatedKeySize(k)
|
|
381
|
+
}
|
|
382
|
+
|
|
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)
|
|
352
388
|
}
|
|
353
389
|
}
|
|
354
390
|
|
|
391
|
+
if (this.type === TYPE_COMPAT) await toCompatType(context, batch, this.ops)
|
|
392
|
+
|
|
355
393
|
const length = context.core.length
|
|
356
394
|
|
|
357
395
|
// if noop and not genesis, bail early
|
|
358
|
-
if (this.applied === 0 && length > 0)
|
|
359
|
-
|
|
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
|
|
360
400
|
}
|
|
361
401
|
|
|
362
|
-
if (
|
|
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]
|
|
407
|
+
|
|
408
|
+
update.size += getEstimatedValueSize(k)
|
|
409
|
+
update.values.push(k)
|
|
410
|
+
|
|
411
|
+
if (i === values.length - 1 || update.size >= this.preferredBlockSize) {
|
|
412
|
+
batch.push(update)
|
|
413
|
+
update = { size: 0, nodes: [], keys: [], values: [] }
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
363
417
|
|
|
364
418
|
const blocks = new Array(batch.length)
|
|
365
419
|
|
|
@@ -368,33 +422,63 @@ module.exports = class WriteBatch {
|
|
|
368
422
|
const seq = length + batch.length - i - 1
|
|
369
423
|
|
|
370
424
|
const block = {
|
|
371
|
-
type:
|
|
425
|
+
type: this.type,
|
|
372
426
|
checkpoint: 0,
|
|
373
427
|
batch: { start: batch.length - 1 - i, end: i },
|
|
374
428
|
previous: null,
|
|
429
|
+
metadata: null,
|
|
375
430
|
tree: null,
|
|
376
|
-
|
|
377
|
-
|
|
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
|
|
378
448
|
}
|
|
379
449
|
|
|
380
450
|
for (const k of update.keys) {
|
|
381
|
-
if (block.
|
|
451
|
+
if (block.keys === null) block.keys = []
|
|
382
452
|
|
|
383
453
|
k.core = 0
|
|
384
454
|
k.context = context
|
|
385
455
|
k.seq = seq
|
|
386
|
-
k.offset = block.
|
|
387
|
-
|
|
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)
|
|
388
462
|
}
|
|
389
463
|
|
|
390
|
-
for (const n of update.
|
|
464
|
+
for (const n of update.nodes) {
|
|
391
465
|
if (block.tree === null) block.tree = []
|
|
392
466
|
|
|
393
467
|
n.core = 0
|
|
394
468
|
n.context = context
|
|
395
469
|
n.seq = seq
|
|
396
470
|
n.offset = block.tree.length
|
|
397
|
-
|
|
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)
|
|
398
482
|
}
|
|
399
483
|
|
|
400
484
|
blocks[seq - length] = block
|
|
@@ -403,22 +487,19 @@ module.exports = class WriteBatch {
|
|
|
403
487
|
const buffers = new Array(blocks.length)
|
|
404
488
|
|
|
405
489
|
if (blocks.length > 0 && this.length > 0) {
|
|
406
|
-
const core = this.key
|
|
407
|
-
? await context.getCoreOffsetByKey(this.key, this.tree.activeRequests)
|
|
408
|
-
: 0
|
|
490
|
+
const core = this.key ? await context.getCoreOffsetByKey(this.key, activeRequests) : 0
|
|
409
491
|
blocks[blocks.length - 1].previous = { core, seq: this.length - 1 }
|
|
410
492
|
}
|
|
411
493
|
|
|
412
494
|
// TODO: make this transaction safe
|
|
413
495
|
if (context.changed) {
|
|
414
|
-
context.changed = false
|
|
415
496
|
context.checkpoint = context.core.length + blocks.length
|
|
416
|
-
blocks[blocks.length - 1].
|
|
497
|
+
blocks[blocks.length - 1].metadata = context.flush()
|
|
417
498
|
}
|
|
418
499
|
|
|
419
500
|
for (let i = 0; i < blocks.length; i++) {
|
|
420
501
|
blocks[i].checkpoint = context.checkpoint
|
|
421
|
-
buffers[i] = encodeBlock(blocks[i]
|
|
502
|
+
buffers[i] = encodeBlock(blocks[i])
|
|
422
503
|
}
|
|
423
504
|
|
|
424
505
|
if (this.closed) {
|
|
@@ -430,14 +511,15 @@ module.exports = class WriteBatch {
|
|
|
430
511
|
for (let i = 0; i < batch.length; i++) {
|
|
431
512
|
const update = batch[i]
|
|
432
513
|
|
|
433
|
-
for (let j = 0; j < update.
|
|
434
|
-
|
|
514
|
+
for (let j = 0; j < update.nodes.length; j++) {
|
|
515
|
+
const node = update.nodes[j]
|
|
516
|
+
this.snapshot.bump(node)
|
|
435
517
|
}
|
|
436
518
|
}
|
|
437
519
|
}
|
|
438
520
|
}
|
|
439
521
|
|
|
440
|
-
function
|
|
522
|
+
async function toCompatType(context, batch, ops) {
|
|
441
523
|
const map = new Map()
|
|
442
524
|
let index = 0
|
|
443
525
|
|
|
@@ -450,10 +532,69 @@ function toBlockFormat2(context, batch, ops) {
|
|
|
450
532
|
if (!op.put && !op.applied) continue
|
|
451
533
|
|
|
452
534
|
const k = map.get(b4a.toString(op.key, 'hex'))
|
|
535
|
+
|
|
453
536
|
const j = index++
|
|
454
|
-
if (j === batch.length) batch.push({
|
|
455
|
-
batch[j].keys = [k || new
|
|
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)]
|
|
456
539
|
}
|
|
457
540
|
|
|
458
|
-
|
|
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
|
|
459
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
|
},
|