hyperbee2 1.0.0 → 1.1.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 +3 -2
- package/lib/context.js +10 -5
- package/lib/tree.js +15 -2
- package/lib/write.js +59 -39
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -11,8 +11,9 @@ class Hyperbee {
|
|
|
11
11
|
constructor(store, options = {}) {
|
|
12
12
|
const {
|
|
13
13
|
key = null,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
encryption = null,
|
|
15
|
+
core = key ? store.get(key) : store.get({ key, name: 'bee', encryption }),
|
|
16
|
+
context = new CoreContext(store, core, core, encryption),
|
|
16
17
|
maxCacheSize = 4096,
|
|
17
18
|
cache = new NodeCache(maxCacheSize),
|
|
18
19
|
root = null,
|
package/lib/context.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
|
+
const ScopeLock = require('scope-lock')
|
|
2
3
|
const { decodeBlock } = require('./encoding.js')
|
|
3
4
|
|
|
4
5
|
class CoreContext {
|
|
5
|
-
constructor(store, local, core, other = new Map()) {
|
|
6
|
+
constructor(store, local, core, encryption, lock = new ScopeLock(), other = new Map()) {
|
|
6
7
|
this.store = store
|
|
7
8
|
this.local = local
|
|
8
9
|
this.core = core
|
|
10
|
+
this.encryption = encryption
|
|
11
|
+
this.lock = lock
|
|
9
12
|
this.other = other
|
|
10
13
|
this.length = 0
|
|
11
14
|
this.checkpoint = 0
|
|
@@ -43,7 +46,7 @@ class CoreContext {
|
|
|
43
46
|
|
|
44
47
|
async getCoreOffset(context, core, activeRequests) {
|
|
45
48
|
if (core !== 0 && core - 1 >= context.cores.length) await context.update(activeRequests)
|
|
46
|
-
const key = core === 0 ? context.core.key : context.cores[core - 1]
|
|
49
|
+
const key = core === 0 ? context.core.key : context.cores[core - 1].key
|
|
47
50
|
|
|
48
51
|
if (b4a.equals(key, this.core.key)) return 0
|
|
49
52
|
|
|
@@ -90,7 +93,8 @@ class CoreContext {
|
|
|
90
93
|
if (index === 0) return this.core
|
|
91
94
|
if (index > this.cores.length) throw new Error('Bad core index: ' + index)
|
|
92
95
|
if (this.opened[index - 1] === null) {
|
|
93
|
-
|
|
96
|
+
const key = this.cores[index - 1].key
|
|
97
|
+
this.opened[index - 1] = this.store.get({ key, encryption: this.encryption })
|
|
94
98
|
}
|
|
95
99
|
return this.opened[index - 1]
|
|
96
100
|
}
|
|
@@ -113,7 +117,8 @@ class CoreContext {
|
|
|
113
117
|
const hex = b4a.toString(key, 'hex')
|
|
114
118
|
if (this.other.has(hex)) return this.other.get(hex)
|
|
115
119
|
|
|
116
|
-
const
|
|
120
|
+
const hc = this.store.get({ key, encryption: this.encryption })
|
|
121
|
+
const ctx = new CoreContext(this.store, this.local, hc, this.encryption, this.lock, this.other)
|
|
117
122
|
this.other.set(hex, ctx)
|
|
118
123
|
return ctx
|
|
119
124
|
}
|
|
@@ -127,7 +132,7 @@ class CoreContext {
|
|
|
127
132
|
if (this.other.has(hex)) return this.other.get(hex)
|
|
128
133
|
|
|
129
134
|
const hc = this.getCore(core)
|
|
130
|
-
const ctx = new CoreContext(this.store, this.local, hc)
|
|
135
|
+
const ctx = new CoreContext(this.store, this.local, hc, this.encryption, this.lock, this.other)
|
|
131
136
|
this.other.set(hex, ctx)
|
|
132
137
|
return ctx
|
|
133
138
|
}
|
package/lib/tree.js
CHANGED
|
@@ -4,6 +4,10 @@ const T = 5
|
|
|
4
4
|
const MIN_KEYS = T - 1
|
|
5
5
|
const MAX_CHILDREN = MIN_KEYS * 2 + 1
|
|
6
6
|
|
|
7
|
+
const UNCHANGED = 0
|
|
8
|
+
const CHANGED = 1
|
|
9
|
+
const NEEDS_SPLIT = 2
|
|
10
|
+
|
|
7
11
|
class DataPointer {
|
|
8
12
|
constructor(context, core, seq, offset, changed, key, value) {
|
|
9
13
|
this.context = context
|
|
@@ -40,6 +44,10 @@ class TreeNode {
|
|
|
40
44
|
this.children = children
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
isEmpty() {
|
|
48
|
+
return this.keys.length === 0 && this.children.length === 0
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
put(context, key, value, child) {
|
|
44
52
|
let s = 0
|
|
45
53
|
let e = this.keys.length
|
|
@@ -52,8 +60,9 @@ class TreeNode {
|
|
|
52
60
|
c = b4a.compare(key, k.key)
|
|
53
61
|
|
|
54
62
|
if (c === 0) {
|
|
63
|
+
if (b4a.equals(k.value, value)) return UNCHANGED
|
|
55
64
|
this.keys[mid] = new DataPointer(context, 0, 0, 0, true, key, value)
|
|
56
|
-
return
|
|
65
|
+
return CHANGED
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
if (c < 0) e = mid
|
|
@@ -64,7 +73,7 @@ class TreeNode {
|
|
|
64
73
|
this.keys.splice(i, 0, new DataPointer(context, 0, 0, 0, true, key, value))
|
|
65
74
|
if (child) this.children.splice(i + 1, 0, child)
|
|
66
75
|
|
|
67
|
-
return this.keys.length < MAX_CHILDREN
|
|
76
|
+
return this.keys.length < MAX_CHILDREN ? CHANGED : NEEDS_SPLIT
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
setValue(context, i, value) {
|
|
@@ -123,6 +132,10 @@ exports.T = T
|
|
|
123
132
|
exports.MIN_KEYS = MIN_KEYS
|
|
124
133
|
exports.MAX_CHILDREN = MAX_CHILDREN
|
|
125
134
|
|
|
135
|
+
exports.UNCHANGED = UNCHANGED
|
|
136
|
+
exports.CHANGED = CHANGED
|
|
137
|
+
exports.NEEDS_SPLIT = NEEDS_SPLIT
|
|
138
|
+
|
|
126
139
|
exports.TreeNodePointer = TreeNodePointer
|
|
127
140
|
exports.TreeNode = TreeNode
|
|
128
141
|
exports.DataPointer = DataPointer
|
package/lib/write.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const c = require('compact-encoding')
|
|
3
|
-
const { TreeNode, TreeNodePointer, MIN_KEYS } = require('./tree.js')
|
|
4
3
|
const { Block } = require('./encoding.js')
|
|
4
|
+
const {
|
|
5
|
+
TreeNode,
|
|
6
|
+
TreeNodePointer,
|
|
7
|
+
MIN_KEYS,
|
|
8
|
+
UNCHANGED,
|
|
9
|
+
CHANGED,
|
|
10
|
+
NEEDS_SPLIT
|
|
11
|
+
} = require('./tree.js')
|
|
5
12
|
|
|
6
13
|
module.exports = class WriteBatch {
|
|
7
14
|
constructor(tree, { length = -1, key = null, autoUpdate = true } = {}) {
|
|
@@ -11,7 +18,6 @@ module.exports = class WriteBatch {
|
|
|
11
18
|
this.length = length
|
|
12
19
|
this.key = key
|
|
13
20
|
this.closed = false
|
|
14
|
-
this.flushing = false
|
|
15
21
|
this.root = null
|
|
16
22
|
this.ops = []
|
|
17
23
|
}
|
|
@@ -40,40 +46,44 @@ module.exports = class WriteBatch {
|
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
async flush() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
49
|
+
const lock = this.tree.context.lock
|
|
50
|
+
await lock.lock()
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const ops = this.ops
|
|
54
|
+
this.ops = []
|
|
55
|
+
|
|
56
|
+
const root = await this.tree.bootstrap()
|
|
57
|
+
|
|
58
|
+
const length = this._getLength(root)
|
|
59
|
+
const context = this._getContext(root)
|
|
60
|
+
|
|
61
|
+
const changed = length === 0
|
|
62
|
+
const seq = length === 0 ? 0 : length - 1
|
|
63
|
+
|
|
64
|
+
this.length = length
|
|
65
|
+
this.root = new TreeNodePointer(
|
|
66
|
+
context,
|
|
67
|
+
0,
|
|
68
|
+
seq,
|
|
69
|
+
0,
|
|
70
|
+
changed,
|
|
71
|
+
changed ? new TreeNode([], []) : null
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
for (const op of ops) {
|
|
75
|
+
if (op.put) await this._put(op.key, op.value)
|
|
76
|
+
else await this._delete(op.key)
|
|
77
|
+
}
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
await this._flush()
|
|
80
|
+
await this.snapshot.close()
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
if (this.autoUpdate) {
|
|
83
|
+
this.tree.update(this.root)
|
|
84
|
+
}
|
|
85
|
+
} finally {
|
|
86
|
+
lock.unlock()
|
|
77
87
|
}
|
|
78
88
|
}
|
|
79
89
|
|
|
@@ -105,7 +115,7 @@ module.exports = class WriteBatch {
|
|
|
105
115
|
c = b4a.compare(target, m.key)
|
|
106
116
|
|
|
107
117
|
if (c === 0) {
|
|
108
|
-
if (b4a.
|
|
118
|
+
if (b4a.equals(m.value, value)) return
|
|
109
119
|
v.setValue(this.tree.context, mid, value)
|
|
110
120
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
111
121
|
return
|
|
@@ -120,27 +130,29 @@ module.exports = class WriteBatch {
|
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
const v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
123
|
-
let
|
|
133
|
+
let status = v.put(this.tree.context, target, value, null)
|
|
134
|
+
|
|
135
|
+
if (status === UNCHANGED) return
|
|
124
136
|
|
|
125
137
|
ptr.changed = true
|
|
126
138
|
|
|
127
139
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
128
140
|
|
|
129
|
-
while (
|
|
141
|
+
while (status === NEEDS_SPLIT) {
|
|
130
142
|
const v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
131
143
|
const parent = stack.pop()
|
|
132
144
|
const { median, right } = v.split(this.tree.context)
|
|
133
145
|
|
|
134
146
|
if (parent) {
|
|
135
147
|
const p = parent.value ? this.snapshot.bump(parent) : await this.snapshot.inflate(parent)
|
|
136
|
-
|
|
148
|
+
status = p.put(this.tree.context, median.key, median.value, right)
|
|
137
149
|
ptr = parent
|
|
138
150
|
} else {
|
|
139
151
|
this.root = new TreeNodePointer(this.tree.context, 0, 0, 0, true, new TreeNode([], []))
|
|
140
152
|
this.root.value.keys.push(median)
|
|
141
153
|
this.root.value.children.push(ptr, right)
|
|
142
154
|
this.snapshot.bump(this.root)
|
|
143
|
-
|
|
155
|
+
status = UNCHANGED
|
|
144
156
|
}
|
|
145
157
|
}
|
|
146
158
|
}
|
|
@@ -333,6 +345,14 @@ module.exports = class WriteBatch {
|
|
|
333
345
|
}
|
|
334
346
|
}
|
|
335
347
|
|
|
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.isEmpty()) return
|
|
354
|
+
}
|
|
355
|
+
|
|
336
356
|
const length = context.core.length
|
|
337
357
|
const blocks = new Array(batch.length)
|
|
338
358
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperbee2",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "btree",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"hypercore": "^11.15.0",
|
|
21
21
|
"hyperschema": "^1.14.0",
|
|
22
22
|
"protocol-buffers-encodings": "^1.2.0",
|
|
23
|
+
"scope-lock": "^1.2.4",
|
|
23
24
|
"streamx": "^2.22.1"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|