mirror of
https://github.com/block/buzz.git
synced 2026-10-27 20:41:57 +00:00
Entering Projects fires a large fan: an exhaustive paginated relay enumeration (projects/repos/tombstones), five 2,000-event work-item queries plus assignment-operation scans, per-repo activity summaries, and a local-repository filesystem scan. Measured on a large community (101 issues / 258 PRs): | query | measured cost | |---|---| | work-items (5 × 2,000-event REQs + assignment scans) | 3.5–3.9s | | activity summaries | 4.1s | | repository activity | 1.0–2.2s | | local repository scan | 1.7s | Two lifecycle bugs made the fan far more expensive than it needs to be: - **Freshness windows guaranteed a full refetch on nearly every re-entry** (60s enumeration, 30s work-items/activity, 10s local scan) — i.e., the costs above were re-paid on almost every visit. Every local write path already invalidates its keys explicitly (issue/PR mutations, project creation, repo sync), so the short windows only served remote-actor freshness. Raised to 5m/2m/2m with a 30m enumeration cache: re-entries now paint from cache, and the fan re-runs at most every 2–5 minutes. - **Leaving Projects left the whole fan running**, competing with the next surface's channel fetches on the same relay connection. AbortSignal is now threaded through the enumeration and assignment pagination loops (optional params — behavior identical without a signal), and leaving the surface cancels the work-items query. Deliberately NOT cancelled: the enumeration (the always-mounted sidebar projects section observes it and its 30m cache is valuable), repo snapshots and local scans (native work that can't abort — cancelling would discard the finished result and force the same clones again), and activity summaries (a single bounded request). Abort behavior is covered by red-first unit tests on both pagination loops. Remaining follow-up (out of scope): the queries themselves want a relay-side aggregate instead of shipping thousands of events to compute counts client-side. --------- Signed-off-by: Max Lampert <maxwell@squareup.com>
89 lines
2.2 KiB
TypeScript
89 lines
2.2 KiB
TypeScript
const RELAY_QUERY_ROOTS = new Set<string>([
|
|
"archivedIdentities",
|
|
"channel-canvas",
|
|
"channel-messages",
|
|
"channels",
|
|
"contact-list",
|
|
"custom-emoji",
|
|
"custom-emoji-own",
|
|
"forum-posts",
|
|
"forum-thread",
|
|
"global-notes",
|
|
"home-feed",
|
|
"liked-notes",
|
|
"my-notes",
|
|
"myRelayMembership",
|
|
"oaOwner",
|
|
"presence",
|
|
"profile",
|
|
"pulse-note",
|
|
"pulse-reactions",
|
|
"pulse-timeline",
|
|
"relay-agents",
|
|
"relayMembers",
|
|
"reminders",
|
|
"run-approvals",
|
|
"search-messages",
|
|
"thread-replies",
|
|
"user-profile",
|
|
"user-search",
|
|
"user-status",
|
|
"users-batch",
|
|
"workflow",
|
|
"workflow-runs",
|
|
"workflows",
|
|
"workflows-all",
|
|
]);
|
|
|
|
const RELAY_PROJECT_QUERY_PARTS = new Set<string>([
|
|
"activity-summaries",
|
|
"issues",
|
|
"pull-requests",
|
|
// Fresh for two minutes (PROJECT_WORK_ITEMS_STALE_TIME_MS) and tolerant of
|
|
// partial fan-out failure — reconnect auto-heal is what repairs a partial
|
|
// result inside that window.
|
|
"work-items",
|
|
]);
|
|
|
|
const LOCAL_PROJECT_QUERY_PARTS = new Set<string>([
|
|
"commit-diff",
|
|
"local-repo-diff",
|
|
"local-repo-snapshot",
|
|
"local-repositories",
|
|
"repo-diff",
|
|
"repo-snapshot",
|
|
"repo-state",
|
|
"repo-sync-status",
|
|
]);
|
|
|
|
function isRelayDependentProjectQueryKey(queryKey: readonly unknown[]) {
|
|
if (queryKey[0] === "projects") {
|
|
const scope = queryKey[1];
|
|
if (scope === undefined) return true;
|
|
if (typeof scope !== "string") return false;
|
|
if (LOCAL_PROJECT_QUERY_PARTS.has(scope)) return false;
|
|
return RELAY_PROJECT_QUERY_PARTS.has(scope);
|
|
}
|
|
|
|
if (queryKey[0] === "project") {
|
|
const scope = queryKey[2];
|
|
if (scope === undefined) return true;
|
|
if (typeof scope !== "string") return false;
|
|
if (LOCAL_PROJECT_QUERY_PARTS.has(scope)) return false;
|
|
return RELAY_PROJECT_QUERY_PARTS.has(scope);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function isRelayDependentQueryKey(queryKey: readonly unknown[]) {
|
|
const root = queryKey[0];
|
|
if (typeof root !== "string") return false;
|
|
if (RELAY_QUERY_ROOTS.has(root)) return true;
|
|
return isRelayDependentProjectQueryKey(queryKey);
|
|
}
|
|
|
|
export function isRelayDependentQuery(query: { queryKey: readonly unknown[] }) {
|
|
return isRelayDependentQueryKey(query.queryKey);
|
|
}
|