unfollowing or blocking causes gc

This commit is contained in:
Andre Staltz 2023-12-19 16:24:45 +02:00
parent 704f747421
commit 3569a9c2ac
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
2 changed files with 224 additions and 3 deletions

View File

@ -1,3 +1,5 @@
const makeDebug = require('debug')
/** /**
* @typedef {ReturnType<import('ppppp-db').init>} PPPPPDB * @typedef {ReturnType<import('ppppp-db').init>} PPPPPDB
* @typedef {ReturnType<import('ppppp-goals').init>} PPPPPGoal * @typedef {ReturnType<import('ppppp-goals').init>} PPPPPGoal
@ -81,6 +83,8 @@ function initConductor(peer, config) {
assertGCPlugin(peer) assertGCPlugin(peer)
assertSyncPlugin(peer) assertSyncPlugin(peer)
const debug = makeDebug('ppppp:conductor')
/** /**
* Set replication goals for various tangles of an account: * Set replication goals for various tangles of an account:
* - Account tangle * - Account tangle
@ -111,6 +115,38 @@ function initConductor(peer, config) {
const feedID = peer.db.feed.getID(accountID, domain) const feedID = peer.db.feed.getID(accountID, domain)
peer.goals.set(feedID, goalDSL) peer.goals.set(feedID, goalDSL)
} }
// prettier-ignore
debug('Setup goals for %s@all, %s@set, %s@set, %s', accountID, followDomain, blockDomain, rules.join(', '))
}
/**
* @param {string} accountID
* @param {Array<string>} rules
*/
function teardownAccountGoals(accountID, rules) {
assertDBPlugin(peer)
assertSetPlugin(peer)
assertGoalsPlugin(peer)
peer.goals.set(accountID, 'none')
const followDomain = peer.set.getDomain('follow')
const followFeedID = peer.db.feed.getID(accountID, followDomain)
peer.goals.set(followFeedID, 'none')
const blockDomain = peer.set.getDomain('block')
const blockFeedID = peer.db.feed.getID(accountID, blockDomain)
peer.goals.set(blockFeedID, 'none')
for (const rule of rules) {
const [domain] = parseRule(rule)
const feedID = peer.db.feed.getID(accountID, domain)
peer.goals.set(feedID, 'none')
}
// prettier-ignore
debug('Teardown goals for %s@all, %s@set, %s@set, %s', accountID, followDomain, blockDomain, rules.join(', '))
} }
/** /**
@ -135,14 +171,25 @@ function initConductor(peer, config) {
setupAccountGoals(myID, myRules) setupAccountGoals(myID, myRules)
// TODO: watch the set for live updates, on add, setupAccountGoals()
// TODO: watch the set for live updates, on remove, teardownAccountGoals()
const followedAccounts = peer.set.values('follow') const followedAccounts = peer.set.values('follow')
for (const theirID of followedAccounts) { for (const theirID of followedAccounts) {
setupAccountGoals(theirID, theirRules) setupAccountGoals(theirID, theirRules)
} }
// @ts-ignore
peer.set.watch(({ event, subdomain, value }) => {
const theirID = value
if (subdomain === 'follow' && event === 'add') {
setupAccountGoals(theirID, theirRules)
}
if (subdomain === 'follow' && event === 'del') {
teardownAccountGoals(theirID, theirRules)
}
if (subdomain === 'block' && event === 'add') {
teardownAccountGoals(theirID, theirRules)
}
})
peer.gc.stop() peer.gc.stop() // TODO: This should happen automatically in gc.start()
peer.gc.start(maxBytes) peer.gc.start(maxBytes)
peer.sync.start() peer.sync.start()
} }

View File

@ -176,3 +176,177 @@ test('GC selected feeds of followed accounts', async (t) => {
await p(bob.close)(true) await p(bob.close)(true)
await p(carol.close)(true) await p(carol.close)(true)
}) })
test('GC recently-unfollowed accounts', async (t) => {
// Alice
const alice = createPeer({ name: 'alice' })
await alice.db.loaded()
// Alice creates her own account
const aliceID = await p(alice.db.account.create)({
subdomain: 'account',
_nonce: 'alice',
})
await p(alice.set.load)(aliceID)
// Alice creates a feed of posts
for (let i = 0; i < 5; i++) {
await p(alice.db.feed.publish)({
account: aliceID,
domain: 'post',
data: { text: 'A' + i },
})
}
// Bob
const bob = createPeer({ name: 'bob' })
await bob.db.loaded()
// Bob creates his own account
const bobID = await p(bob.db.account.create)({
subdomain: 'account',
_nonce: 'bob',
})
await p(bob.set.load)(bobID)
// Bob creates a feed of posts
for (let i = 0; i < 5; i++) {
await p(bob.db.feed.publish)({
account: bobID,
domain: 'post',
data: { text: 'B' + i },
})
}
// Carol
const carol = createPeer({ name: 'carol' })
await carol.db.loaded()
// Carol creates her own account
const carolID = await p(carol.db.account.create)({
subdomain: 'account',
_nonce: 'carol',
})
await p(carol.set.load)(bobID)
// Carol creates a feed of posts
for (let i = 0; i < 5; i++) {
await p(carol.db.feed.publish)({
account: carolID,
domain: 'post',
data: { text: 'C' + i },
})
}
// Alice follows Bob, but not Carol
assert(await p(alice.set.add)('follow', bobID), 'alice follows bob')
alice.conductor.start(aliceID, [['post@all'], ['post@all']], 4_000)
bob.conductor.start(bobID, [['post@all'], ['post@all']], 4_000)
const aliceDialingBob = await p(alice.connect)(bob.getAddress())
const aliceDialingCarol = await p(alice.connect)(carol.getAddress())
await p(setTimeout)(2000)
assert.deepEqual(
getTexts([...alice.db.msgs()]),
['A0', 'A1', 'A2', 'A3', 'A4', /* */ 'B0', 'B1', 'B2', 'B3', 'B4'],
'alice has alice and bob posts'
)
assert(await p(alice.set.del)('follow', bobID), 'alice unfollows bob')
await p(setTimeout)(1000)
assert.deepEqual(
getTexts([...alice.db.msgs()]),
['A0', 'A1', 'A2', 'A3', 'A4'],
'alice has alice posts'
)
await p(aliceDialingBob.close)(true)
await p(aliceDialingCarol.close)(true)
await p(alice.close)(true)
await p(bob.close)(true)
await p(carol.close)(true)
})
test('GC recently-blocked accounts', async (t) => {
// Alice
const alice = createPeer({ name: 'alice' })
await alice.db.loaded()
// Alice creates her own account
const aliceID = await p(alice.db.account.create)({
subdomain: 'account',
_nonce: 'alice',
})
await p(alice.set.load)(aliceID)
// Alice creates a feed of posts
for (let i = 0; i < 5; i++) {
await p(alice.db.feed.publish)({
account: aliceID,
domain: 'post',
data: { text: 'A' + i },
})
}
// Bob
const bob = createPeer({ name: 'bob' })
await bob.db.loaded()
// Bob creates his own account
const bobID = await p(bob.db.account.create)({
subdomain: 'account',
_nonce: 'bob',
})
await p(bob.set.load)(bobID)
// Bob creates a feed of posts
for (let i = 0; i < 5; i++) {
await p(bob.db.feed.publish)({
account: bobID,
domain: 'post',
data: { text: 'B' + i },
})
}
// Carol
const carol = createPeer({ name: 'carol' })
await carol.db.loaded()
// Carol creates her own account
const carolID = await p(carol.db.account.create)({
subdomain: 'account',
_nonce: 'carol',
})
await p(carol.set.load)(bobID)
// Carol creates a feed of posts
for (let i = 0; i < 5; i++) {
await p(carol.db.feed.publish)({
account: carolID,
domain: 'post',
data: { text: 'C' + i },
})
}
// Alice follows Bob, but not Carol
assert(await p(alice.set.add)('follow', bobID), 'alice follows bob')
alice.conductor.start(aliceID, [['post@all'], ['post@all']], 4_000)
bob.conductor.start(bobID, [['post@all'], ['post@all']], 4_000)
const aliceDialingBob = await p(alice.connect)(bob.getAddress())
const aliceDialingCarol = await p(alice.connect)(carol.getAddress())
await p(setTimeout)(2000)
assert.deepEqual(
getTexts([...alice.db.msgs()]),
['A0', 'A1', 'A2', 'A3', 'A4', /* */ 'B0', 'B1', 'B2', 'B3', 'B4'],
'alice has alice and bob posts'
)
assert(await p(alice.set.add)('block', bobID), 'alice blocks bob')
await p(setTimeout)(1000)
assert.deepEqual(
getTexts([...alice.db.msgs()]),
['A0', 'A1', 'A2', 'A3', 'A4'],
'alice has alice posts'
)
await p(aliceDialingBob.close)(true)
await p(aliceDialingCarol.close)(true)
await p(alice.close)(true)
await p(bob.close)(true)
await p(carol.close)(true)
})