update with latest ppppp-db supporting compact

This commit is contained in:
Andre Staltz 2023-11-23 17:14:38 +02:00
parent 28430facc4
commit 7b1119795e
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
6 changed files with 33 additions and 51 deletions

View File

@ -58,14 +58,14 @@ function initGC(peer, config) {
assertGoalsPlugin(peer) assertGoalsPlugin(peer)
assertValidConfig(config) assertValidConfig(config)
// Constants const MAX_LOG_BYTES = config.gc.maxLogBytes
const COMPACTION_INTERVAL = config.gc?.compactionInterval ?? 120e3
/** Number of records that match roughly 1% of the max log size */
const CHECKPOINT = Math.floor((MAX_LOG_BYTES * 0.01) / 500) // assuming 1 record = 500 bytes
// State // State
const debug = makeDebug('ppppp:gc') const debug = makeDebug('ppppp:gc')
let lastCompacted = Date.now()
let stopMonitoringLogSize = /** @type {CallableFunction | null} */ (null) let stopMonitoringLogSize = /** @type {CallableFunction | null} */ (null)
let hasCompactionScheduled = false
let hasCleanupScheduled = false let hasCleanupScheduled = false
/** /**
@ -107,35 +107,13 @@ function initGC(peer, config) {
// prettier-ignore // prettier-ignore
if (err) debug('cleanup-per-purpose 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-per-purpose ended') else debug('cleanup-per-purpose ended')
cb() assertDBPlugin(peer)
peer.db.log.compact(cb)
} }
if (waiting) done(whenEnded) if (waiting) done(whenEnded)
else whenEnded() else whenEnded()
} }
/**
* Compact the log (remove deleted records by filling in all the blanks).
* @private
* @param {number} waitPeriod
* @param {CB<void>} cb
*/
function compact(waitPeriod, cb) {
assertDBPlugin(peer)
const log = peer.db._getLog() // TODO: use public API?
debug('compaction started')
/** @param {Error=} err */
function whenEnded(err) {
if (err) debug('compaction ended with an error %s', err.message ?? err)
else debug('compaction ended')
cb()
}
if (waitPeriod > 0) {
setTimeout(log.compact, waitPeriod, whenEnded)
} else {
log.compact(whenEnded)
}
}
/** /**
* Monitor the log size and schedule compaction and/or cleanup. * Monitor the log size and schedule compaction and/or cleanup.
*/ */
@ -143,24 +121,15 @@ function initGC(peer, config) {
assertDBPlugin(peer) assertDBPlugin(peer)
function checkLogSize() { function checkLogSize() {
assertDBPlugin(peer) assertDBPlugin(peer)
assertValidConfig(config) peer.db.log.stats((err, stats) => {
peer.db.logStats((err, stats) => {
if (err) return if (err) return
const percentUsed = (stats.totalBytes / config.gc.maxLogBytes) * 100 const percentUsed = (stats.totalBytes / MAX_LOG_BYTES) * 100
const needsCompaction = stats.deletedBytes > 0 const percentDeleted = (stats.deletedBytes / stats.totalBytes) * 100
const needsCleanup = percentUsed > 80
// Schedule compaction const needsCompaction = percentDeleted > 30
if (needsCompaction && !hasCompactionScheduled) {
const nextCompacted = lastCompacted + COMPACTION_INTERVAL
const waitPeriod = Math.max(0, nextCompacted - Date.now())
hasCompactionScheduled = true
compact(waitPeriod, () => {
hasCompactionScheduled = false
})
}
// Schedule clean up // Schedule clean up
if (percentUsed > 80 && !hasCleanupScheduled) { if ((needsCleanup || needsCompaction) && !hasCleanupScheduled) {
hasCleanupScheduled = true hasCleanupScheduled = true
cleanup(() => { cleanup(() => {
hasCleanupScheduled = false hasCleanupScheduled = false
@ -172,7 +141,7 @@ function initGC(peer, config) {
let count = 0 let count = 0
stopMonitoringLogSize = peer.db.onRecordAdded(() => { stopMonitoringLogSize = peer.db.onRecordAdded(() => {
count += 1 count += 1
if (count >= 1000) { if (count >= CHECKPOINT) {
count = 0 count = 0
checkLogSize() checkLogSize()
} }

View File

@ -28,6 +28,7 @@
"multicb": "^1.2.2" "multicb": "^1.2.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "18.x",
"@types/debug": "4.1.9", "@types/debug": "4.1.9",
"bs58": "^5.0.0", "bs58": "^5.0.0",
"c8": "7", "c8": "7",

View File

@ -33,11 +33,11 @@ test('Dict ghosts', async (t) => {
// Alice creates her own account // Alice creates her own account
const aliceID = await p(alice.db.account.create)({ const aliceID = await p(alice.db.account.create)({
domain: 'account', subdomain: 'account',
_nonce: 'alice', _nonce: 'alice',
}) })
// Alice constructs adict // Alice constructs a dict
await p(alice.dict.load)(aliceID) await p(alice.dict.load)(aliceID)
await p(alice.dict.update)('profile', { name: 'alice' }) await p(alice.dict.update)('profile', { name: 'alice' })
await p(alice.dict.update)('profile', { age: 24 }) await p(alice.dict.update)('profile', { age: 24 })
@ -74,6 +74,12 @@ test('Dict ghosts', async (t) => {
assert.ok(isPresent(alice.db.get(msgID4)), 'msg4 exists') assert.ok(isPresent(alice.db.get(msgID4)), 'msg4 exists')
assert.ok(isPresent(alice.db.get(msgID5)), 'msg5 exists') assert.ok(isPresent(alice.db.get(msgID5)), 'msg5 exists')
assert.deepEqual(
await p(alice.db.log.stats)(),
{ totalBytes: 3684, deletedBytes: 0 },
'log stats before'
)
// Perform garbage collection // Perform garbage collection
alice.goals.set(aliceID, 'all') alice.goals.set(aliceID, 'all')
alice.goals.set(dictID, 'dict') alice.goals.set(dictID, 'dict')
@ -86,6 +92,12 @@ test('Dict ghosts', async (t) => {
'alice has only field root msgs' 'alice has only field root msgs'
) )
assert.deepEqual(
await p(alice.db.log.stats)(),
{ totalBytes: 2572, deletedBytes: 0 },
'log stats after'
)
assert.ok(isErased(alice.db.get(mootID)), 'moot by def erased') 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(msgID1)), 'msg1 deleted')
assert.ok(isDeleted(alice.db.get(msgID2)), 'msg2 deleted') // ghost! assert.ok(isDeleted(alice.db.get(msgID2)), 'msg2 deleted') // ghost!

View File

@ -17,7 +17,7 @@ test('feed decay', async (t) => {
// Alice creates her own account // Alice creates her own account
const aliceID = await p(alice.db.account.create)({ const aliceID = await p(alice.db.account.create)({
domain: 'account', subdomain: 'account',
_nonce: 'alice', _nonce: 'alice',
}) })

View File

@ -17,7 +17,7 @@ test('feed holes', async (t) => {
// Alice creates her own account // Alice creates her own account
const aliceID = await p(alice.db.account.create)({ const aliceID = await p(alice.db.account.create)({
domain: 'account', subdomain: 'account',
_nonce: 'alice', _nonce: 'alice',
}) })

View File

@ -21,18 +21,18 @@ test('orphan weave msgs', async (t) => {
// Alice creates her own account // Alice creates her own account
const aliceID = await p(alice.db.account.create)({ const aliceID = await p(alice.db.account.create)({
domain: 'account', subdomain: 'account',
_nonce: 'alice', _nonce: 'alice',
}) })
// Alice creates Bob // Alice creates Bob
const bobID = await p(alice.db.account.create)({ const bobID = await p(alice.db.account.create)({
domain: 'account', subdomain: 'account',
keypair: bobKeypair, keypair: bobKeypair,
_nonce: 'bob', _nonce: 'bob',
}) })
// Alice creates Carol // Alice creates Carol
const carolID = await p(alice.db.account.create)({ const carolID = await p(alice.db.account.create)({
domain: 'account', subdomain: 'account',
keypair: carolKeypair, keypair: carolKeypair,
_nonce: 'carol', _nonce: 'carol',
}) })