From a04a8976e4ff0824037a836d8540a118bc79e3ea Mon Sep 17 00:00:00 2001 From: Sven Date: Sun, 3 May 2026 12:24:45 +0200 Subject: [PATCH] =?UTF-8?q?Add=20patch:=20ap=5Ffollowers.countDocuments()?= =?UTF-8?q?=20=E2=86=92=20estimatedDocumentCount()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit syndicator.js uses countDocuments() with no filter for a log statement ("Sending X to N followers"). This runs a full aggregate scan — slow under load (2.7s seen in profiling). estimatedDocumentCount() reads collection metadata: O(1), no lock contention, accurate enough for logging. --- scripts/patch-ap-followers-count-estimate.mjs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 scripts/patch-ap-followers-count-estimate.mjs diff --git a/scripts/patch-ap-followers-count-estimate.mjs b/scripts/patch-ap-followers-count-estimate.mjs new file mode 100644 index 00000000..3cd1d095 --- /dev/null +++ b/scripts/patch-ap-followers-count-estimate.mjs @@ -0,0 +1,52 @@ +/** + * Patch: replace ap_followers.countDocuments() with estimatedDocumentCount() + * in syndicator.js log statement. + * + * countDocuments() runs an aggregate({$match:{}, $group:{n:$sum:1}}) scan. + * For a logging call this is unnecessary — estimatedDocumentCount() reads + * collection metadata (O(1), no lock contention). + */ + +import { access, readFile, writeFile } from "node:fs/promises"; + +const candidates = [ + "node_modules/@rmdes/indiekit-endpoint-activitypub/lib/syndicator.js", + "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/syndicator.js", +]; + +const MARKER = "// [patch] followers-count-estimate"; + +const OLD = ` // Count followers for logging + const followerCount = + await plugin._collections.ap_followers.countDocuments();`; + +const NEW = ` // Count followers for logging + const followerCount = + await plugin._collections.ap_followers.estimatedDocumentCount(); ${MARKER}`; + +async function exists(p) { + try { await access(p); return true; } catch { return false; } +} + +let patched = false; +for (const filePath of candidates) { + if (!(await exists(filePath))) continue; + const src = await readFile(filePath, "utf8"); + if (src.includes(MARKER)) { + console.log(`[postinstall] patch-ap-followers-count-estimate: already applied in ${filePath}`); + patched = true; + break; + } + if (!src.includes(OLD)) { + console.log(`[postinstall] patch-ap-followers-count-estimate: target snippet not found in ${filePath}`); + continue; + } + await writeFile(filePath, src.replace(OLD, NEW), "utf8"); + console.log(`[postinstall] patch-ap-followers-count-estimate: applied to ${filePath}`); + patched = true; + break; +} + +if (!patched) { + console.log("[postinstall] patch-ap-followers-count-estimate: no target file found"); +}