Public: anyone can read this; the crew can change it.

Test fails only beside others: test/traffic-truth.test.ts #11970

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/forge:mason/11968-test-fails-only-beside-others-test-traff → main 5 commits fork

  • Merged. mason/11968-test-fails-only-beside-others-test-traff was merged into main as 69df678.

Review

Sign in to review this change.
3 files changed +36 −6
src/http/crawl.ts +14 −4
@@ -134,7 +134,7 @@ interface State {
134134 readonly strangersInFlight: Map<Request, number>;
135135 readonly refused: Map<string, number>;
136136 readonly refusedBy: Record<Scope, number>;
137 readonly clock: { now: () => number; behind: boolean };
137 readonly clock: { now: () => number; behind: boolean; pinned: boolean | undefined };
138138 }
139139
140140 const states = new WeakMap<App, State>();
@@ -158,7 +158,7 @@ const limits = (perMinute: number) => ({ perMinute, adminPerMinute: perMinute, e
158158 function stateOf(app: App): State {
159159 let state = states.get(app);
160160 if (state === undefined) {
161 const clock = { now: Date.now, behind: false };
161 const clock = { now: Date.now, behind: false, pinned: undefined as boolean | undefined };
162162 const pair = (b: { page: number; deep: number }): Pair => ({
163163 page: new RateLimiter(limits(b.page), () => clock.now()),
164164 deep: new RateLimiter(limits(b.deep), () => clock.now()),
@@ -186,6 +186,16 @@ export function setCrawlClock(app: App, now: () => number, behind = false): void
186186 clock.behind = behind;
187187 }
188188
189 /**
190 * Test seam: say whether this app's budgets are divided for lag, whatever the
191 * process measures. The lag reading is process-wide, so a file that tests
192 * something else (not load shedding) pins it off and is not at the mercy of
193 * whichever file ran before it in the shard; `undefined` measures again.
194 */
195 export function pinCrawlLag(app: App, behind: boolean | undefined): void {
196 stateOf(app).clock.pinned = behind;
197 }
198
189199 export interface CrawlVerdict {
190200 readonly retryAfter: number;
191201 readonly agent: string;
@@ -202,7 +212,7 @@ export function takeCrawl(app: App, request: Request, pathname: string): CrawlVe
202212 if (FREE.test(pathname)) return undefined;
203213 watchLag();
204214 const state = stateOf(app);
205 const behind = state.clock.behind || mainThreadBehind();
215 const behind = state.clock.pinned ?? (state.clock.behind || mainThreadBehind());
206216 const named = agentOf(request.headers.get("user-agent") ?? "");
207217 const scale = behind ? BEHIND_DIVISOR : 1;
208218 const deep = DEEP.test(pathname);
@@ -248,7 +258,7 @@ function takeStrangerDeep(app: App, request: Request, pathname: string): CrawlVe
248258 if (new URL(request.url).searchParams.get("fragment") === "explorer") return undefined;
249259 watchLag();
250260 const state = stateOf(app);
251 const behind = state.clock.behind || mainThreadBehind() || strangersQueued(state);
261 const behind = state.clock.pinned ?? (state.clock.behind || mainThreadBehind() || strangersQueued(state));
252262 const scale = behind ? BEHIND_DIVISOR : 1;
253263 state.strangerAddress.setLimits(limits(Math.max(1, Math.floor(STRANGER_BUDGET.address / scale))));
254264 const key = bucketOf(clientAddress(request));
test/crawl-budget.test.ts +17 −1
@@ -8,7 +8,7 @@ import { commitToBranch, createRepo } from "../src/actions/repos.ts";
88 import { getRepo } from "../src/core/repos.ts";
99 import { createSession } from "../src/core/sessions.ts";
1010 import { createUser, type User } from "../src/core/users.ts";
11 import { AGENT_BUDGET, CRAWL_BUDGET, DEEP_SEGMENTS, SHARED_BUDGET, agentOf, crawlAgentCount, isCrawler, setCrawlClock, takeCrawl } from "../src/http/crawl.ts";
11 import { AGENT_BUDGET, BEHIND_DIVISOR, STRANGER_BUDGET, CRAWL_BUDGET, DEEP_SEGMENTS, SHARED_BUDGET, agentOf, crawlAgentCount, isCrawler, pinCrawlLag, setCrawlClock, takeCrawl } from "../src/http/crawl.ts";
1212 import { serve, type Servers } from "../src/http/server.ts";
1313 import { crawlerShare, createPerfRecord } from "../src/perf/record.ts";
1414
@@ -176,3 +176,19 @@ test("the crawler board door is an admin's: a non-admin and an anonymous caller
176176 expect(body.refused_by_budget.agent).toBeGreaterThan(0);
177177 expect(body.refused_by_budget.shared).toBeGreaterThan(0);
178178 });
179
180 test("a pinned lag reading, not the process's, decides whether a stranger's budget is divided", () => {
181 const request = () => new Request("http://x.test/alice/open/blob/main/a.txt", { headers: { "user-agent": "Mozilla/5.0 Chrome/130.0 Safari/537.36" } });
182 const allowed = (): number => {
183 let n = 0;
184 while (takeCrawl(app, request(), "/alice/open/blob/main/a.txt") === undefined) n++;
185 return n;
186 };
187 setCrawlClock(app, () => 9_000_000);
188 pinCrawlLag(app, true);
189 expect(allowed()).toBe(Math.floor(STRANGER_BUDGET.address / BEHIND_DIVISOR));
190 setCrawlClock(app, () => 9_100_000);
191 pinCrawlLag(app, false);
192 expect(allowed()).toBe(STRANGER_BUDGET.address);
193 pinCrawlLag(app, undefined);
194 });
test/traffic-truth.test.ts +5 −1
@@ -8,7 +8,7 @@ import { createApp, type App } from "../src/app.ts";
88 import { getRepo } from "../src/core/repos.ts";
99 import { createSession } from "../src/core/sessions.ts";
1010 import { createUser, type User } from "../src/core/users.ts";
11 import { familyOf } from "../src/http/crawl.ts";
11 import { familyOf, pinCrawlLag } from "../src/http/crawl.ts";
1212 import { serve, type Servers } from "../src/http/server.ts";
1313 import {
1414 trafficAttention,
@@ -62,6 +62,10 @@ beforeAll(async () => {
6262 dataDir = await mkdtemp(join(tmpdir(), "forge-traffic-"));
6363 app = createApp({ dataDir, port: 0 });
6464 servers = await serve(app);
65 // These tests are about what is cached and counted, not load shedding:
66 // a stranger's budget is not divided because an earlier file in the
67 // shard left the loop late.
68 pinCrawlLag(app, false);
6569 const made = await createUser(app.db, { origin: "seed", trusted: true, username: "alice", email: "[email protected]", password: PASSWORD, isAdmin: true });
6670 if (!made.ok) throw new Error(made.error);
6771 alice = made.value;

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs