Pace engine reads under the proxy's rate limit
The proxy allows 10 requests a second per address and answers the rest 503. Limiting how many reads run at once still sent 40-50 a second, since each answers in about 100 ms. Reads now share one pace of 8 a second, actions skip the queue, and a 503 pauses every read for 2 seconds. Results and Schedule share each schedule's production status, and an archived schedule is read once until an action. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
import { EngineError, request, sleep } from '../api/client'
|
||||
import { t } from '../i18n'
|
||||
import { kindLabel, type ContentKind, type Rule } from './parse'
|
||||
import { expressAgents, MAX_ATTEMPTS, PLAN_ACTIVE, productionStatus, retryItem, runNow, type PlanItem, type Schedule } from './schedules'
|
||||
import { expressAgents, forgetProductionReads, MAX_ATTEMPTS, PLAN_ACTIVE, productionStatus, retryItem, runNow, type PlanItem, type Schedule } from './schedules'
|
||||
|
||||
const AGENTS = '/organizations/current/agents'
|
||||
|
||||
@@ -206,13 +206,27 @@ const PER_SCHEDULE_LIMIT = 4
|
||||
// The proxy's rate limit (503/429), a 5xx or no answer at all passes; a 4xx (e.g. a deleted agent) does not.
|
||||
const passing = (e: unknown) => e instanceof EngineError && (e.status === 0 || e.status === 429 || e.status >= 500)
|
||||
|
||||
const settledReads: Set<number>[] = []
|
||||
|
||||
/** After any action: an archived schedule's results (a decision on one) and production plans
|
||||
* (a started or retried piece) may have changed, so the next poll reads them again. */
|
||||
export function forgetReads() {
|
||||
for (const s of settledReads) s.clear()
|
||||
forgetProductionReads()
|
||||
}
|
||||
|
||||
/** Wraps a per-schedule read so a passing failure does not change what the page shows: a busy
|
||||
* engine is asked once more after a pause, and if it still fails, the schedule's results from the
|
||||
* last poll that read them are used. Dropping them instead made every count on the page dip for
|
||||
* one poll and come back on the next. */
|
||||
export function remembering<T>(fn: (s: Schedule) => Promise<T[]>, pause = 3000) {
|
||||
const last = new Map<number, T[]>()
|
||||
const settled = new Set<number>()
|
||||
settledReads.push(settled)
|
||||
return async (s: Schedule): Promise<T[]> => {
|
||||
// An archived schedule makes nothing new, so after one read it costs nothing until an action.
|
||||
const archived = s.agent.status === 'archived'
|
||||
if (archived && settled.has(s.agent.id)) return last.get(s.agent.id)!
|
||||
try {
|
||||
const read = await fn(s).catch(async e => {
|
||||
if (!(e instanceof EngineError && (e.status === 503 || e.status === 429))) throw e
|
||||
@@ -220,6 +234,7 @@ export function remembering<T>(fn: (s: Schedule) => Promise<T[]>, pause = 3000)
|
||||
return fn(s)
|
||||
})
|
||||
last.set(s.agent.id, read)
|
||||
if (archived) settled.add(s.agent.id)
|
||||
return read
|
||||
} catch (e) {
|
||||
const kept = last.get(s.agent.id)
|
||||
|
||||
Reference in New Issue
Block a user