Files
indiekit-server/scripts/patch-ap-collection-count-estimate.mjs
Sven 4bfc6e2120
Deploy Indiekit Server / deploy (push) Successful in 1m47s
Add patch: followers/following countDocuments() → estimatedDocumentCount()
federation-setup.js calls countDocuments() with no filter 4 times for the
AP followers/following collection dispatchers (pagination totalItems + counter).
All are wrapped in cachedQuery() but on cache miss they run full aggregate scans.
estimatedDocumentCount() reads collection metadata — O(1), no scan.
2026-05-03 12:36:28 +02:00

78 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Patch: replace countDocuments() with estimatedDocumentCount() in
* federation-setup.js followers/following collection dispatchers.
*
* All four calls are already wrapped in cachedQuery(), but on cache miss
* countDocuments() runs a full aggregate scan. estimatedDocumentCount()
* reads collection metadata — O(1).
*
* Covers: ap_followers (×2) and ap_following (×2) in collection pagination
* and counter dispatchers.
*/
import { access, readFile, writeFile } from "node:fs/promises";
const candidates = [
"node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
];
const MARKER = "// [patch] collection-count-estimate";
const replacements = [
{
old: ` const t = await collections.ap_followers.countDocuments();
return [d, t];`,
new: ` const t = await collections.ap_followers.estimatedDocumentCount(); ${MARKER}
return [d, t];`,
},
{
old: ` return await collections.ap_followers.countDocuments();`,
new: ` return await collections.ap_followers.estimatedDocumentCount(); ${MARKER}`,
},
{
old: ` const t = await collections.ap_following.countDocuments();
return [d, t];`,
new: ` const t = await collections.ap_following.estimatedDocumentCount(); ${MARKER}
return [d, t];`,
},
{
old: ` return await collections.ap_following.countDocuments();`,
new: ` return await collections.ap_following.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;
let src = await readFile(filePath, "utf8");
if (src.includes(MARKER)) {
console.log(`[postinstall] patch-ap-collection-count-estimate: already applied in ${filePath}`);
patched = true;
break;
}
let changed = 0;
for (const { old, new: replacement } of replacements) {
if (src.includes(old)) {
src = src.replace(old, replacement);
changed++;
}
}
if (changed === 0) {
console.log(`[postinstall] patch-ap-collection-count-estimate: target snippets not found in ${filePath}`);
continue;
}
await writeFile(filePath, src, "utf8");
console.log(`[postinstall] patch-ap-collection-count-estimate: applied ${changed}/4 replacements in ${filePath}`);
patched = true;
break;
}
if (!patched) {
console.log("[postinstall] patch-ap-collection-count-estimate: no target file found");
}