hypercore-storage 3.1.2 → 3.2.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/README.md +11 -1
- package/index.js +54 -12
- package/lib/tx.js +67 -13
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -14,10 +14,20 @@ The following API is what Hypercore 11 binds to to do I/O.
|
|
|
14
14
|
const Storage = require('hypercore-storage')
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
#### `store = new Storage(dbOrPath)`
|
|
17
|
+
#### `store = new Storage(dbOrPath, opts = {})`
|
|
18
18
|
|
|
19
19
|
Make a new storage engine.
|
|
20
20
|
|
|
21
|
+
`opts` includes:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
{
|
|
25
|
+
treeCache: { // Same options as `xache`
|
|
26
|
+
maxSize: 8192 // Max number of tree nodes to cache
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
21
31
|
#### `core = await store.createCore({ key, discoveyKey, manifest?, keyPair?, encryptionKey?, userData? })`
|
|
22
32
|
|
|
23
33
|
Create a new core, returns a storage instance for that core.
|
package/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const ScopeLock = require('scope-lock')
|
|
|
4
4
|
const DeviceFile = require('device-file')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const fs = require('fs')
|
|
7
|
+
const Xache = require('xache')
|
|
7
8
|
|
|
8
9
|
const View = require('./lib/view.js')
|
|
9
10
|
|
|
@@ -84,7 +85,7 @@ class Atom {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
class HypercoreStorage {
|
|
87
|
-
constructor(store, db, core, view, atom) {
|
|
88
|
+
constructor(store, db, treeCache, core, view, atom) {
|
|
88
89
|
this.store = store
|
|
89
90
|
this.db = db
|
|
90
91
|
this.core = core
|
|
@@ -92,6 +93,7 @@ class HypercoreStorage {
|
|
|
92
93
|
this.atom = atom
|
|
93
94
|
|
|
94
95
|
this.view.readStart()
|
|
96
|
+
this.treeCache = treeCache
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
get readOnly() {
|
|
@@ -201,6 +203,7 @@ class HypercoreStorage {
|
|
|
201
203
|
return new HypercoreStorage(
|
|
202
204
|
this.store,
|
|
203
205
|
this.db.snapshot(),
|
|
206
|
+
this.treeCache,
|
|
204
207
|
this.core,
|
|
205
208
|
this.view.snapshot(),
|
|
206
209
|
this.atom
|
|
@@ -218,7 +221,15 @@ class HypercoreStorage {
|
|
|
218
221
|
if (this.atom && this.atom !== atom) {
|
|
219
222
|
throw new Error('Cannot atomize and atomized session with a new atom')
|
|
220
223
|
}
|
|
221
|
-
|
|
224
|
+
|
|
225
|
+
return new HypercoreStorage(
|
|
226
|
+
this.store,
|
|
227
|
+
this.db.session(),
|
|
228
|
+
this.treeCache,
|
|
229
|
+
this.core,
|
|
230
|
+
atom.view,
|
|
231
|
+
atom
|
|
232
|
+
)
|
|
222
233
|
}
|
|
223
234
|
|
|
224
235
|
createAtom() {
|
|
@@ -267,7 +278,7 @@ class HypercoreStorage {
|
|
|
267
278
|
dependencies: []
|
|
268
279
|
}
|
|
269
280
|
|
|
270
|
-
const coreRx = new CoreRX(core, this.db, this.view)
|
|
281
|
+
const coreRx = new CoreRX(core, this.db, this.view, this.treeCache, this.store.stats, -1)
|
|
271
282
|
|
|
272
283
|
const dependencyPromise = coreRx.getDependency()
|
|
273
284
|
coreRx.tryFlush()
|
|
@@ -278,6 +289,7 @@ class HypercoreStorage {
|
|
|
278
289
|
return new HypercoreStorage(
|
|
279
290
|
this.store,
|
|
280
291
|
this.db.session(),
|
|
292
|
+
this.treeCache,
|
|
281
293
|
core,
|
|
282
294
|
this.atom ? this.view : new View(),
|
|
283
295
|
this.atom
|
|
@@ -341,6 +353,7 @@ class HypercoreStorage {
|
|
|
341
353
|
return new HypercoreStorage(
|
|
342
354
|
this.store,
|
|
343
355
|
this.db.session(),
|
|
356
|
+
this.treeCache,
|
|
344
357
|
core,
|
|
345
358
|
this.atom ? this.view : new View(),
|
|
346
359
|
this.atom
|
|
@@ -412,8 +425,11 @@ class HypercoreStorage {
|
|
|
412
425
|
await tx.flush()
|
|
413
426
|
}
|
|
414
427
|
|
|
415
|
-
read() {
|
|
416
|
-
|
|
428
|
+
read(fork = -1) {
|
|
429
|
+
let treeCache = null
|
|
430
|
+
if (!this.atom) treeCache = this.treeCache
|
|
431
|
+
|
|
432
|
+
return new CoreRX(this.core, this.db, this.view, treeCache, this.store.stats, fork)
|
|
417
433
|
}
|
|
418
434
|
|
|
419
435
|
write() {
|
|
@@ -430,7 +446,7 @@ class HypercoreStorage {
|
|
|
430
446
|
}
|
|
431
447
|
|
|
432
448
|
static async export(ptr, db, { batches = false } = {}) {
|
|
433
|
-
const rx = new CoreRX(ptr, db, EMPTY)
|
|
449
|
+
const rx = new CoreRX(ptr, db, EMPTY, null, null, -1)
|
|
434
450
|
|
|
435
451
|
const core = {
|
|
436
452
|
head: null,
|
|
@@ -507,8 +523,19 @@ class CorestoreStorage {
|
|
|
507
523
|
|
|
508
524
|
const dbPath = path.join(this.path, 'db')
|
|
509
525
|
|
|
526
|
+
this.stats = {
|
|
527
|
+
treeCache: {
|
|
528
|
+
hits: 0,
|
|
529
|
+
misses: 0,
|
|
530
|
+
parallel: 0,
|
|
531
|
+
skips: 0
|
|
532
|
+
}
|
|
533
|
+
}
|
|
510
534
|
this.rocks = storage === null ? db : new RocksDB(dbPath, { ...opts, lock: this.deviceFile })
|
|
511
535
|
this.db = createColumnFamily(this.rocks, opts)
|
|
536
|
+
|
|
537
|
+
const { treeCache = { maxSize: 8192 } } = opts
|
|
538
|
+
this.treeCache = new Xache(treeCache)
|
|
512
539
|
}
|
|
513
540
|
|
|
514
541
|
get opened() {
|
|
@@ -530,7 +557,7 @@ class CorestoreStorage {
|
|
|
530
557
|
|
|
531
558
|
async audit() {
|
|
532
559
|
for await (const { core } of this.createCoreStream()) {
|
|
533
|
-
const coreRx = new CoreRX(core, this.db, EMPTY)
|
|
560
|
+
const coreRx = new CoreRX(core, this.db, EMPTY, this.treeCache, this.stats, -1)
|
|
534
561
|
const authPromise = coreRx.getAuth()
|
|
535
562
|
|
|
536
563
|
coreRx.tryFlush()
|
|
@@ -548,7 +575,7 @@ class CorestoreStorage {
|
|
|
548
575
|
}
|
|
549
576
|
|
|
550
577
|
async deleteCore(ptr) {
|
|
551
|
-
const rx = new CoreRX(ptr, this.db, EMPTY)
|
|
578
|
+
const rx = new CoreRX(ptr, this.db, EMPTY, this.treeCache, this.stats, -1)
|
|
552
579
|
|
|
553
580
|
const authPromise = rx.getAuth()
|
|
554
581
|
const sessionsPromise = rx.getSessions()
|
|
@@ -782,6 +809,7 @@ class CorestoreStorage {
|
|
|
782
809
|
await this._flush()
|
|
783
810
|
await this.db.close()
|
|
784
811
|
await this.rocks.close()
|
|
812
|
+
this.treeCache.destroy()
|
|
785
813
|
if (this.deviceFile) await this.deviceFile.close()
|
|
786
814
|
}
|
|
787
815
|
|
|
@@ -1053,7 +1081,14 @@ class CorestoreStorage {
|
|
|
1053
1081
|
const ptr = { corePointer, dataPointer, dependencies: [] }
|
|
1054
1082
|
|
|
1055
1083
|
while (true) {
|
|
1056
|
-
const rx = new CoreRX(
|
|
1084
|
+
const rx = new CoreRX(
|
|
1085
|
+
{ dataPointer, corePointer: 0, dependencies: [] },
|
|
1086
|
+
this.db,
|
|
1087
|
+
EMPTY,
|
|
1088
|
+
this.treeCache,
|
|
1089
|
+
this.stats,
|
|
1090
|
+
-1
|
|
1091
|
+
)
|
|
1057
1092
|
const dependencyPromise = rx.getDependency()
|
|
1058
1093
|
rx.tryFlush()
|
|
1059
1094
|
const dependency = await dependencyPromise
|
|
@@ -1074,7 +1109,14 @@ class CorestoreStorage {
|
|
|
1074
1109
|
const core = { corePointer, dataPointer, dependencies: [] }
|
|
1075
1110
|
|
|
1076
1111
|
while (true) {
|
|
1077
|
-
const rx = new CoreRX(
|
|
1112
|
+
const rx = new CoreRX(
|
|
1113
|
+
{ dataPointer, corePointer: 0, dependencies: [] },
|
|
1114
|
+
this.db,
|
|
1115
|
+
view,
|
|
1116
|
+
this.treeCache,
|
|
1117
|
+
this.stats,
|
|
1118
|
+
-1
|
|
1119
|
+
)
|
|
1078
1120
|
const dependencyPromise = rx.getDependency()
|
|
1079
1121
|
rx.tryFlush()
|
|
1080
1122
|
const dependency = await dependencyPromise
|
|
@@ -1083,7 +1125,7 @@ class CorestoreStorage {
|
|
|
1083
1125
|
dataPointer = dependency.dataPointer
|
|
1084
1126
|
}
|
|
1085
1127
|
|
|
1086
|
-
const result = new HypercoreStorage(this, this.db.session(), core, EMPTY, null)
|
|
1128
|
+
const result = new HypercoreStorage(this, this.db.session(), this.treeCache, core, EMPTY, null)
|
|
1087
1129
|
|
|
1088
1130
|
if (version < VERSION) await this._migrateCore(result, discoveryKey, version, create)
|
|
1089
1131
|
return result
|
|
@@ -1136,7 +1178,7 @@ class CorestoreStorage {
|
|
|
1136
1178
|
|
|
1137
1179
|
tx.apply()
|
|
1138
1180
|
|
|
1139
|
-
return new HypercoreStorage(this, this.db.session(), ptr, EMPTY, null)
|
|
1181
|
+
return new HypercoreStorage(this, this.db.session(), this.treeCache, ptr, EMPTY, null)
|
|
1140
1182
|
}
|
|
1141
1183
|
|
|
1142
1184
|
async createCore(data) {
|
package/lib/tx.js
CHANGED
|
@@ -176,39 +176,42 @@ class CoreTX {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
class CoreRX {
|
|
179
|
-
constructor(core, db, view) {
|
|
179
|
+
constructor(core, db, view, treeCache, stats, fork) {
|
|
180
180
|
this.core = core
|
|
181
181
|
this.read = db.read({ autoDestroy: true })
|
|
182
182
|
this.view = view
|
|
183
|
+
this.treeCache = treeCache
|
|
184
|
+
this.fork = fork
|
|
185
|
+
this.stats = stats
|
|
183
186
|
|
|
184
187
|
view.readStart()
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
static async getAuth(db, c) {
|
|
188
|
-
return
|
|
191
|
+
return decode(CORE_AUTH, await db.get(core.auth(c.corePointer)))
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
async getAuth() {
|
|
192
|
-
return
|
|
195
|
+
return decode(CORE_AUTH, await this.view.get(this.read, core.auth(this.core.corePointer)))
|
|
193
196
|
}
|
|
194
197
|
|
|
195
198
|
async getSessions() {
|
|
196
|
-
return
|
|
199
|
+
return decode(
|
|
197
200
|
CORE_SESSIONS,
|
|
198
201
|
await this.view.get(this.read, core.sessions(this.core.corePointer))
|
|
199
202
|
)
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
static async getHead(db, c) {
|
|
203
|
-
return
|
|
206
|
+
return decode(CORE_HEAD, await db.get(core.head(c.dataPointer)))
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
async getHead() {
|
|
207
|
-
return
|
|
210
|
+
return decode(CORE_HEAD, await this.view.get(this.read, core.head(this.core.dataPointer)))
|
|
208
211
|
}
|
|
209
212
|
|
|
210
213
|
async getDependency() {
|
|
211
|
-
return
|
|
214
|
+
return decode(
|
|
212
215
|
CORE_DEPENDENCY,
|
|
213
216
|
await this.view.get(this.read, core.dependency(this.core.dataPointer))
|
|
214
217
|
)
|
|
@@ -222,14 +225,11 @@ class CoreRX {
|
|
|
222
225
|
}
|
|
223
226
|
|
|
224
227
|
static async getHints(db, c) {
|
|
225
|
-
return
|
|
228
|
+
return decode(CORE_HINTS, await db.get(core.hints(c.dataPointer)))
|
|
226
229
|
}
|
|
227
230
|
|
|
228
231
|
async getHints() {
|
|
229
|
-
return await
|
|
230
|
-
CORE_HINTS,
|
|
231
|
-
await this.view.get(this.read, core.hints(this.core.dataPointer))
|
|
232
|
-
)
|
|
232
|
+
return decode(CORE_HINTS, await this.view.get(this.read, core.hints(this.core.dataPointer)))
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
getBlock(index) {
|
|
@@ -242,10 +242,64 @@ class CoreRX {
|
|
|
242
242
|
return this.view.get(this.read, core.bitfield(this.core.dataPointer, index, 0))
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
async _resolveTreeCache(key, promise, entry) {
|
|
246
|
+
try {
|
|
247
|
+
const value = decode(CORE_TREE_NODE, await promise)
|
|
248
|
+
const cached = this.treeCache.get(key)
|
|
249
|
+
|
|
250
|
+
// preflight is gone; no-op
|
|
251
|
+
if (cached !== entry) return value
|
|
252
|
+
|
|
253
|
+
if (value !== null) {
|
|
254
|
+
// replace cached entry with value
|
|
255
|
+
this.treeCache.set(key, { pending: null, value })
|
|
256
|
+
} else {
|
|
257
|
+
// discard non-existing values
|
|
258
|
+
this.treeCache.delete(key)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return value
|
|
262
|
+
} catch (err) {
|
|
263
|
+
// cleanup
|
|
264
|
+
const cached = this.treeCache.get(key)
|
|
265
|
+
if (cached === entry) {
|
|
266
|
+
this.treeCache.delete(key)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
throw err
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
245
273
|
async getTreeNode(index) {
|
|
246
274
|
const dep = findTreeDependency(this.core.dependencies, index)
|
|
247
275
|
const data = dep === null ? this.core.dataPointer : dep.dataPointer
|
|
248
|
-
|
|
276
|
+
|
|
277
|
+
if (!this.treeCache || this.fork < 0) {
|
|
278
|
+
if (this.stats !== null) this.stats.treeCache.skips++
|
|
279
|
+
// no-cache
|
|
280
|
+
return decode(CORE_TREE_NODE, await this.view.get(this.read, core.tree(data, index)))
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const key = `${data}:${index}:${this.fork}`
|
|
284
|
+
let entry = this.treeCache.get(key)
|
|
285
|
+
if (entry) {
|
|
286
|
+
if (entry.pending) {
|
|
287
|
+
if (this.stats !== null) this.stats.treeCache.parallel++
|
|
288
|
+
return entry.pending
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (this.stats !== null) this.stats.treeCache.hits++
|
|
292
|
+
return entry.value
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (this.stats !== null) this.stats.treeCache.misses++
|
|
296
|
+
|
|
297
|
+
const promise = this.view.get(this.read, core.tree(data, index))
|
|
298
|
+
entry = { pending: null, value: null }
|
|
299
|
+
entry.pending = this._resolveTreeCache(key, promise, entry)
|
|
300
|
+
this.treeCache.set(key, entry)
|
|
301
|
+
|
|
302
|
+
return entry.pending
|
|
249
303
|
}
|
|
250
304
|
|
|
251
305
|
async hasTreeNode(index) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore-storage",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"index.js",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"resolve-reject-promise": "^1.0.0",
|
|
59
59
|
"rocksdb-native": "^3.11.0",
|
|
60
60
|
"scope-lock": "^1.2.4",
|
|
61
|
-
"streamx": "^2.21.1"
|
|
61
|
+
"streamx": "^2.21.1",
|
|
62
|
+
"xache": "^1.2.1"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
65
|
"bare-os": "^3.9.1",
|