/ concept-collection / turing-surface-cache
Sign in
concept-collection / turing-surface-cache
turing-surface-cache / src / cache / fillWalk.ts
87 lines · 3.2 KBCodeBlameHistory
2 * Working through the parameter space on an idle machine: for every target of
3 * the walk (src/cache/autoWalk.ts), skip whatever the cloud already has and
4 * compute and contribute the rest, until stopped.
5 *
6 * The loop is short but its rules matter, and they are the same in the page
7 * and on the command line. A skip costs one HEAD, and only the longest end
8 * time need be asked about, since a run reaching it emits every shorter one on
9 * the way. One bad combination — a compile that fails, a solution that blows
10 * up — is reported and stepped over rather than ending the walk. And every
11 * target goes through the ordinary local run, warm start, background uploads,
12 * divergence guard and all.
13 */
14import type { AutoTarget } from './autoWalk.ts';
15import { isCached, lookupFor } from './client.ts';
16import { runSpec, type RunEvents, type RunOutcome } from './runSpec.ts';
17import type { SolverSession } from './solver.ts';
18import type { CacheSpec } from './spec.ts';
20export interface FillCounts {
21 computed: number;
22 skipped: number;
23 failed: number;
26export interface FillEvents extends RunEvents {
27 /** A target is being considered; its cache status is not known yet. */
28 onTarget?(target: AutoTarget, spec: CacheSpec): void;
29 /** Already in the cloud — nothing to do. */
30 onCached?(target: AutoTarget, spec: CacheSpec): void;
31 /** Not cached: the run is about to start. */
32 onComputing?(target: AutoTarget, spec: CacheSpec): void;
33 onOutcome?(target: AutoTarget, spec: CacheSpec, outcome: RunOutcome): void;
34 onFailure?(target: AutoTarget, spec: CacheSpec, error: unknown): void;
35 /** True ends the walk at the next target boundary. */
36 walkStopped?(): boolean;
39export interface FillOptions {
40 targets: AutoTarget[];
41 solver: SolverSession;
42 adapter: string;
43 apiKey(): string;
44 /**
45 * Take the selection to this target and hand back the spec to compute. The
46 * page uses this to drive its own dropdowns and URL, so that what is on
47 * screen always says what is being computed.
48 */
49 beforeTarget(target: AutoTarget): CacheSpec | Promise<CacheSpec>;
50 events?: FillEvents;
53export async function fillWalk(opts: FillOptions): Promise<FillCounts> {
54 const ev = opts.events ?? {};
55 const counts: FillCounts = { computed: 0, skipped: 0, failed: 0 };
56 for (const target of opts.targets) {
57 if (ev.walkStopped?.()) break;
58 const spec = await opts.beforeTarget(target);
59 ev.onTarget?.(target, spec);
60 try {
61 if (await isCached(await lookupFor(spec))) {
62 counts.skipped++;
63 ev.onCached?.(target, spec);
64 continue;
65 }
66 if (ev.walkStopped?.()) break;
67 ev.onComputing?.(target, spec);
68 await opts.solver.apply(spec);
69 const outcome = await runSpec({
70 solver: opts.solver,
71 spec,
72 adapter: opts.adapter,
73 apiKey: opts.apiKey,
74 events: ev,
75 });
76 if (outcome.kind === 'done') counts.computed++;
77 else if (outcome.kind === 'diverged') counts.failed++;
78 ev.onOutcome?.(target, spec, outcome);
79 if (outcome.kind === 'abandoned') break;
80 } catch (e) {
81 // One bad combination must not end the walk: report it and move on.
82 counts.failed++;
83 ev.onFailure?.(target, spec, e);
84 }
85 }
86 return counts;
moveopenescclose