commit c661ab224d6fe5d7596ff72042b58caf358afc2b
parent 869c914abe607a9dea7664be75951dec9fdd9e13
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 22 Sep 2026 16:17:59 -0400
operations: the four-lane status poll reads the corpus once
buildAutoQueueStatusPayload already reads the channel listing and the
auto-queue state document for the rest of its payload, then asked four lanes
for their pending work — and each computeLeafPending listed every channel's
config off disk again (a readdir plus a read per channel, thirty on Jeralyzer)
and re-parsed the state. Nothing about either read is per-lane, and this runs
on a ~3 s poll.
computeLeafPending takes an optional `shared: { configs, state }`; omitted, it
reads both exactly as before, so every other caller is untouched. The page
passes the briefs it already has and the state it already parsed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
3 files changed, 95 insertions(+), 4 deletions(-)
diff --git a/common/controller/autoRunner.test.ts b/common/controller/autoRunner.test.ts
@@ -4,6 +4,7 @@ import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import {
+ computeLeafPending,
focusHoldLine,
laneDispatchRoot,
makeFocusHoldReporter,
@@ -19,6 +20,11 @@ import {
type FocusSummary,
} from "../lib/channelPriority";
import { LANES } from "../lib/autoQueueTypes";
+import {
+ emptyAutoQueueKindState,
+ emptyAutoQueueState,
+ type AutoQueueState,
+} from "../jobs/autoQueueState";
import type {
AutoQueueGroup,
AutoQueuePolicy,
@@ -479,3 +485,64 @@ test("a focus holds the scan the same way it holds every download pick", () => {
);
});
+
+// --- computeLeafPending's `shared`: the four-lane poll reads once -----------
+//
+// /operations asks all four lanes for their pending work on a ~3 s poll, and
+// each call used to list every channel's config off disk and re-parse the
+// auto-queue state — both of which the caller had already read for the rest of
+// the payload, and neither of which is per-lane.
+
+function pendingFixture(): Paths {
+ const dir = mkdtempSync(path.join(tmpdir(), "leaf-pending-"));
+ const channelsDir = path.join(dir, "channels");
+ // A channel that IS on disk. An injected listing has to be believed over it.
+ mkdirSync(path.join(channelsDir, "on-disk"), { recursive: true });
+ writeFileSync(
+ path.join(channelsDir, "on-disk", "config.json"),
+ JSON.stringify({ url: "https://www.youtube.com/@ondisk" }),
+ );
+ return {
+ channelsDir,
+ sitesDir: path.join(dir, "sites"),
+ autoQueueStateFile: path.join(dir, "auto-queue.json"),
+ } as Paths;
+}
+
+test("computeLeafPending takes its channel listing from `shared`", async () => {
+ const paths = pendingFixture();
+ const result = await computeLeafPending(LANES[0], paths, {
+ configs: [],
+ state: emptyAutoQueueState(),
+ });
+ // The leaves the compiled root defines, all at zero: an empty listing is no
+ // channels, so there is no work to attribute and nothing to pick.
+ assert.deepEqual(Object.values(result.counts), [0]);
+ assert.deepEqual(Object.values(result.head), [[]]);
+ assert.deepEqual(result.owner, {});
+ assert.equal(result.nextUp, null);
+});
+
+test("computeLeafPending reads the injected state, not the file", async () => {
+ const paths = pendingFixture();
+ // readAutoQueueState returns a FULLY POPULATED state even when the file is
+ // absent (emptyAutoQueueState), so a state missing this lane can only be the
+ // injected one. Dereferencing it is the proof that no second read happened.
+ const missingLane = Object.fromEntries(
+ LANES.filter((lane) => lane !== LANES[0]).map((lane) => [
+ lane,
+ emptyAutoQueueKindState(),
+ ]),
+ ) as unknown as AutoQueueState;
+ await assert.rejects(
+ computeLeafPending(LANES[0], paths, { configs: [], state: missingLane }),
+ TypeError,
+ );
+ // And with the lane present it goes through, off the same absent file.
+ await assert.doesNotReject(
+ computeLeafPending(LANES[0], paths, {
+ configs: [],
+ state: emptyAutoQueueState(),
+ }),
+ );
+});
diff --git a/common/controller/autoRunner.ts b/common/controller/autoRunner.ts
@@ -354,8 +354,12 @@ async function listChannelMeta(
paths: Paths,
kind: AutoQueueKind,
priority: ChannelPriority,
+ // A listing the CALLER already has. The status poll reads every channel's
+ // config once and then asked four lanes for their pending work, each of which
+ // re-read the whole directory — see computeLeafPending's `shared`.
+ sharedConfigs?: readonly { slug: string; config: ChannelConfig }[],
): Promise<{ meta: ChannelMeta[]; slugs: string[] }> {
- const configs = await listChannelConfigs(paths);
+ const configs = sharedConfigs ?? (await listChannelConfigs(paths));
return {
meta: configs
.filter(({ slug }) => !isChannelPaused(priority, slug, kind))
@@ -904,9 +908,21 @@ export type LeafPending = {
// operator sees as "cornbreadman: 12 pending". Uses the same matching AND the
// same ordering as the runner, so the numbers and the drill-down line up with
// what would actually be picked.
+//
+// `shared` IS THE FOUR-LANE POLL'S WAY OUT OF READING EVERYTHING FOUR TIMES.
+// /operations asks all four lanes on a ~3 s poll, and each call listed every
+// channel's config (a readdir plus a read per channel — 30 on Jeralyzer) and
+// parsed the auto-queue state document again, having already read both itself
+// for the rest of the payload. Nothing about either read is per-lane, so a
+// caller that holds them passes them in and the poll pays once. Omitted, each
+// is read here exactly as before, so every other caller is unchanged.
export async function computeLeafPending(
kind: AutoQueueKind,
paths: Paths = getPaths(),
+ shared?: {
+ configs?: readonly { slug: string; config: ChannelConfig }[];
+ state?: AutoQueueState;
+ },
): Promise<LeafPending> {
const settings = getSettings();
const policy = settings.autoQueue[kind];
@@ -920,6 +936,7 @@ export async function computeLeafPending(
paths,
kind,
settings.channelPriority,
+ shared?.configs,
);
const ctx = priorityContextFor(paths, settings, slugs);
const root = laneDispatchRoot(kind, policy, ctx, meta.map((m) => m.slug));
@@ -974,7 +991,7 @@ export async function computeLeafPending(
// runtime.currentWeights in place. This runs on a 3-second status poll, so
// asking the live runtime would let merely HAVING the page open skew a
// round-robin group's rotation. Deep-clone first; the clone is discarded.
- const state = await readAutoQueueState(paths);
+ const state = shared?.state ?? (await readAutoQueueState(paths));
const runtime = {
currentWeights: { ...state[kind].runtime.currentWeights },
};
diff --git a/editor/app/operations/status.ts b/editor/app/operations/status.ts
@@ -27,7 +27,8 @@ import { readPriorityView } from "./channelPriorityView";
// set costs a channel listing and, for a site focus, a sites read; the
// auto-queue state document is one JSON parse — it was read once PER LANE, four
// times per poll, because `buildKind` did its own reading; and the channel
-// briefs are shared with the lanes builder through the per-request cache.
+// briefs are shared with the lanes builder through the per-request cache, and
+// with the four computeLeafPending calls through their `shared` argument.
export async function buildAutoQueueStatusPayload(): Promise<AutoQueueStatusPayload> {
const paths = getPaths();
const settings = getSettings();
@@ -37,8 +38,14 @@ export async function buildAutoQueueStatusPayload(): Promise<AutoQueueStatusPayl
readAutoQueueState(paths),
getChannelBriefs(paths),
]);
+ // …and the four lanes' pending work re-reads NEITHER. `briefs` is the channel
+ // listing and `state` the auto-queue document, both already in hand one line
+ // up; without them each of the four calls listed every channel's config off
+ // disk again and re-parsed the state, on a ~3 s poll.
const pendingByKind = await Promise.all(
- LANES.map((lane) => computeLeafPending(lane, paths)),
+ LANES.map((lane) =>
+ computeLeafPending(lane, paths, { configs: briefs, state }),
+ ),
);
const byLane = <T>(values: readonly T[]): Record<AutoQueueKind, T> =>
Object.fromEntries(LANES.map((lane, i) => [lane, values[i]])) as Record<