mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
refactor(moderation): batch kind-1984 report subs via SubscriptionManager (P2)
Replaces the per-video live report subscription (one pool.sub per visible video × ~20 relays, re-fired on every reconnect — the ~1000 REQ/s storm) with ONE batched SubscriptionManager subscription over the whole active id set, updated in place as the set changes. Backfill fetches history only for newly-added ids (one batched list()). Unchanged sets are a no-op (dedup guard). MODERATION_REPORT_SUBS_ENABLED flipped back on. Trusted-mute subs were already batched-by-author, so the relay cap + contact-loop fix already tamed them. Legacy per-id path retained only for SubscriptionManager-less environments (unit tests / mock clients). All 39 moderation tests pass; new cheat-resistant batching test added; nip07-heat shows reports no longer storm (steady idle). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
9f0541737f
commit
25d3e83c46
2 files changed
+201
-13
No files matched your search
@@ -0,0 +1,77 @@
|
||||
// Scenario (SCN-report-subs-batched):
|
||||
// Given the moderation service has a SubscriptionManager available,
|
||||
// When the feed sets the active visible event-id set,
|
||||
// Then it opens exactly ONE batched kind-1984 subscription (not one per id),
|
||||
// updates that same subscription in place as the set changes,
|
||||
// backfills history for only the newly-added ids, and does nothing when the
|
||||
// set is unchanged.
|
||||
//
|
||||
// This is the P2 fix for the ~1000 REQ/s report storm (one sub per video × ~20
|
||||
// relays, re-fired on every reconnect). See docs/architecture-refactor.md.
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
withMockedNostrTools,
|
||||
createModerationServiceHarness,
|
||||
} from "../helpers/moderation-test-helpers.mjs";
|
||||
|
||||
const A = "a".repeat(64);
|
||||
const B = "b".repeat(64);
|
||||
const C = "c".repeat(64);
|
||||
const f = (...ids) => [{ kinds: [1984], "#e": ids, limit: 500 }];
|
||||
|
||||
test("setActiveEventIds uses one batched report sub, updated in place", async (t) => {
|
||||
withMockedNostrTools(t);
|
||||
const { service } = createModerationServiceHarness(t);
|
||||
|
||||
const calls = { subscribe: 0, update: 0, list: 0, lastFilters: null, lastList: null };
|
||||
const handle = {
|
||||
update({ filters }) {
|
||||
calls.update += 1;
|
||||
calls.lastFilters = filters;
|
||||
},
|
||||
close() {},
|
||||
};
|
||||
const fakeManager = {
|
||||
subscribe({ filters }) {
|
||||
calls.subscribe += 1;
|
||||
calls.lastFilters = filters;
|
||||
return handle;
|
||||
},
|
||||
list({ filters }) {
|
||||
calls.list += 1;
|
||||
calls.lastList = filters;
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
};
|
||||
service.nostrClient = { getSubscriptionManager: () => fakeManager };
|
||||
|
||||
// Initial set of two ids => ONE subscription, ONE backfill covering both.
|
||||
await service.setActiveEventIds([A, B]);
|
||||
assert.equal(calls.subscribe, 1, "exactly one batched subscription (not per-id)");
|
||||
assert.deepEqual(calls.lastFilters, f(A, B), "live sub covers the whole set in one filter");
|
||||
assert.equal(calls.list, 1, "one backfill list");
|
||||
assert.deepEqual(calls.lastList, f(A, B), "backfill covers the two new ids");
|
||||
|
||||
// Add a third id => same sub updated in place; backfill ONLY the new id.
|
||||
await service.setActiveEventIds([A, B, C]);
|
||||
assert.equal(calls.subscribe, 1, "no new subscription is opened");
|
||||
assert.equal(calls.update, 1, "existing subscription updated in place");
|
||||
assert.deepEqual(calls.lastFilters, f(A, B, C), "updated filter covers the full set");
|
||||
assert.equal(calls.list, 2);
|
||||
assert.deepEqual(calls.lastList, f(C), "backfill only the newly-added id");
|
||||
|
||||
// Unchanged set => no work at all.
|
||||
await service.setActiveEventIds([A, B, C]);
|
||||
assert.equal(calls.update, 1, "unchanged set must not re-issue");
|
||||
assert.equal(calls.list, 2, "unchanged set must not re-backfill");
|
||||
|
||||
// Empty set => close the subscription.
|
||||
let closed = false;
|
||||
handle.close = () => {
|
||||
closed = true;
|
||||
};
|
||||
await service.setActiveEventIds([]);
|
||||
assert.ok(closed, "emptying the set closes the live subscription");
|
||||
});
|
||||
Reference in new issue
Block a user