Add patch: followers/following countDocuments() → estimatedDocumentCount()
Deploy Indiekit Server / deploy (push) Successful in 1m47s
Deploy Indiekit Server / deploy (push) Successful in 1m47s
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.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
Reference in New Issue
Block a user