diff --git a/lib/index.js b/lib/index.js index f353066..7673780 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,13 +7,28 @@ module.exports = { permissions: { anonymous: {}, }, + + /** + * @param {any} peer + * @param {{ + * gc?: { + * maxLogBytes?: number + * } + * }} config + */ init(peer, config) { + // Assertions if (!peer.goals) throw new Error('gc requires the goals plugin') + if (typeof config.gc?.maxLogBytes !== 'number') { + throw new Error('gc requires config.gc.maxLogBytes') + } + + // State const debug = makeDebug('ppppp:gc') - function purgeGoallessMsgs(cb) { - debug('purge goalless msgs') - const done = multicb() + function cleanup(cb) { + debug('cleanup goalless started') + const done = multicb({ pluck: 1 }) let waitingForDels = false for (const rec of peer.db.records()) { if (!rec.msg) continue @@ -21,17 +36,22 @@ module.exports = { if (goals.length === 0) { peer.db.del(rec.id, done()) waitingForDels = true + } else { } } - if (waitingForDels) done(cb) - else cb() + function whenEnded(err) { + // prettier-ignore + if (err) debug('cleanup goalless ended with an error %s', err.message ?? err) + else debug('cleanup goalless ended') + cb() + } + if (waitingForDels) done(whenEnded) + else whenEnded() } - function initiate() {} - function forceImmediately(cb) { debug('force immediately') - purgeGoallessMsgs(cb) + cleanup(cb) } return { diff --git a/test/feed-decay.test.js b/test/feed-decay.test.js new file mode 100644 index 0000000..15d4e1d --- /dev/null +++ b/test/feed-decay.test.js @@ -0,0 +1,52 @@ +const test = require('node:test') +const assert = require('node:assert') +const p = require('node:util').promisify +const { createPeer } = require('./util') + +function getTexts(msgs) { + return msgs.filter((msg) => msg.data?.text).map((msg) => msg.data.text) +} + +test('feed decay', async (t) => { + const alice = createPeer({ + name: 'alice', + gc: { maxLogBytes: 100 * 1024 * 1024 }, + }) + + await alice.db.loaded() + + // Alice creates her own account + const aliceID = await p(alice.db.account.create)({ + domain: 'account', + _nonce: 'alice', + }) + + for (let i = 0; i < 5; i++) { + await p(alice.db.feed.publish)({ + account: aliceID, + domain: 'post', + data: { text: 'A' + i }, + }) + } + + assert.deepEqual( + getTexts([...alice.db.msgs()]), + ['A0', 'A1', 'A2', 'A3', 'A4'], + 'alice has the whole feed' + ) + + alice.goals.set(aliceID, 'all') // alice wants her account tangle + const postFeedID = alice.db.feed.getID(aliceID, 'post') + alice.goals.set(postFeedID, 'newest-3') + assert('alice set a goal for newest-3 of post feed') + + await p(alice.gc.forceImmediately)() + + assert.deepEqual( + getTexts([...alice.db.msgs()]), + ['A2', 'A3', 'A4'], + 'alice has only latest 3 msgs in the feed' + ) + + await p(alice.close)(true) +}) diff --git a/test/purge-weave.test.js b/test/orphan-weave.test.js similarity index 93% rename from test/purge-weave.test.js rename to test/orphan-weave.test.js index 3f8dfe6..2f80c50 100644 --- a/test/purge-weave.test.js +++ b/test/orphan-weave.test.js @@ -11,8 +11,11 @@ function getTexts(msgs) { return msgs.filter((msg) => msg.data?.text).map((msg) => msg.data.text) } -test('purge an orphan weave', async (t) => { - const alice = createPeer({ name: 'alice' }) +test('orphan weave msgs', async (t) => { + const alice = createPeer({ + name: 'alice', + gc: { maxLogBytes: 100 * 1024 * 1024 }, + }) await alice.db.loaded() @@ -27,7 +30,7 @@ test('purge an orphan weave', async (t) => { keypair: bobKeypair, _nonce: 'bob', }) - // Alice creates Bob + // Alice creates Carol const carolID = await p(alice.db.account.create)({ domain: 'account', keypair: carolKeypair,