mirror of https://codeberg.org/pzp/pzp-gc.git
update with latest ppppp-db supporting compact
This commit is contained in:
parent
28430facc4
commit
7b1119795e
57
lib/index.js
57
lib/index.js
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -33,7 +33,7 @@ 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',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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!
|
||||||
|
|
|
@ -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',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue