perf(primer): drop pre-fire usage refresh so the Codex primer fires exactly on the slot
The Codex primer awaited refreshAllCodexAccountUsage + refreshActiveCodexUsage before firing, purely to feed the old usage-based warm-up gating. With forceAttempt the primer fires regardless of usage, so those pre-call refreshes only added variable latency (the few-hundred-ms jitter that pushed the call off the exact slot) and a needless dependency on the usage API. Fire first at the slot; refresh once afterwards for observability (never delaying the call). The hourly usage collector keeps cached usage current. Verified: typecheck, build, bundle-smoke; usage-primer + codex-warmup suites pass (primer now fires before any refresh; refreshAll no longer called). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ vi.mock('./codex-warmup.js', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe('usage-primer', () => {
|
describe('usage-primer', () => {
|
||||||
it('refreshes Codex usage before primer selection and fires regardless of usage level', async () => {
|
it('fires the Codex primer first with forceAttempt and refreshes usage only afterwards', async () => {
|
||||||
callOrder.length = 0;
|
callOrder.length = 0;
|
||||||
refreshAllCodexAccountUsage.mockClear();
|
refreshAllCodexAccountUsage.mockClear();
|
||||||
refreshActiveCodexUsage.mockClear();
|
refreshActiveCodexUsage.mockClear();
|
||||||
@@ -63,7 +63,10 @@ describe('usage-primer', () => {
|
|||||||
const { runCodexPrimerCycle } = await import('./usage-primer.js');
|
const { runCodexPrimerCycle } = await import('./usage-primer.js');
|
||||||
await runCodexPrimerCycle();
|
await runCodexPrimerCycle();
|
||||||
|
|
||||||
expect(callOrder).toEqual(['refreshAll', 'refreshActive', 'warmup']);
|
// No pre-call usage refresh (that was the off-slot latency source); the
|
||||||
|
// command fires immediately, then usage is refreshed for observability.
|
||||||
|
expect(callOrder).toEqual(['warmup', 'refreshActive']);
|
||||||
|
expect(refreshAllCodexAccountUsage).not.toHaveBeenCalled();
|
||||||
expect(runCodexWarmupCycle).toHaveBeenCalledWith(
|
expect(runCodexWarmupCycle).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { CODEX_WARMUP_CONFIG } from './config.js';
|
import { CODEX_WARMUP_CONFIG } from './config.js';
|
||||||
import {
|
import { refreshActiveCodexUsage } from './codex-usage-collector.js';
|
||||||
refreshActiveCodexUsage,
|
|
||||||
refreshAllCodexAccountUsage,
|
|
||||||
} from './codex-usage-collector.js';
|
|
||||||
import { runCodexWarmupCycle } from './codex-warmup.js';
|
import { runCodexWarmupCycle } from './codex-warmup.js';
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import { getAllTokens } from './token-rotation.js';
|
import { getAllTokens } from './token-rotation.js';
|
||||||
@@ -153,8 +150,11 @@ async function runClaudePrimerCycle(): Promise<void> {
|
|||||||
|
|
||||||
export async function runCodexPrimerCycle(): Promise<void> {
|
export async function runCodexPrimerCycle(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await refreshAllCodexAccountUsage();
|
// Fire immediately at the slot. There is NO pre-call usage refresh on
|
||||||
await refreshActiveCodexUsage();
|
// purpose: forceAttempt fires regardless of usage, so reading usage first
|
||||||
|
// only added variable latency (the few-hundred-ms processing jitter that
|
||||||
|
// pushed the call off the exact slot) and a needless dependency on the
|
||||||
|
// usage API. The hourly usage collector keeps cached usage current.
|
||||||
const result = await runCodexWarmupCycle(
|
const result = await runCodexWarmupCycle(
|
||||||
{
|
{
|
||||||
...CODEX_WARMUP_CONFIG,
|
...CODEX_WARMUP_CONFIG,
|
||||||
@@ -164,17 +164,17 @@ export async function runCodexPrimerCycle(): Promise<void> {
|
|||||||
maxD7UsagePct: CODEX_PRIMER_MAX_D7_USAGE_PCT,
|
maxD7UsagePct: CODEX_PRIMER_MAX_D7_USAGE_PCT,
|
||||||
},
|
},
|
||||||
// forceAttempt: fire a real Codex command at *every* fixed slot,
|
// forceAttempt: fire a real Codex command at *every* fixed slot,
|
||||||
// bypassing the opportunistic warm-up skip gates. Without it, two slots
|
// bypassing the opportunistic warm-up skip gates (min-interval, stagger,
|
||||||
// exactly 5h apart fall just under minIntervalMs (the primer fires a few
|
// failure cooldown, usage/rate-limit filters).
|
||||||
// hundred ms after the slot) and the later slot is silently skipped.
|
|
||||||
{ ignoreZeroUsageWindow: true, forceAttempt: true },
|
{ ignoreZeroUsageWindow: true, forceAttempt: true },
|
||||||
);
|
);
|
||||||
// Best-effort: a slot may land while the trailing 5h window is exhausted,
|
|
||||||
// in which case this records `failed`. We deliberately do NOT try to
|
|
||||||
// re-anchor to the reported reset time: the 5h limit is a trailing rolling
|
|
||||||
// window whose reset slides forward with continued Codex usage, so no
|
|
||||||
// single timed call can pin the reset to a fixed clock time.
|
|
||||||
logger.info({ result }, 'Codex primer cycle finished');
|
logger.info({ result }, 'Codex primer cycle finished');
|
||||||
|
// Observability only — runs AFTER the call so it never delays it: refresh
|
||||||
|
// so the newly-anchored reset window shows up in usage data/logs. A slot
|
||||||
|
// may still land while the trailing 5h window is exhausted (records
|
||||||
|
// `failed`); we do not chase the reported reset time, since a trailing
|
||||||
|
// window keeps sliding with later usage.
|
||||||
|
await refreshActiveCodexUsage();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn({ err }, 'Codex primer cycle threw');
|
logger.warn({ err }, 'Codex primer cycle threw');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user