mirror of https://codeberg.org/pzp/pzp-db.git
add() can replace a dataless msg with a dataful
This commit is contained in:
parent
cbeabab904
commit
6661b73fd4
38
lib/index.js
38
lib/index.js
|
@ -512,6 +512,14 @@ function initDB(peer, config) {
|
||||||
return tangleID
|
return tangleID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} msgID
|
||||||
|
* @param {CB<void>} cb
|
||||||
|
*/
|
||||||
|
function bypassPredelete(msgID, cb) {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Msg} msg
|
* @param {Msg} msg
|
||||||
* @param {MsgID | null} tangleID
|
* @param {MsgID | null} tangleID
|
||||||
|
@ -529,23 +537,34 @@ function initDB(peer, config) {
|
||||||
// TODO: optimize this. Perhaps have a Map() of msgID -> record
|
// TODO: optimize this. Perhaps have a Map() of msgID -> record
|
||||||
// Or even better, a bloom filter. If you just want to answer no/perhaps.
|
// Or even better, a bloom filter. If you just want to answer no/perhaps.
|
||||||
let rec
|
let rec
|
||||||
if ((rec = getRecord(msgID))) return cb(null, rec)
|
let maybePredelete = bypassPredelete
|
||||||
else rec = { msg, id: msgID }
|
if ((rec = getRecord(msgID))) {
|
||||||
|
// If existing record is dataless but new is dataful, then delete
|
||||||
|
if (rec.msg.data === null && msg.data !== null) {
|
||||||
|
maybePredelete = del
|
||||||
|
rec = { msg, id: msgID }
|
||||||
|
} else {
|
||||||
|
return cb(null, rec)
|
||||||
|
}
|
||||||
|
} else rec = { msg, id: msgID }
|
||||||
|
|
||||||
tangleID ??= inferTangleID(rec)
|
const actualTangleID = tangleID ?? inferTangleID(rec)
|
||||||
|
|
||||||
let err
|
let err
|
||||||
if ((err = verifyRec(rec, tangleID))) {
|
if ((err = verifyRec(rec, actualTangleID))) {
|
||||||
return cb(new Error('add() failed to verify msg', { cause: err }))
|
return cb(new Error('add() failed to verify msg', { cause: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The majority of cases don't have ghosts to be removed, but this operation
|
maybePredelete(msgID, (err) => {
|
||||||
// is silent and cheap if there are no ghosts.
|
if (err) return cb(new Error('add() failed to predelete', { cause: err }))
|
||||||
removeGhost(tangleID, msgID, (err) => {
|
// The majority of cases don't have ghosts to be removed, but this
|
||||||
|
// operation is silent and cheap if there are no ghosts.
|
||||||
|
removeGhost(actualTangleID, msgID, (err) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return cb(new Error('add() failed to remove ghost', { cause: err }))
|
if (err) return cb(new Error('add() failed to remove ghost', { cause: err }))
|
||||||
logAppend(msgID, msg, (err, rec) => {
|
logAppend(msgID, msg, (err, rec) => {
|
||||||
if (err) return cb(new Error('add() failed in the log', { cause: err }))
|
if (err)
|
||||||
|
return cb(new Error('add() failed in the log', { cause: err }))
|
||||||
const doneable = msgsBeingAdded.get(msgID)
|
const doneable = msgsBeingAdded.get(msgID)
|
||||||
msgsBeingAdded.delete(msgID)
|
msgsBeingAdded.delete(msgID)
|
||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
|
@ -555,6 +574,7 @@ function initDB(peer, config) {
|
||||||
cb(null, rec)
|
cb(null, rec)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1230,7 +1250,7 @@ function initDB(peer, config) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
if (err) return cb?.(err)
|
if (err) return cb?.(err)
|
||||||
rescanLogPostCompaction(cb)
|
rescanLogPostCompaction(cb)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -60,5 +60,79 @@ test('add()', async (t) => {
|
||||||
assert.deepEqual(stats, { totalBytes: 2072, deletedBytes: 0 })
|
assert.deepEqual(stats, { totalBytes: 2072, deletedBytes: 0 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await t.test('dataful msg replacing a dataless msg', async (t) => {
|
||||||
|
const rootMsg = MsgV4.createMoot(id, 'something', keypair)
|
||||||
|
const rootID = MsgV4.getMsgID(rootMsg)
|
||||||
|
await p(peer.db.add)(rootMsg, rootID)
|
||||||
|
|
||||||
|
const tangle = new MsgV4.Tangle(rootID)
|
||||||
|
tangle.add(rootID, rootMsg)
|
||||||
|
|
||||||
|
const msg1Dataful = MsgV4.create({
|
||||||
|
keypair,
|
||||||
|
account: id,
|
||||||
|
accountTips: [id],
|
||||||
|
domain: 'something',
|
||||||
|
data: { text: 'first' },
|
||||||
|
tangles: {
|
||||||
|
[rootID]: tangle,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const msg1Dataless = { ...msg1Dataful, data: null }
|
||||||
|
const msg1ID = MsgV4.getMsgID(msg1Dataful)
|
||||||
|
|
||||||
|
tangle.add(msg1ID, msg1Dataful)
|
||||||
|
|
||||||
|
const msg2 = MsgV4.create({
|
||||||
|
keypair,
|
||||||
|
account: id,
|
||||||
|
accountTips: [id],
|
||||||
|
domain: 'something',
|
||||||
|
data: { text: 'second' },
|
||||||
|
tangles: {
|
||||||
|
[rootID]: tangle,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const msg2ID = MsgV4.getMsgID(msg2)
|
||||||
|
|
||||||
|
await p(peer.db.add)(msg1Dataless, rootID)
|
||||||
|
await p(peer.db.add)(msg2, rootID)
|
||||||
|
|
||||||
|
// We expect there to be 3 msgs: root, dataless msg1, dataful msg2
|
||||||
|
{
|
||||||
|
const ids = []
|
||||||
|
const texts = []
|
||||||
|
for (const rec of peer.db.records()) {
|
||||||
|
if (rec.msg.metadata.domain === 'something') {
|
||||||
|
ids.push(rec.id)
|
||||||
|
texts.push(rec.msg.data?.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.deepEqual(ids, [rootID, msg1ID, msg2ID])
|
||||||
|
assert.deepEqual(texts, [undefined, undefined, 'second'])
|
||||||
|
const stats = await p(peer.db.log.stats)()
|
||||||
|
assert.deepEqual(stats, { totalBytes: 3718, deletedBytes: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
await p(peer.db.add)(msg1Dataful, rootID)
|
||||||
|
|
||||||
|
// We expect there to be 3 msgs: root, (deleted) dataless msg1, dataful msg2
|
||||||
|
// and dataful msg1 appended at the end
|
||||||
|
{
|
||||||
|
const ids = []
|
||||||
|
const texts = []
|
||||||
|
for (const rec of peer.db.records()) {
|
||||||
|
if (rec.msg.metadata.domain === 'something') {
|
||||||
|
ids.push(rec.id)
|
||||||
|
texts.push(rec.msg.data?.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.deepEqual(ids, [rootID, msg2ID, msg1ID])
|
||||||
|
assert.deepEqual(texts, [undefined, 'second', 'first'])
|
||||||
|
const stats = await p(peer.db.log.stats)()
|
||||||
|
assert.deepEqual(stats, { totalBytes: 4340, deletedBytes: 610 })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
await p(peer.close)(true)
|
await p(peer.close)(true)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue