mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:00:24 +08:00
109 lines
3.0 KiB
TypeScript
109 lines
3.0 KiB
TypeScript
import { createHash } from 'crypto';
|
|
import type { ExecutionSpec } from './execution';
|
|
import { cloneExecutionSpec } from './executionSpec';
|
|
import type { RunDispatchCandidate } from './runDispatchCandidate';
|
|
import {
|
|
assertRunDispatchLeaseRecord,
|
|
type RunDispatchLeaseRecord,
|
|
} from './runDispatchLease';
|
|
|
|
export const RUN_DISPATCH_OFFER_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
|
|
export interface RunDispatchPlan {
|
|
/** Untrusted until normalized by the Dispatcher. */
|
|
placement: unknown;
|
|
executionSpec: ExecutionSpec;
|
|
}
|
|
|
|
export interface ClaimedExecutionOffer {
|
|
/** Stable for one Attempt lease generation, including crash recovery. */
|
|
offerId: string;
|
|
/** Worker dedupe must reject the same offerId with a different digest. */
|
|
executionSpecDigest: string;
|
|
deliveryKind: 'new_claim' | 'lease_recovery';
|
|
candidate: RunDispatchCandidate;
|
|
worker: {
|
|
id: string;
|
|
sessionId: string;
|
|
generation: number;
|
|
};
|
|
/** Contains the opaque lease capability; never log or persist outside its owner. */
|
|
lease: RunDispatchLeaseRecord;
|
|
executionSpec: ExecutionSpec;
|
|
placementScore?: number;
|
|
}
|
|
|
|
export interface RunDispatcherStats {
|
|
recoveryPages: number;
|
|
recoveriesScanned: number;
|
|
recoveryPlansUnavailable: number;
|
|
candidatePages: number;
|
|
candidatesScanned: number;
|
|
workerPages: number;
|
|
workersScanned: number;
|
|
plansUnavailable: number;
|
|
matchingWorkers: number;
|
|
claimAttempts: number;
|
|
claimRaces: number;
|
|
}
|
|
|
|
export type RunDispatcherIdleReason =
|
|
| 'recovery_plans_unavailable'
|
|
| 'recovery_scan_budget_exhausted'
|
|
| 'no_candidates'
|
|
| 'no_workers'
|
|
| 'plans_unavailable'
|
|
| 'no_match'
|
|
| 'claim_raced'
|
|
| 'claim_budget_exhausted'
|
|
| 'scan_budget_exhausted';
|
|
|
|
export type RunDispatcherResult =
|
|
| {
|
|
status: 'offered';
|
|
offer: ClaimedExecutionOffer;
|
|
stats: RunDispatcherStats;
|
|
truncated: boolean;
|
|
}
|
|
| {
|
|
status: 'idle';
|
|
reason: RunDispatcherIdleReason;
|
|
stats: RunDispatcherStats;
|
|
truncated: boolean;
|
|
};
|
|
|
|
export function createRunDispatchOfferId(
|
|
lease: RunDispatchLeaseRecord,
|
|
): string {
|
|
assertRunDispatchLeaseRecord(lease);
|
|
if (lease.status !== 'leased') {
|
|
throw new TypeError(
|
|
'Execution offer requires an active Run dispatch lease',
|
|
);
|
|
}
|
|
return createHash('sha256')
|
|
.update('qinglong-run-dispatch-offer-v1\0', 'utf8')
|
|
.update(lease.attemptId, 'utf8')
|
|
.update('\0', 'utf8')
|
|
.update(String(lease.leaseGeneration), 'utf8')
|
|
.update('\0', 'utf8')
|
|
.update(lease.workerId, 'utf8')
|
|
.update('\0', 'utf8')
|
|
.update(lease.workerSessionId, 'utf8')
|
|
.update('\0', 'utf8')
|
|
.update(String(lease.workerGeneration), 'utf8')
|
|
.digest('hex');
|
|
}
|
|
|
|
export function createExecutionSpecDigest(spec: ExecutionSpec): string {
|
|
return createHash('sha256')
|
|
.update(JSON.stringify(cloneExecutionSpec(spec)), 'utf8')
|
|
.digest('hex');
|
|
}
|
|
|
|
export function assertRunDispatchOfferId(value: string): void {
|
|
if (!RUN_DISPATCH_OFFER_ID_PATTERN.test(value)) {
|
|
throw new TypeError('Run dispatch offer ID is invalid');
|
|
}
|
|
}
|