mirror of https://codeberg.org/pzp/pzp-gc.git
add types in JSDoc
This commit is contained in:
parent
889d003be4
commit
597f3a3bce
168
lib/index.js
168
lib/index.js
|
@ -1,36 +1,81 @@
|
||||||
const makeDebug = require('debug')
|
// @ts-ignore
|
||||||
const multicb = require('multicb')
|
const multicb = require('multicb')
|
||||||
|
const makeDebug = require('debug')
|
||||||
module.exports = {
|
|
||||||
name: 'gc',
|
|
||||||
manifest: {},
|
|
||||||
permissions: {
|
|
||||||
anonymous: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {any} peer
|
* @typedef {ReturnType<import('ppppp-db').init>} PPPPPDB
|
||||||
* @param {{
|
* @typedef {ReturnType<import('ppppp-goals').init>} PPPPPGoal
|
||||||
* gc?: {
|
* @typedef {{
|
||||||
* maxLogBytes?: number
|
* gc: {
|
||||||
|
* maxLogBytes: number
|
||||||
|
* compactionInterval?: number
|
||||||
* }
|
* }
|
||||||
* }} config
|
* }} ExpectedConfig
|
||||||
|
* @typedef {{gc?: Partial<ExpectedConfig['gc']>}} Config
|
||||||
*/
|
*/
|
||||||
init(peer, config) {
|
|
||||||
// Assertions
|
/**
|
||||||
if (!peer.goals) throw new Error('gc requires the goals plugin')
|
* @template T
|
||||||
|
* @typedef {T extends void ?
|
||||||
|
* (...args: [Error] | []) => void :
|
||||||
|
* (...args: [Error] | [null, T]) => void
|
||||||
|
* } CB
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ db: PPPPPDB | null }} peer
|
||||||
|
* @returns {asserts peer is { db: PPPPPDB }}
|
||||||
|
*/
|
||||||
|
function assertDBExists(peer) {
|
||||||
|
if (!peer.db) throw new Error('gc plugin requires ppppp-db plugin')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ goals: PPPPPGoal | null }} peer
|
||||||
|
* @returns {asserts peer is { goals: PPPPPGoal }}
|
||||||
|
*/
|
||||||
|
function assertGoalsExists(peer) {
|
||||||
|
if (!peer.goals) throw new Error('gc plugin requires ppppp-goals plugin')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Config} config
|
||||||
|
* @returns {asserts config is ExpectedConfig}
|
||||||
|
*/
|
||||||
|
function assertValidConfig(config) {
|
||||||
if (typeof config.gc?.maxLogBytes !== 'number') {
|
if (typeof config.gc?.maxLogBytes !== 'number') {
|
||||||
throw new Error('gc requires config.gc.maxLogBytes')
|
throw new Error('gc requires config.gc.maxLogBytes')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ db: PPPPPDB | null, goals: PPPPPGoal | null }} peer
|
||||||
|
* @param {Config} config
|
||||||
|
*/
|
||||||
|
function initGC(peer, config) {
|
||||||
|
// Assertions
|
||||||
|
assertDBExists(peer)
|
||||||
|
assertGoalsExists(peer)
|
||||||
|
assertValidConfig(config)
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const COMPACTION_INTERVAL = config.gc?.compactionInterval ?? 120e3
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const debug = makeDebug('ppppp:gc')
|
const debug = makeDebug('ppppp:gc')
|
||||||
|
let lastCompacted = Date.now()
|
||||||
|
let stopMonitoringLogSize = /** @type {CallableFunction | null} */ (null)
|
||||||
|
let hasCompactionScheduled = false
|
||||||
|
let hasCleanupScheduled = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes messages that don't correspond to any goal.
|
* Deletes messages that don't correspond to any goal.
|
||||||
* @private
|
* @private
|
||||||
|
* @param {CB<void>} cb
|
||||||
*/
|
*/
|
||||||
function cleanup(cb) {
|
function cleanup(cb) {
|
||||||
|
assertDBExists(peer)
|
||||||
|
assertGoalsExists(peer)
|
||||||
debug('cleanup goalless started')
|
debug('cleanup goalless started')
|
||||||
const done = multicb({ pluck: 1 })
|
const done = multicb({ pluck: 1 })
|
||||||
let waiting = false
|
let waiting = false
|
||||||
|
@ -45,6 +90,7 @@ module.exports = {
|
||||||
waiting = true
|
waiting = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** @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 goalless ended with an error %s', err.message ?? err)
|
||||||
|
@ -55,14 +101,100 @@ module.exports = {
|
||||||
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) {
|
||||||
|
assertDBExists(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.
|
||||||
|
*/
|
||||||
|
function monitorLogSize() {
|
||||||
|
assertDBExists(peer)
|
||||||
|
function checkLogSize() {
|
||||||
|
assertDBExists(peer)
|
||||||
|
assertValidConfig(config)
|
||||||
|
peer.db.logStats((err, stats) => {
|
||||||
|
if (err) return
|
||||||
|
const percentUsed = (stats.totalBytes / config.gc.maxLogBytes) * 100
|
||||||
|
const needsCompaction = stats.deletedBytes > 0
|
||||||
|
|
||||||
|
// Schedule compaction
|
||||||
|
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
|
||||||
|
if (percentUsed > 80 && !hasCleanupScheduled) {
|
||||||
|
hasCleanupScheduled = true
|
||||||
|
cleanup(() => {
|
||||||
|
hasCleanupScheduled = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
stopMonitoringLogSize = peer.db.onRecordAdded(() => {
|
||||||
|
count += 1
|
||||||
|
if (count >= 1000) {
|
||||||
|
count = 0
|
||||||
|
checkLogSize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
checkLogSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (!stopMonitoringLogSize) {
|
||||||
|
monitorLogSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
if (stopMonitoringLogSize) {
|
||||||
|
stopMonitoringLogSize()
|
||||||
|
stopMonitoringLogSize = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {CB<void>} cb
|
||||||
|
*/
|
||||||
function forceImmediately(cb) {
|
function forceImmediately(cb) {
|
||||||
debug('force immediately')
|
debug('force immediately')
|
||||||
cleanup(cb)
|
cleanup(cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initiate,
|
start,
|
||||||
|
stop,
|
||||||
forceImmediately,
|
forceImmediately,
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.name = 'gc'
|
||||||
|
exports.init = initGC
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"multicb": "^1.2.2"
|
"multicb": "^1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/debug": "4.1.9",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^5.0.0",
|
||||||
"c8": "7",
|
"c8": "7",
|
||||||
"ppppp-caps": "github:staltz/ppppp-caps",
|
"ppppp-caps": "github:staltz/ppppp-caps",
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"include": ["lib/**/*.js"],
|
||||||
|
"exclude": ["coverage/", "node_modules/", "test/"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"lib": ["es2022", "dom"],
|
||||||
|
"module": "node16",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "es2021"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue