fix(storage): deepen hyperbee secondary-index and hyperdrive snapshots

- Fix missing totalPrimaries() on bee-secondary-index (runtime bug in getStats)
- Add indexPutMany, lookupFirst, and tests for secondary index
- drive-version-snapshot: findByLabel, latestSnapshot, deleteSnapshot, merge gossip

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-21 02:21:32 -04:00
co-authored by Cursor
parent 3de911fc2e
commit cbc5da6b6c
3 changed files with 60 additions and 1 deletions
@@ -74,6 +74,26 @@ class HyperP2PBeeSecondaryIndex extends EventEmitter {
return set ? set.size : 0 return set ? set.size : 0
} }
totalPrimaries () {
let n = 0
for (const set of this._index.values()) n += set.size
return n
}
indexPutMany (pairs) {
if (!Array.isArray(pairs)) throw new Error('pairs must be an array')
let n = 0
for (const p of pairs) {
if (p && this.indexPut(p.secondaryKey, p.primaryKey)) n++
}
return n
}
lookupFirst (secondaryKey) {
const keys = this.indexLookup(secondaryKey)
return keys.length ? keys[0] : null
}
getStats () { getStats () {
return beeStats(this._stats, PROTOCOL, { return beeStats(this._stats, PROTOCOL, {
secondaryKeys: this._index.size, secondaryKeys: this._index.size,
@@ -36,3 +36,13 @@ test('rebuildIndex', async (t) => {
t.is(n, 1) t.is(n, 1)
await m.close() await m.close()
}) })
test('totalPrimaries and lookupFirst', async (t) => {
const m = new HyperP2PBeeSecondaryIndex()
m.indexPut('a', 'p1')
m.indexPut('a', 'p2')
m.indexPut('b', 'p3')
t.is(m.totalPrimaries(), 3)
t.is(m.lookupFirst('a'), 'p1')
await m.close()
})
@@ -58,10 +58,39 @@ class HyperP2PDriveVersionSnapshot extends EventEmitter {
return this._snapshots.get(id) || null return this._snapshots.get(id) || null
} }
findByLabel (label) {
assertNonEmpty(label, 'label')
return this.listSnapshots().filter((s) => s.label === label)
}
latestSnapshot (label = null) {
const list = label ? this.findByLabel(label) : this.listSnapshots()
return list.length ? list[0] : null
}
deleteSnapshot (id) {
const ok = this._snapshots.delete(id)
if (ok) {
sendGossip(this, { type: 'snapshot-delete', id })
this._stats.gossipOut++
this.emit('delete', { id })
}
return ok
}
snapshotCount () {
return this._snapshots.size
}
_onGossip (d) { _onGossip (d) {
if (d?.type === 'snapshot-delete') {
this._snapshots.delete(d.id)
return
}
if (!d || d.type !== 'snapshot' || !d.snap) return if (!d || d.type !== 'snapshot' || !d.snap) return
this._stats.gossipIn++ this._stats.gossipIn++
this._snapshots.set(d.snap.id, d.snap) const prev = this._snapshots.get(d.snap.id)
if (!prev || d.snap.at >= prev.at) this._snapshots.set(d.snap.id, d.snap)
} }
getStats () { getStats () {