mirror of https://codeberg.org/pzp/pzp-gc.git
support deleting plus ghosting
This commit is contained in:
parent
597f3a3bce
commit
c7dc78b859
24
lib/index.js
24
lib/index.js
|
@ -76,25 +76,37 @@ function initGC(peer, config) {
|
||||||
function cleanup(cb) {
|
function cleanup(cb) {
|
||||||
assertDBExists(peer)
|
assertDBExists(peer)
|
||||||
assertGoalsExists(peer)
|
assertGoalsExists(peer)
|
||||||
debug('cleanup goalless started')
|
debug('cleanup-per-purpose started')
|
||||||
|
|
||||||
const done = multicb({ pluck: 1 })
|
const done = multicb({ pluck: 1 })
|
||||||
let waiting = false
|
let waiting = false
|
||||||
for (const rec of peer.db.records()) {
|
for (const rec of peer.db.records()) {
|
||||||
if (!rec.msg) continue
|
if (!rec.msg) continue
|
||||||
const purpose = peer.goals.getRecordPurpose(rec)
|
const { id: msgID, msg } = rec
|
||||||
|
const [purpose, details] = peer.goals.getMsgPurpose(msgID, msg)
|
||||||
if (purpose === 'none') {
|
if (purpose === 'none') {
|
||||||
peer.db.del(rec.id, done())
|
peer.db.del(msgID, done())
|
||||||
|
waiting = true
|
||||||
|
} else if (purpose === 'ghost') {
|
||||||
|
const { tangleID, span } = details
|
||||||
|
const cb = done()
|
||||||
|
// TODO: Could one msg be a ghostable in MANY tangles? Or just one?
|
||||||
|
peer.db.ghosts.add({ tangleID, msgID, span }, (err) => {
|
||||||
|
// prettier-ignore
|
||||||
|
if (err) return cb(new Error('gc failed to add ghost', { cause: err }))
|
||||||
|
peer.db.del(msgID, cb)
|
||||||
|
})
|
||||||
waiting = true
|
waiting = true
|
||||||
} else if (purpose === 'trail') {
|
} else if (purpose === 'trail') {
|
||||||
peer.db.erase(rec.id, done())
|
peer.db.erase(msgID, done())
|
||||||
waiting = true
|
waiting = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** @param {Error=} err */
|
/** @param {Error=} err */
|
||||||
function whenEnded(err) {
|
function whenEnded(err) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) debug('cleanup goalless ended with an error %s', err.message ?? err)
|
if (err) debug('cleanup-per-purpose ended with an error %s', err.message ?? err)
|
||||||
else debug('cleanup goalless ended')
|
else debug('cleanup-per-purpose ended')
|
||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
if (waiting) done(whenEnded)
|
if (waiting) done(whenEnded)
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
"ppppp-db": "github:staltz/ppppp-db",
|
"ppppp-db": "github:staltz/ppppp-db",
|
||||||
"ppppp-goals": "github:staltz/ppppp-goals",
|
"ppppp-goals": "github:staltz/ppppp-goals",
|
||||||
"ppppp-keypair": "github:staltz/ppppp-keypair",
|
"ppppp-keypair": "github:staltz/ppppp-keypair",
|
||||||
|
"ppppp-record": "github:staltz/ppppp-record",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
"rimraf": "^4.4.0",
|
"rimraf": "^4.4.0",
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
const test = require('node:test')
|
||||||
|
const assert = require('node:assert')
|
||||||
|
const p = require('node:util').promisify
|
||||||
|
const { createPeer } = require('./util')
|
||||||
|
|
||||||
|
function getFields(msgs) {
|
||||||
|
return msgs
|
||||||
|
.map((msg) => msg.data?.update)
|
||||||
|
.filter((x) => !!x)
|
||||||
|
.map((x) => x.age ?? x.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isErased(msg) {
|
||||||
|
return !msg.data
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDeleted(msg) {
|
||||||
|
return !msg
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPresent(msg) {
|
||||||
|
return !!msg.data.update
|
||||||
|
}
|
||||||
|
|
||||||
|
test('record ghosts', async (t) => {
|
||||||
|
const alice = createPeer({
|
||||||
|
name: 'alice',
|
||||||
|
gc: { maxLogBytes: 100 * 1024 * 1024 },
|
||||||
|
record: { ghostSpan: 2 },
|
||||||
|
})
|
||||||
|
|
||||||
|
await alice.db.loaded()
|
||||||
|
|
||||||
|
// Alice creates her own account
|
||||||
|
const aliceID = await p(alice.db.account.create)({
|
||||||
|
domain: 'account',
|
||||||
|
_nonce: 'alice',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Alice constructs a record
|
||||||
|
await p(alice.record.load)(aliceID)
|
||||||
|
await p(alice.record.update)('profile', { name: 'alice' })
|
||||||
|
await p(alice.record.update)('profile', { age: 24 })
|
||||||
|
await p(alice.record.update)('profile', { name: 'Alice' })
|
||||||
|
await p(alice.record.update)('profile', { age: 25 })
|
||||||
|
await p(alice.record.update)('profile', { name: 'ALICE' })
|
||||||
|
const recordID = alice.record.getFeedID('profile')
|
||||||
|
|
||||||
|
let mootID
|
||||||
|
let msgID1
|
||||||
|
let msgID2
|
||||||
|
let msgID3
|
||||||
|
let msgID4
|
||||||
|
let msgID5
|
||||||
|
for (const rec of alice.db.records()) {
|
||||||
|
if (rec.msg.metadata.dataSize === 0) mootID = rec.id
|
||||||
|
if (rec.msg.data?.update?.name === 'alice') msgID1 = rec.id
|
||||||
|
if (rec.msg.data?.update?.age === 24) msgID2 = rec.id
|
||||||
|
if (rec.msg.data?.update?.name === 'Alice') msgID3 = rec.id
|
||||||
|
if (rec.msg.data?.update?.age === 25) msgID4 = rec.id
|
||||||
|
if (rec.msg.data?.update?.name === 'ALICE') msgID5 = rec.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert situation before GC
|
||||||
|
assert.deepEqual(
|
||||||
|
getFields([...alice.db.msgs()]),
|
||||||
|
['alice', 24, 'Alice', 25, 'ALICE'],
|
||||||
|
'has all record msgs'
|
||||||
|
)
|
||||||
|
assert.ok(isErased(alice.db.get(mootID)), 'moot by def erased')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID1)), 'msg1 exists')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID2)), 'msg2 exists')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID3)), 'msg3 exists')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID4)), 'msg4 exists')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID5)), 'msg5 exists')
|
||||||
|
|
||||||
|
// Perform garbage collection
|
||||||
|
alice.goals.set(aliceID, 'all')
|
||||||
|
alice.goals.set(recordID, 'record')
|
||||||
|
await p(alice.gc.forceImmediately)()
|
||||||
|
|
||||||
|
// Assert situation after GC
|
||||||
|
assert.deepEqual(
|
||||||
|
getFields([...alice.db.msgs()]),
|
||||||
|
[25, 'ALICE'],
|
||||||
|
'alice has only field root msgs'
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ok(isErased(alice.db.get(mootID)), 'moot by def erased')
|
||||||
|
assert.ok(isDeleted(alice.db.get(msgID1)), 'msg1 deleted')
|
||||||
|
assert.ok(isDeleted(alice.db.get(msgID2)), 'msg2 deleted') // ghost!
|
||||||
|
assert.ok(isErased(alice.db.get(msgID3)), 'msg3 erased')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID4)), 'msg4 exists')
|
||||||
|
assert.ok(isPresent(alice.db.get(msgID5)), 'msg5 exists')
|
||||||
|
|
||||||
|
assert.deepEqual(alice.db.ghosts.get(recordID), [msgID2])
|
||||||
|
|
||||||
|
await p(alice.close)(true)
|
||||||
|
})
|
|
@ -18,6 +18,7 @@ function createPeer(opts) {
|
||||||
.use(require('secret-stack/plugins/net'))
|
.use(require('secret-stack/plugins/net'))
|
||||||
.use(require('secret-handshake-ext/secret-stack'))
|
.use(require('secret-handshake-ext/secret-stack'))
|
||||||
.use(require('ppppp-db'))
|
.use(require('ppppp-db'))
|
||||||
|
.use(require('ppppp-record'))
|
||||||
.use(require('ppppp-goals'))
|
.use(require('ppppp-goals'))
|
||||||
.use(require('ssb-box'))
|
.use(require('ssb-box'))
|
||||||
.use(require('../lib'))
|
.use(require('../lib'))
|
||||||
|
|
Loading…
Reference in New Issue