feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,419 @@
// Worker Execution owns crash-replayable Artifact upload and completion convergence.
import { createHash, timingSafeEqual } from 'node:crypto';
import type {
CompletionReceipt,
CompletionReceiptStore,
} from '@qinglong/local-process';
import {
normalizeWorkerRemoteExecutionInboxRecord,
type WorkerRemoteExecutionInbox,
type WorkerRemoteExecutionInboxRecord,
} from '../remote-execution/executionInbox';
import type { WorkerRemoteExecutionSession } from '../remote-execution/executionInboxProcessor';
import type {
WorkerRemoteLogArtifactReadLease,
WorkerRemoteLogArtifactSource,
} from './workerFileLogArtifactAllocator';
import type { RemoteWorkerExecutionFence } from '@qinglong/runtime-core/remote-worker-completion';
const SHA256 = /^[a-f0-9]{64}$/;
const COMPLETION_EVIDENCE_STATES = new Set([
'launching',
'started',
'running_acknowledged',
'recovery_required',
]);
export interface WorkerRemoteLogArtifactUploadCommand {
readonly workerId: RemoteWorkerExecutionFence['workerId'];
readonly workerSessionId: RemoteWorkerExecutionFence['workerSessionId'];
readonly workerGeneration: RemoteWorkerExecutionFence['workerGeneration'];
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly offerId: RemoteWorkerExecutionFence['offerId'];
readonly leaseGeneration: RemoteWorkerExecutionFence['leaseGeneration'];
readonly leaseToken: RemoteWorkerExecutionFence['leaseToken'];
readonly expectedLeaseVersion: RemoteWorkerExecutionFence['expectedLeaseVersion'];
readonly logArtifactId: string;
readonly byteLength: number;
readonly truncated: boolean | undefined;
readonly content: AsyncIterable<Uint8Array>;
}
export interface WorkerRemoteLogArtifactUploadResult {
readonly status: 'stored' | 'already_stored';
readonly logArtifactId: string;
readonly byteLength: number;
readonly sha256: string;
}
export interface WorkerRemoteLogArtifactUploader {
upload(
command: WorkerRemoteLogArtifactUploadCommand,
): Promise<Readonly<WorkerRemoteLogArtifactUploadResult>>;
}
export interface WorkerRemoteExecutionCompletionCommand {
readonly offerId: string;
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly callbackSequence: number;
readonly callbackTokenDigest: string;
readonly result: Readonly<{
outcome: 'succeeded' | 'failed';
startedAtMs: number;
finishedAtMs: number;
exitCode: number;
}>;
readonly artifact: Readonly<{
logArtifactId: string;
byteLength: number;
sha256: string;
truncated: boolean | undefined;
}>;
readonly executorType: string;
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly leaseGeneration: number;
readonly leaseToken: string;
readonly expectedLeaseVersion: number;
}
export interface WorkerRemoteExecutionCompletionResult {
readonly status: 'applied' | 'already_completed' | 'already_terminal';
readonly runId: string;
readonly attemptId: string;
readonly callbackSequence: number;
}
export interface WorkerRemoteExecutionCompletionClient {
complete(
command: WorkerRemoteExecutionCompletionCommand,
): Promise<Readonly<WorkerRemoteExecutionCompletionResult>>;
}
export type WorkerRemoteCompletionStatus =
| 'not_found'
| 'deferred'
| 'receipt_missing'
| 'receipt_unavailable'
| 'receipt_invalid'
| 'artifact_missing'
| 'completion_acknowledged'
| 'already_completed'
| 'control_plane_terminal';
export interface WorkerRemoteCompletionResult {
readonly offerId: string;
readonly status: WorkerRemoteCompletionStatus;
readonly receiptCleanup?: 'removed' | 'already_absent' | 'pending';
}
export interface WorkerRemoteCompletionCoordinatorOptions {
readonly currentSession: () => WorkerRemoteExecutionSession | undefined;
readonly now?: () => number;
}
export class WorkerRemoteCompletionCoordinatorError extends Error {
constructor(
readonly reason:
| 'invalid_configuration'
| 'artifact_response_invalid'
| 'completion_response_invalid',
) {
super(`Worker remote completion coordination failed: ${reason}`);
this.name = 'WorkerRemoteCompletionCoordinatorError';
}
}
/**
* Crash-replayable Worker completion pipeline. The local receipt is an
* authenticated capability, the log is uploaded before terminal mutation,
* and receipt deletion happens only after the inbox completion barrier.
*/
export class WorkerRemoteCompletionCoordinator {
private readonly currentSessionProvider: () =>
WorkerRemoteExecutionSession | undefined;
private readonly nowProvider: () => number;
private readonly inFlight = new Map<
string,
Promise<WorkerRemoteCompletionResult>
>();
constructor(
private readonly inbox: Pick<
WorkerRemoteExecutionInbox,
'readOffer' | 'replaceOffer'
>,
private readonly receipts: Pick<CompletionReceiptStore, 'read' | 'remove'>,
private readonly artifacts: WorkerRemoteLogArtifactSource,
private readonly uploader: WorkerRemoteLogArtifactUploader,
private readonly completion: WorkerRemoteExecutionCompletionClient,
options: WorkerRemoteCompletionCoordinatorOptions,
) {
if (
typeof inbox?.readOffer !== 'function' ||
typeof inbox?.replaceOffer !== 'function' ||
typeof receipts?.read !== 'function' ||
typeof receipts?.remove !== 'function' ||
typeof artifacts?.open !== 'function' ||
typeof uploader?.upload !== 'function' ||
typeof completion?.complete !== 'function' ||
typeof options?.currentSession !== 'function'
) {
throw new WorkerRemoteCompletionCoordinatorError('invalid_configuration');
}
this.currentSessionProvider = options.currentSession;
this.nowProvider = options.now ?? Date.now;
}
recover(offerId: string): Promise<WorkerRemoteCompletionResult> {
const active = this.inFlight.get(offerId);
if (active) return active;
const operation = this.process(offerId).finally(() => {
if (this.inFlight.get(offerId) === operation) this.inFlight.delete(offerId);
});
this.inFlight.set(offerId, operation);
return operation;
}
private async process(offerId: string): Promise<WorkerRemoteCompletionResult> {
const value = await this.inbox.readOffer(offerId);
if (!value) return Object.freeze({ offerId, status: 'not_found' });
let record = normalizeWorkerRemoteExecutionInboxRecord(value);
if (record.state === 'completion_acknowledged') {
return Object.freeze({
offerId,
status: 'already_completed',
receiptCleanup: await this.cleanup(record.offer.candidate.attemptId),
});
}
if (!this.canSubmit(record)) {
return Object.freeze({ offerId, status: 'deferred' });
}
let receipt: CompletionReceipt | undefined;
try {
receipt = await this.receipts.read(record.offer.candidate.attemptId);
} catch {
return Object.freeze({ offerId, status: 'receipt_unavailable' });
}
if (!receipt) return Object.freeze({ offerId, status: 'receipt_missing' });
try {
this.authenticate(record, receipt);
} catch {
return Object.freeze({ offerId, status: 'receipt_invalid' });
}
const artifact = await this.artifacts.open({
runId: record.offer.candidate.runId,
attemptId: record.offer.candidate.attemptId,
logArtifactId: record.logArtifactId!,
});
if (!artifact) return Object.freeze({ offerId, status: 'artifact_missing' });
const uploaded = await this.upload(record, artifact);
const completed = await this.completion.complete(Object.freeze({
offerId: record.offer.offerId,
projectId: record.offer.candidate.projectId,
runId: record.offer.candidate.runId,
attemptId: record.offer.candidate.attemptId,
callbackSequence: receipt.callbackSequence,
callbackTokenDigest: record.completionReceiptTokenDigest!,
result: Object.freeze({
outcome: receipt.exitCode === 0 ? 'succeeded' as const : 'failed' as const,
startedAtMs: receipt.startedAtMs,
finishedAtMs: receipt.finishedAtMs,
exitCode: receipt.exitCode,
}),
artifact: Object.freeze({
logArtifactId: uploaded.logArtifactId,
byteLength: uploaded.byteLength,
sha256: uploaded.sha256,
truncated: artifact.truncated,
}),
executorType: record.offer.candidate.executorType,
workerId: record.offer.lease.workerId,
workerSessionId: record.offer.lease.workerSessionId,
workerGeneration: record.offer.lease.workerGeneration,
leaseGeneration: record.offer.lease.leaseGeneration,
leaseToken: record.offer.leaseToken,
expectedLeaseVersion: record.offer.lease.version,
}));
this.assertCompletionResponse(record, receipt, completed);
if (completed.status === 'already_terminal') {
return Object.freeze({ offerId, status: 'control_plane_terminal' });
}
record = await this.markAcknowledged(record);
return Object.freeze({
offerId,
status: 'completion_acknowledged',
receiptCleanup: await this.cleanup(record.offer.candidate.attemptId),
});
}
private canSubmit(record: WorkerRemoteExecutionInboxRecord): boolean {
if (!COMPLETION_EVIDENCE_STATES.has(record.state)) return false;
if (
record.state === 'recovery_required' &&
record.recoveryReason !== 'launch_outcome_unknown'
) return false;
const current = this.currentSessionProvider();
const now = this.now();
return Boolean(
current &&
current.workerId === record.offer.worker.workerId &&
current.sessionId === record.offer.worker.sessionId &&
current.generation === record.offer.worker.generation &&
current.status !== 'offline' &&
current.leaseExpiresAtMs > now &&
record.offer.lease.expiresAtMs > now &&
record.executorStartedAtMs !== undefined &&
record.logArtifactId !== undefined &&
record.completionReceiptCallbackSequence !== undefined &&
record.completionReceiptTokenDigest !== undefined
);
}
private authenticate(
record: WorkerRemoteExecutionInboxRecord,
receipt: CompletionReceipt,
): void {
if (
receipt.runId !== record.offer.candidate.runId ||
receipt.attemptId !== record.offer.candidate.attemptId ||
receipt.callbackSequence !== record.completionReceiptCallbackSequence ||
receipt.startedAtMs !== record.executorStartedAtMs ||
receipt.finishedAtMs > this.now() ||
!record.completionReceiptTokenDigest ||
!SHA256.test(record.completionReceiptTokenDigest)
) throw new Error('Completion receipt authority does not match');
const token = Buffer.from(receipt.token, 'base64url');
try {
if (token.byteLength !== 32 || token.toString('base64url') !== receipt.token) {
throw new Error('Completion receipt capability is not canonical');
}
const expected = Buffer.from(record.completionReceiptTokenDigest, 'hex');
const actual = createHash('sha256').update(token).digest();
if (!timingSafeEqual(expected, actual)) {
throw new Error('Completion receipt capability does not match');
}
} finally {
token.fill(0);
}
}
private async upload(
record: WorkerRemoteExecutionInboxRecord,
artifact: WorkerRemoteLogArtifactReadLease,
): Promise<WorkerRemoteLogArtifactUploadResult> {
const digest = createHash('sha256');
let observedBytes = 0;
const content = (async function* () {
for await (const chunk of artifact.chunks()) {
observedBytes += chunk.byteLength;
digest.update(chunk);
yield chunk;
}
})();
let result: Readonly<WorkerRemoteLogArtifactUploadResult>;
try {
result = await this.uploader.upload(Object.freeze({
workerId: record.offer.lease.workerId,
workerSessionId: record.offer.lease.workerSessionId,
workerGeneration: record.offer.lease.workerGeneration,
projectId: record.offer.candidate.projectId,
runId: record.offer.candidate.runId,
attemptId: record.offer.candidate.attemptId,
offerId: record.offer.offerId,
leaseGeneration: record.offer.lease.leaseGeneration,
leaseToken: record.offer.leaseToken,
expectedLeaseVersion: record.offer.lease.version,
logArtifactId: artifact.logArtifactId,
byteLength: artifact.byteLength,
truncated: artifact.truncated,
content,
}));
} finally {
await artifact.close();
}
const actualDigest = digest.digest('hex');
if (
observedBytes !== artifact.byteLength ||
(result?.status !== 'stored' && result?.status !== 'already_stored') ||
result.logArtifactId !== artifact.logArtifactId ||
result.byteLength !== artifact.byteLength ||
!SHA256.test(result.sha256) ||
result.sha256 !== actualDigest
) {
throw new WorkerRemoteCompletionCoordinatorError(
'artifact_response_invalid',
);
}
return Object.freeze({ ...result });
}
private assertCompletionResponse(
record: WorkerRemoteExecutionInboxRecord,
receipt: CompletionReceipt,
result: Readonly<WorkerRemoteExecutionCompletionResult>,
): void {
if (
!['applied', 'already_completed', 'already_terminal'].includes(result?.status) ||
result.runId !== record.offer.candidate.runId ||
result.attemptId !== record.offer.candidate.attemptId ||
result.callbackSequence !== receipt.callbackSequence
) {
throw new WorkerRemoteCompletionCoordinatorError(
'completion_response_invalid',
);
}
}
private async markAcknowledged(
record: WorkerRemoteExecutionInboxRecord,
): Promise<WorkerRemoteExecutionInboxRecord> {
const updatedAtMs = Math.max(this.now(), record.updatedAtMs);
const { recoveryReason: _recoveryReason, ...authority } = record;
const next = normalizeWorkerRemoteExecutionInboxRecord({
...authority,
revision: record.revision + 1,
state: 'completion_acknowledged',
updatedAtMs,
completionAcknowledgedAtMs: updatedAtMs,
});
try {
await this.inbox.replaceOffer(next, record.revision);
return next;
} catch (error) {
const current = await this.inbox.readOffer(record.offer.offerId);
if (current) {
const normalized = normalizeWorkerRemoteExecutionInboxRecord(current);
if (normalized.state === 'completion_acknowledged') return normalized;
}
throw error;
}
}
private async cleanup(
attemptId: string,
): Promise<'removed' | 'already_absent' | 'pending'> {
try {
return (await this.receipts.remove(attemptId))
? 'removed'
: 'already_absent';
} catch {
return 'pending';
}
}
private now(): number {
const value = this.nowProvider();
if (!Number.isSafeInteger(value) || value < 0) {
throw new WorkerRemoteCompletionCoordinatorError('invalid_configuration');
}
return value;
}
}
@@ -0,0 +1,415 @@
// Worker Execution owns lease renewal, stop fencing, and completion convergence.
import type {
LocalProcessController,
LocalProcessStopResult,
} from '@qinglong/local-process';
import type { RemoteWorkerLeaseControlResult } from '@qinglong/runtime-core/remote-worker-lease-control';
import {
normalizeWorkerRemoteExecutionInboxRecord,
type WorkerRemoteExecutionInbox,
type WorkerRemoteExecutionInboxRecord,
} from '../remote-execution/executionInbox';
import type { WorkerRemoteExecutionSession } from '../remote-execution/executionInboxProcessor';
import type {
WorkerRemoteCompletionResult,
WorkerRemoteCompletionStatus,
} from './workerCompletionCoordinator';
import type { WorkerRemoteLeaseControlClient } from '../remote-execution/transport/remoteWorkerLeaseControlHttpsClient';
export interface WorkerRemoteExecutionControlCoordinatorOptions {
readonly currentSession: () => WorkerRemoteExecutionSession | undefined;
readonly now?: () => number;
}
export type WorkerRemoteExecutionControlResult = Readonly<{
readonly offerId: string;
readonly completionStatus?: WorkerRemoteCompletionStatus;
} & (
| { readonly status: 'not_found' }
| { readonly status: 'completion_acknowledged' }
| {
readonly status: 'renewed';
readonly leaseVersion: number;
readonly expiresAtMs: number;
}
| {
readonly status: 'stop_requested' | 'stop_unverified';
readonly reason: NonNullable<RemoteWorkerLeaseControlResult['stop']>['reason'];
readonly leaseVersion: number;
readonly expiresAtMs: number;
readonly stop: LocalProcessStopResult;
}
| {
readonly status: 'terminal';
readonly terminalStatus: NonNullable<
RemoteWorkerLeaseControlResult['terminalStatus']
>;
readonly stop: LocalProcessStopResult;
}
| {
readonly status: 'completion_terminal';
readonly stop: LocalProcessStopResult;
}
| {
readonly status: 'lease_expired';
readonly stop: LocalProcessStopResult;
readonly recoveryReason:
| 'lease_lost_local_execution_stopped'
| 'lease_lost_local_execution_unverified';
}
| { readonly status: 'session_unavailable' }
)>;
export class WorkerRemoteExecutionControlCoordinatorError extends Error {
constructor(
readonly reason:
| 'invalid_configuration'
| 'invalid_clock'
| 'lease_response_invalid',
options?: ErrorOptions,
) {
super(`Worker remote execution control failed: ${reason}`, options);
this.name = 'WorkerRemoteExecutionControlCoordinatorError';
}
}
const CONTROL_PLANE_TERMINAL_TRANSITIONS = new Set([
'accepted',
'starting_acknowledged',
'launching',
'started',
'running_acknowledged',
'start_failed',
'recovery_required',
]);
/**
* One bounded, timer-free supervision step for an accepted remote execution.
* Completion evidence is replayed first; live authority is then renewed or an
* exact durable process identity is stopped before recovery evidence is stored.
*/
export class WorkerRemoteExecutionControlCoordinator {
private readonly currentSessionProvider: () =>
WorkerRemoteExecutionSession | undefined;
private readonly nowProvider: () => number;
private readonly inFlight = new Map<
string,
Promise<WorkerRemoteExecutionControlResult>
>();
constructor(
private readonly inbox: Pick<
WorkerRemoteExecutionInbox,
'readOffer' | 'replaceOffer'
>,
private readonly completion: Pick<
{ recover(offerId: string): Promise<WorkerRemoteCompletionResult> },
'recover'
>,
private readonly leaseControl: WorkerRemoteLeaseControlClient,
private readonly processes: Pick<LocalProcessController, 'stop'>,
options: WorkerRemoteExecutionControlCoordinatorOptions,
) {
if (
typeof inbox?.readOffer !== 'function' ||
typeof inbox?.replaceOffer !== 'function' ||
typeof completion?.recover !== 'function' ||
typeof leaseControl?.control !== 'function' ||
typeof processes?.stop !== 'function' ||
typeof options?.currentSession !== 'function' ||
(options.now !== undefined && typeof options.now !== 'function')
) {
throw new WorkerRemoteExecutionControlCoordinatorError(
'invalid_configuration',
);
}
this.currentSessionProvider = options.currentSession;
this.nowProvider = options.now ?? Date.now;
}
reconcile(offerId: string): Promise<WorkerRemoteExecutionControlResult> {
const active = this.inFlight.get(offerId);
if (active) return active;
const operation = this.reconcileOnce(offerId).finally(() => {
if (this.inFlight.get(offerId) === operation) this.inFlight.delete(offerId);
});
this.inFlight.set(offerId, operation);
return operation;
}
private async reconcileOnce(
offerId: string,
): Promise<WorkerRemoteExecutionControlResult> {
const value = await this.inbox.readOffer(offerId);
if (!value) return Object.freeze({ offerId, status: 'not_found' as const });
let record = normalizeWorkerRemoteExecutionInboxRecord(value);
if (record.state === 'completion_acknowledged') {
return Object.freeze({
offerId,
status: 'completion_acknowledged' as const,
});
}
let replay: WorkerRemoteCompletionResult | undefined;
try {
replay = await this.completion.recover(offerId);
} catch {
// Completion and lease transports are intentionally isolated: a failed
// Artifact upload must not prevent the Worker from retaining authority.
}
if (
replay?.status === 'completion_acknowledged' ||
replay?.status === 'already_completed'
) {
return Object.freeze({
offerId,
status: 'completion_acknowledged' as const,
completionStatus: replay.status,
});
}
record = await this.readRequired(offerId);
const completionStatus = replay?.status;
if (replay?.status === 'control_plane_terminal') {
const stop = await this.stop(record);
await this.markControlPlaneTerminal(record);
return Object.freeze({
offerId,
status: 'completion_terminal' as const,
stop,
completionStatus,
});
}
const now = this.now();
if (record.offer.lease.expiresAtMs <= now) {
const stop = await this.stop(record);
const recoveryReason = this.stopWasConclusive(stop)
? 'lease_lost_local_execution_stopped' as const
: 'lease_lost_local_execution_unverified' as const;
await this.markLeaseLost(record, recoveryReason);
return Object.freeze({
offerId,
status: 'lease_expired' as const,
stop,
recoveryReason,
...(completionStatus === undefined ? {} : { completionStatus }),
});
}
if (!this.sessionMatches(record, now)) {
return Object.freeze({
offerId,
status: 'session_unavailable' as const,
...(completionStatus === undefined ? {} : { completionStatus }),
});
}
const control = await this.leaseControl.control(Object.freeze({
workerId: record.offer.lease.workerId,
workerSessionId: record.offer.lease.workerSessionId,
workerGeneration: record.offer.lease.workerGeneration,
projectId: record.offer.candidate.projectId,
runId: record.offer.candidate.runId,
attemptId: record.offer.candidate.attemptId,
offerId: record.offer.offerId,
leaseGeneration: record.offer.lease.leaseGeneration,
leaseToken: record.offer.leaseToken,
expectedLeaseVersion: record.offer.lease.version,
}));
this.assertAuthority(record, control);
if (control.status === 'terminal') {
const stop = await this.stop(record);
await this.markControlPlaneTerminal(record);
return Object.freeze({
offerId,
status: 'terminal' as const,
terminalStatus: control.terminalStatus!,
stop,
...(completionStatus === undefined ? {} : { completionStatus }),
});
}
record = await this.persistRenewedLease(record, control);
if (control.status === 'renewed') {
return Object.freeze({
offerId,
status: 'renewed' as const,
leaseVersion: record.offer.lease.version,
expiresAtMs: record.offer.lease.expiresAtMs,
...(completionStatus === undefined ? {} : { completionStatus }),
});
}
const stop = await this.stop(record);
return Object.freeze({
offerId,
status: this.stopWasConclusive(stop)
? 'stop_requested' as const
: 'stop_unverified' as const,
reason: control.stop!.reason,
leaseVersion: record.offer.lease.version,
expiresAtMs: record.offer.lease.expiresAtMs,
stop,
...(completionStatus === undefined ? {} : { completionStatus }),
});
}
private assertAuthority(
record: WorkerRemoteExecutionInboxRecord,
result: Readonly<RemoteWorkerLeaseControlResult>,
): void {
if (
result.projectId !== record.offer.candidate.projectId ||
result.runId !== record.offer.candidate.runId ||
result.attemptId !== record.offer.candidate.attemptId ||
result.offerId !== record.offer.offerId ||
result.leaseGeneration !== record.offer.lease.leaseGeneration ||
(result.status !== 'terminal' &&
result.leaseVersion !== record.offer.lease.version + 1)
) {
throw new WorkerRemoteExecutionControlCoordinatorError(
'lease_response_invalid',
);
}
}
private async persistRenewedLease(
record: WorkerRemoteExecutionInboxRecord,
result: Readonly<RemoteWorkerLeaseControlResult>,
): Promise<WorkerRemoteExecutionInboxRecord> {
if (
result.leaseVersion === undefined ||
result.renewedAtMs === undefined ||
result.expiresAtMs === undefined ||
result.renewedAtMs < record.offer.lease.renewedAtMs ||
result.renewedAtMs < record.offer.lease.updatedAtMs ||
result.expiresAtMs <= result.renewedAtMs
) {
throw new WorkerRemoteExecutionControlCoordinatorError(
'lease_response_invalid',
);
}
const next = normalizeWorkerRemoteExecutionInboxRecord({
...record,
revision: record.revision + 1,
updatedAtMs: Math.max(record.updatedAtMs, result.renewedAtMs),
offer: {
...record.offer,
lease: {
...record.offer.lease,
version: result.leaseVersion,
renewedAtMs: result.renewedAtMs,
expiresAtMs: result.expiresAtMs,
updatedAtMs: result.renewedAtMs,
},
},
});
try {
await this.inbox.replaceOffer(next, record.revision);
return next;
} catch (error) {
const current = await this.inbox.readOffer(record.offer.offerId);
if (current) {
const normalized = normalizeWorkerRemoteExecutionInboxRecord(current);
if (normalized.offer.lease.version >= result.leaseVersion) {
return normalized;
}
}
throw error;
}
}
private async markLeaseLost(
record: WorkerRemoteExecutionInboxRecord,
recoveryReason:
| 'lease_lost_local_execution_stopped'
| 'lease_lost_local_execution_unverified',
): Promise<void> {
if (record.state === 'recovery_required') return;
if (record.state === 'start_failure_acknowledged') return;
await this.replaceRecovery(record, recoveryReason);
}
private async markControlPlaneTerminal(
record: WorkerRemoteExecutionInboxRecord,
): Promise<void> {
if (
record.state === 'recovery_required' ||
!CONTROL_PLANE_TERMINAL_TRANSITIONS.has(record.state)
) return;
await this.replaceRecovery(record, 'control_plane_terminal');
}
private async replaceRecovery(
record: WorkerRemoteExecutionInboxRecord,
recoveryReason: WorkerRemoteExecutionInboxRecord['recoveryReason'],
): Promise<void> {
const next = normalizeWorkerRemoteExecutionInboxRecord({
...record,
revision: record.revision + 1,
state: 'recovery_required',
recoveryReason,
updatedAtMs: Math.max(record.updatedAtMs, this.now()),
});
try {
await this.inbox.replaceOffer(next, record.revision);
} catch (error) {
const current = await this.inbox.readOffer(record.offer.offerId);
if (
current &&
normalizeWorkerRemoteExecutionInboxRecord(current).state ===
'recovery_required'
) return;
throw error;
}
}
private async stop(
record: WorkerRemoteExecutionInboxRecord,
): Promise<LocalProcessStopResult> {
if (!record.executorHandle) {
return record.state === 'launching'
? Object.freeze({ status: 'unknown' as const, reason: 'invalid_handle' as const })
: Object.freeze({ status: 'already_exited' as const });
}
return this.processes.stop(record.executorHandle);
}
private stopWasConclusive(result: LocalProcessStopResult): boolean {
return result.status === 'stopped' || result.status === 'already_exited';
}
private sessionMatches(
record: WorkerRemoteExecutionInboxRecord,
now: number,
): boolean {
const session = this.currentSessionProvider();
return Boolean(
session &&
session.workerId === record.offer.worker.workerId &&
session.sessionId === record.offer.worker.sessionId &&
session.generation === record.offer.worker.generation &&
session.status !== 'offline' &&
session.leaseExpiresAtMs > now
);
}
private async readRequired(
offerId: string,
): Promise<WorkerRemoteExecutionInboxRecord> {
const value = await this.inbox.readOffer(offerId);
if (!value) {
throw new WorkerRemoteExecutionControlCoordinatorError(
'invalid_configuration',
);
}
return normalizeWorkerRemoteExecutionInboxRecord(value);
}
private now(): number {
const value = this.nowProvider();
if (!Number.isSafeInteger(value) || value < 0) {
throw new WorkerRemoteExecutionControlCoordinatorError('invalid_clock');
}
return value;
}
}
@@ -0,0 +1,642 @@
// Worker Execution owns bounded local log allocation, output, and read leases.
import { createHash } from 'node:crypto';
import { constants } from 'node:fs';
import fs, { type FileHandle } from 'node:fs/promises';
import path from 'node:path';
import { assertRunDispatchId } from '@qinglong/runtime-core/run-dispatch-lease';
import type {
WorkerRemoteExecutionOutputChunk,
WorkerRemoteExecutionOutputSink,
} from '../remote-execution/executionInboxProcessor';
import type {
WorkerRemoteLogArtifactAllocator,
WorkerRemoteLogArtifactPreparation,
} from '../remote-execution/executionContextMaterializer';
const MEBIBYTE = 1024 * 1024;
const MAXIMUM_POLICY_BYTES = 1024 * MEBIBYTE;
const MAXIMUM_RESERVE_BYTES = 1024 * 1024 * MEBIBYTE;
const ARTIFACT_ID_PREFIX = 'wlog-';
const ARTIFACT_ID_DIGEST_LENGTH = 30;
const ARTIFACT_ID_DOMAIN = 'qinglong/worker-log-artifact@v1';
const WORKER_FILE_LOG_OUTPUT_PLAN = Symbol('worker-file-log-output-plan');
export type WorkerRemoteLogArtifactProfile = 'edge' | 'node';
export interface WorkerRemoteLogArtifactPolicy {
readonly maximumAttemptBytes: number;
readonly minimumFreeBytes: number;
readonly maximumWriteChunkBytes: number;
}
export interface WorkerRemoteLogArtifactCapacityProbe {
availableBytes(root: string): Promise<bigint>;
}
export interface WorkerFileLogArtifactAllocatorOptions {
readonly root: string;
readonly policy: WorkerRemoteLogArtifactPolicy;
readonly capacity?: WorkerRemoteLogArtifactCapacityProbe;
}
export interface WorkerFileLogOutputPlan {
readonly filePath: string;
readonly maximumBytes: number;
readonly logArtifactId: string;
}
export interface WorkerRemoteLogArtifactReadRequest {
readonly runId: string;
readonly attemptId: string;
readonly logArtifactId: string;
}
export interface WorkerRemoteLogArtifactReadLease {
readonly logArtifactId: string;
readonly byteLength: number;
/** Undefined means the launcher's bounded truncation fact was unavailable. */
readonly truncated: boolean | undefined;
chunks(): AsyncIterable<Uint8Array>;
close(): Promise<void>;
}
export interface WorkerRemoteLogArtifactSource {
open(
request: WorkerRemoteLogArtifactReadRequest,
): Promise<WorkerRemoteLogArtifactReadLease | undefined>;
}
type PlannedWorkerRemoteExecutionOutputSink = WorkerRemoteExecutionOutputSink & {
readonly [WORKER_FILE_LOG_OUTPUT_PLAN]?: () => WorkerFileLogOutputPlan;
};
/** Adapter-private path capability; it is non-enumerable on the output sink. */
export function workerFileLogOutputPlan(
output: WorkerRemoteExecutionOutputSink,
): Readonly<WorkerFileLogOutputPlan> | undefined {
return (output as PlannedWorkerRemoteExecutionOutputSink)[
WORKER_FILE_LOG_OUTPUT_PLAN
]?.();
}
export class WorkerRemoteLogArtifactError extends Error {
constructor(
readonly reason:
| 'invalid_configuration'
| 'invalid_request'
| 'capacity_unavailable'
| 'unsafe_path'
| 'quota_exceeded'
| 'invalid_output'
| 'closed',
) {
super(`Worker remote log Artifact failed: ${reason}`);
this.name = 'WorkerRemoteLogArtifactError';
}
}
export function workerRemoteLogArtifactPolicy(
profile: WorkerRemoteLogArtifactProfile,
): Readonly<WorkerRemoteLogArtifactPolicy> {
if (profile === 'edge') {
return Object.freeze({
maximumAttemptBytes: 4 * MEBIBYTE,
minimumFreeBytes: 32 * MEBIBYTE,
maximumWriteChunkBytes: MEBIBYTE,
});
}
if (profile === 'node') {
return Object.freeze({
maximumAttemptBytes: 64 * MEBIBYTE,
minimumFreeBytes: 256 * MEBIBYTE,
maximumWriteChunkBytes: MEBIBYTE,
});
}
throw new WorkerRemoteLogArtifactError('invalid_configuration');
}
function normalizePolicy(
policy: WorkerRemoteLogArtifactPolicy,
): Readonly<WorkerRemoteLogArtifactPolicy> {
if (
!policy ||
!Number.isSafeInteger(policy.maximumAttemptBytes) ||
policy.maximumAttemptBytes < 1 ||
policy.maximumAttemptBytes > MAXIMUM_POLICY_BYTES ||
!Number.isSafeInteger(policy.minimumFreeBytes) ||
policy.minimumFreeBytes < 0 ||
policy.minimumFreeBytes > MAXIMUM_RESERVE_BYTES ||
!Number.isSafeInteger(policy.maximumWriteChunkBytes) ||
policy.maximumWriteChunkBytes < 1 ||
policy.maximumWriteChunkBytes > MEBIBYTE
) {
throw new WorkerRemoteLogArtifactError('invalid_configuration');
}
return Object.freeze({
maximumAttemptBytes: policy.maximumAttemptBytes,
minimumFreeBytes: policy.minimumFreeBytes,
maximumWriteChunkBytes: policy.maximumWriteChunkBytes,
});
}
function currentUid(): number | undefined {
return typeof process.getuid === 'function' ? process.getuid() : undefined;
}
function assertOwnedOrdinaryFile(stat: Awaited<ReturnType<FileHandle['stat']>>): void {
const uid = currentUid();
if (
!stat.isFile() ||
stat.nlink !== 1 ||
(uid !== undefined && stat.uid !== uid)
) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
}
async function privateDirectory(directory: string): Promise<Readonly<{
dev: number;
ino: number;
}>> {
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
const before = await fs.lstat(directory);
const uid = currentUid();
if (
!before.isDirectory() ||
before.isSymbolicLink() ||
(uid !== undefined && before.uid !== uid)
) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
await fs.chmod(directory, 0o700);
const after = await fs.lstat(directory);
if (
!after.isDirectory() ||
after.isSymbolicLink() ||
after.dev !== before.dev ||
after.ino !== before.ino ||
(after.mode & 0o777) !== 0o700 ||
(uid !== undefined && after.uid !== uid)
) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
return Object.freeze({ dev: after.dev, ino: after.ino });
}
function requestId(name: string, value: unknown): string {
try {
if (typeof value !== 'string') {
throw new TypeError('invalid ID');
}
assertRunDispatchId(name, value);
return value;
} catch {
throw new WorkerRemoteLogArtifactError('invalid_request');
}
}
export function createWorkerRemoteLogArtifactId(request: Readonly<{
projectId: string;
runId: string;
attemptId: string;
offerId: string;
}>): string {
if (!request || typeof request !== 'object') {
throw new WorkerRemoteLogArtifactError('invalid_request');
}
const values = [
requestId('projectId', request.projectId),
requestId('runId', request.runId),
requestId('attemptId', request.attemptId),
requestId('offerId', request.offerId),
];
const digest = createHash('sha256');
digest.update(ARTIFACT_ID_DOMAIN, 'utf8');
for (const value of values) {
digest.update('\0', 'utf8');
digest.update(value, 'utf8');
}
const artifactId = `${ARTIFACT_ID_PREFIX}${digest.digest('hex').slice(
0,
ARTIFACT_ID_DIGEST_LENGTH,
)}`;
assertRunDispatchId('logArtifactId', artifactId);
return artifactId;
}
class FileSystemCapacityProbe implements WorkerRemoteLogArtifactCapacityProbe {
async availableBytes(root: string): Promise<bigint> {
const stat = await fs.statfs(root, { bigint: true });
return stat.bavail * stat.bsize;
}
}
class WorkerFileLogOutput implements WorkerRemoteExecutionOutputSink {
private pending: Promise<unknown> = Promise.resolve();
private closeOperation: Promise<void> | undefined;
private state: 'open' | 'closing' | 'closed' = 'open';
private remainingBytes: number;
constructor(
readonly logArtifactId: string,
private readonly file: FileHandle,
maximumBytes: number,
existingBytes: number,
private readonly maximumWriteChunkBytes: number,
outputFilePath: string,
) {
this.remainingBytes = maximumBytes - existingBytes;
Object.defineProperty(this, WORKER_FILE_LOG_OUTPUT_PLAN, {
configurable: false,
enumerable: false,
writable: false,
value: () => Object.freeze({
filePath: outputFilePath,
maximumBytes,
logArtifactId,
}),
});
}
write(output: WorkerRemoteExecutionOutputChunk): Promise<void> {
if (this.state !== 'open') {
return Promise.reject(new WorkerRemoteLogArtifactError('closed'));
}
if (
!output ||
typeof output !== 'object' ||
(output.stream !== 'stdout' && output.stream !== 'stderr') ||
!(output.chunk instanceof Uint8Array) ||
output.chunk.byteLength > this.maximumWriteChunkBytes ||
!Number.isSafeInteger(output.observedAtMs) ||
output.observedAtMs < 0
) {
return Promise.reject(new WorkerRemoteLogArtifactError('invalid_output'));
}
const chunk = Buffer.from(output.chunk);
const operation = this.pending.then(async () => {
if (chunk.byteLength === 0) return;
if (this.remainingBytes <= 0) {
throw new WorkerRemoteLogArtifactError('quota_exceeded');
}
const accepted = chunk.subarray(
0,
Math.min(chunk.byteLength, this.remainingBytes),
);
let offset = 0;
while (offset < accepted.byteLength) {
const result = await this.file.write(accepted.subarray(offset));
if (result.bytesWritten < 1) {
throw new Error('Worker remote log Artifact write made no progress');
}
offset += result.bytesWritten;
this.remainingBytes -= result.bytesWritten;
}
if (accepted.byteLength !== chunk.byteLength) {
throw new WorkerRemoteLogArtifactError('quota_exceeded');
}
});
this.pending = operation.catch(() => undefined);
return operation;
}
close(): Promise<void> {
if (this.closeOperation) return this.closeOperation;
this.state = 'closing';
this.closeOperation = (async () => {
await this.pending.catch(() => undefined);
let syncError: unknown;
try {
await this.file.datasync();
} catch (error) {
syncError = error;
}
try {
await this.file.close();
} finally {
this.state = 'closed';
}
if (syncError !== undefined) throw syncError;
})();
return this.closeOperation;
}
}
class WorkerFileLogReadLease implements WorkerRemoteLogArtifactReadLease {
private consumed = false;
private closeOperation: Promise<void> | undefined;
constructor(
readonly logArtifactId: string,
readonly byteLength: number,
readonly truncated: boolean | undefined,
private readonly file: FileHandle,
private readonly chunkBytes: number,
) {}
chunks(): AsyncIterable<Uint8Array> {
if (this.consumed) {
throw new WorkerRemoteLogArtifactError('closed');
}
this.consumed = true;
const file = this.file;
const byteLength = this.byteLength;
const chunkBytes = this.chunkBytes;
const close = () => this.close();
return (async function* () {
let offset = 0;
try {
while (offset < byteLength) {
const buffer = Buffer.allocUnsafe(
Math.min(chunkBytes, byteLength - offset),
);
const result = await file.read(buffer, 0, buffer.length, offset);
if (result.bytesRead < 1) {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
offset += result.bytesRead;
yield Buffer.from(buffer.subarray(0, result.bytesRead));
}
} finally {
await close();
}
})();
}
close(): Promise<void> {
this.closeOperation ??= this.file.close();
return this.closeOperation;
}
}
function isCode(error: unknown, code: string): boolean {
return (
typeof error === 'object' &&
error !== null &&
'code' in error &&
(error as NodeJS.ErrnoException).code === code
);
}
async function readTruncationFact(
directory: string,
request: WorkerRemoteLogArtifactReadRequest,
maximumBytes: number,
): Promise<boolean | undefined> {
const target = path.join(
directory,
`.${request.logArtifactId}.log.truncated.json`,
);
let file: FileHandle;
try {
file = await fs.open(
target,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
);
} catch (error) {
if (isCode(error, 'ENOENT')) return undefined;
if (isCode(error, 'ELOOP')) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
throw error;
}
try {
const stat = await file.stat();
assertOwnedOrdinaryFile(stat);
if (stat.size < 1 || stat.size > 4096 || (stat.mode & 0o777) !== 0o600) {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
const bytes = Buffer.allocUnsafe(stat.size + 1);
const result = await file.read(bytes, 0, bytes.length, 0);
if (result.bytesRead !== stat.size) {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
let value: unknown;
try {
value = JSON.parse(bytes.subarray(0, result.bytesRead).toString('utf8'));
} catch {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
const record = value as Record<string, unknown>;
const keys = Object.keys(record).sort();
if (
JSON.stringify(keys) !== JSON.stringify([
'attemptId', 'logArtifactId', 'maximumBytes', 'observedAtMs',
'quotaReached', 'runId', 'schemaVersion',
]) ||
record.schemaVersion !== 1 ||
record.runId !== request.runId ||
record.attemptId !== request.attemptId ||
record.logArtifactId !== request.logArtifactId ||
record.maximumBytes !== maximumBytes ||
typeof record.quotaReached !== 'boolean' ||
!Number.isSafeInteger(record.observedAtMs) ||
(record.observedAtMs as number) < 0
) {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
return record.quotaReached;
} finally {
await file.close();
}
}
export class WorkerFileLogArtifactAllocator
implements WorkerRemoteLogArtifactAllocator, WorkerRemoteLogArtifactSource {
private readonly root: string;
private readonly policy: Readonly<WorkerRemoteLogArtifactPolicy>;
private readonly capacity: WorkerRemoteLogArtifactCapacityProbe;
constructor(options: WorkerFileLogArtifactAllocatorOptions) {
if (
!options ||
typeof options.root !== 'string' ||
!path.isAbsolute(options.root) ||
options.root.includes('\0') ||
(options.capacity !== undefined &&
typeof options.capacity.availableBytes !== 'function')
) {
throw new WorkerRemoteLogArtifactError('invalid_configuration');
}
this.root = path.resolve(options.root);
this.policy = normalizePolicy(options.policy);
this.capacity = options.capacity ?? new FileSystemCapacityProbe();
}
async prepare(request: Readonly<{
projectId: string;
runId: string;
attemptId: string;
offerId: string;
}>): Promise<WorkerRemoteLogArtifactPreparation> {
const logArtifactId = createWorkerRemoteLogArtifactId(request);
const rootIdentity = await privateDirectory(this.root);
let availableBytes: bigint;
try {
availableBytes = await this.capacity.availableBytes(this.root);
} catch {
throw new WorkerRemoteLogArtifactError('capacity_unavailable');
}
if (typeof availableBytes !== 'bigint' || availableBytes < 0n) {
throw new WorkerRemoteLogArtifactError('capacity_unavailable');
}
const requiredBytes = BigInt(this.policy.minimumFreeBytes) +
BigInt(this.policy.maximumAttemptBytes);
if (availableBytes < requiredBytes) {
throw new WorkerRemoteLogArtifactError('capacity_unavailable');
}
const rootAfterCapacity = await fs.lstat(this.root);
if (
rootAfterCapacity.dev !== rootIdentity.dev ||
rootAfterCapacity.ino !== rootIdentity.ino
) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
const shard = logArtifactId.slice(
ARTIFACT_ID_PREFIX.length,
ARTIFACT_ID_PREFIX.length + 2,
);
const directory = path.join(this.root, shard);
const directoryIdentity = await privateDirectory(directory);
const outputFilePath = path.join(directory, `${logArtifactId}.log`);
let file: FileHandle;
try {
file = await fs.open(
outputFilePath,
constants.O_WRONLY |
constants.O_CREAT |
constants.O_APPEND |
(constants.O_NOFOLLOW ?? 0),
0o600,
);
} catch (error) {
if ((error as NodeJS.ErrnoException)?.code === 'ELOOP') {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
throw error;
}
try {
const stat = await file.stat();
assertOwnedOrdinaryFile(stat);
if (
!Number.isSafeInteger(stat.size) ||
stat.size < 0 ||
stat.size > this.policy.maximumAttemptBytes
) {
throw new WorkerRemoteLogArtifactError('quota_exceeded');
}
await file.chmod(0o600);
const pathStat = await fs.lstat(outputFilePath);
const directoryAfterOpen = await fs.lstat(directory);
if (
pathStat.isSymbolicLink() ||
pathStat.dev !== stat.dev ||
pathStat.ino !== stat.ino ||
(pathStat.mode & 0o777) !== 0o600 ||
directoryAfterOpen.dev !== directoryIdentity.dev ||
directoryAfterOpen.ino !== directoryIdentity.ino
) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
const output = new WorkerFileLogOutput(
logArtifactId,
file,
this.policy.maximumAttemptBytes,
stat.size,
this.policy.maximumWriteChunkBytes,
outputFilePath,
);
let ownership: 'prepared' | 'handed_off' | 'released' = 'prepared';
return Object.freeze({
logArtifactId,
takeOutput() {
if (ownership !== 'prepared') {
throw new WorkerRemoteLogArtifactError('closed');
}
ownership = 'handed_off';
return output;
},
async release() {
if (ownership === 'released' || ownership === 'handed_off') return;
ownership = 'released';
await output.close();
},
});
} catch (error) {
await file.close().catch(() => undefined);
throw error;
}
}
async open(
request: WorkerRemoteLogArtifactReadRequest,
): Promise<WorkerRemoteLogArtifactReadLease | undefined> {
const runId = requestId('runId', request?.runId);
const attemptId = requestId('attemptId', request?.attemptId);
const logArtifactId = requestId('logArtifactId', request?.logArtifactId);
if (!/^wlog-[0-9a-f]{30}$/.test(logArtifactId)) {
throw new WorkerRemoteLogArtifactError('invalid_request');
}
const rootIdentity = await privateDirectory(this.root);
const directory = path.join(this.root, logArtifactId.slice(5, 7));
const directoryIdentity = await privateDirectory(directory);
const target = path.join(directory, `${logArtifactId}.log`);
let file: FileHandle;
try {
file = await fs.open(
target,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
);
} catch (error) {
if (isCode(error, 'ENOENT')) return undefined;
if (isCode(error, 'ELOOP')) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
throw error;
}
try {
const stat = await file.stat();
assertOwnedOrdinaryFile(stat);
if (
!Number.isSafeInteger(stat.size) ||
stat.size < 0 ||
stat.size > this.policy.maximumAttemptBytes ||
(stat.mode & 0o777) !== 0o600
) {
throw new WorkerRemoteLogArtifactError('invalid_output');
}
const pathStat = await fs.lstat(target);
const rootAfterOpen = await fs.lstat(this.root);
const directoryAfterOpen = await fs.lstat(directory);
if (
pathStat.isSymbolicLink() ||
pathStat.dev !== stat.dev ||
pathStat.ino !== stat.ino ||
rootAfterOpen.dev !== rootIdentity.dev ||
rootAfterOpen.ino !== rootIdentity.ino ||
directoryAfterOpen.dev !== directoryIdentity.dev ||
directoryAfterOpen.ino !== directoryIdentity.ino
) {
throw new WorkerRemoteLogArtifactError('unsafe_path');
}
const truncated = await readTruncationFact(
directory,
{ runId, attemptId, logArtifactId },
this.policy.maximumAttemptBytes,
);
return new WorkerFileLogReadLease(
logArtifactId,
stat.size,
truncated,
file,
Math.min(this.policy.maximumWriteChunkBytes, 64 * 1024),
);
} catch (error) {
await file.close().catch(() => undefined);
throw error;
}
}
}
@@ -0,0 +1,215 @@
// Worker Execution owns the fenced POSIX launch adapter and spawn barrier.
import { createHash } from 'node:crypto';
import {
LocalProcessLaunchError,
LocalProcessLauncher,
type LocalProcessIdentityProvider,
} from '@qinglong/local-process';
import { assertRunDispatchId } from '@qinglong/runtime-core/run-dispatch-lease';
import {
normalizeWorkerRemoteExecutionInboxRecord,
type WorkerRemoteExecutionInbox,
} from '../remote-execution/executionInbox';
import type {
WorkerRemoteExecutionExecutor,
WorkerRemoteExecutionLaunch,
} from '../remote-execution/executionInboxProcessor';
import { workerFileLogOutputPlan } from './workerFileLogArtifactAllocator';
export interface WorkerRemoteExecutionSpawnBarrier {
verify(input: Readonly<{
offerId: string;
runId: string;
attemptId: string;
callbackSequence: number;
callbackTokenDigest: string;
logArtifactId: string;
executorStartedAtMs: number;
}>): Promise<void>;
}
export class WorkerInboxExecutionSpawnBarrier
implements WorkerRemoteExecutionSpawnBarrier {
constructor(private readonly inbox: Pick<WorkerRemoteExecutionInbox, 'readOffer'>) {
if (!inbox || typeof inbox.readOffer !== 'function') {
throw new TypeError('Worker execution spawn barrier inbox is invalid');
}
}
async verify(input: Readonly<{
offerId: string;
runId: string;
attemptId: string;
callbackSequence: number;
callbackTokenDigest: string;
logArtifactId: string;
executorStartedAtMs: number;
}>): Promise<void> {
const record = await this.inbox.readOffer(input.offerId);
if (!record) throw new Error('Worker execution spawn barrier is missing');
const current = normalizeWorkerRemoteExecutionInboxRecord(record);
if (
current.state !== 'launching' ||
current.offer.offerId !== input.offerId ||
current.offer.candidate.runId !== input.runId ||
current.offer.candidate.attemptId !== input.attemptId ||
current.completionReceiptCallbackSequence !== input.callbackSequence ||
current.completionReceiptTokenDigest !== input.callbackTokenDigest ||
current.logArtifactId !== input.logArtifactId ||
current.executorStartedAtMs !== input.executorStartedAtMs
) {
throw new Error('Worker execution spawn barrier authority drifted');
}
}
}
export interface WorkerPosixExecutionExecutorOptions {
readonly barrier: WorkerRemoteExecutionSpawnBarrier;
readonly receiptRoot: string;
readonly launcherPath?: string;
readonly expectedLauncherSha256?: string;
readonly identityProvider?: LocalProcessIdentityProvider;
readonly clock?: { now(): number };
readonly createHandleId?: () => string;
}
export class WorkerPosixExecutionExecutor
implements WorkerRemoteExecutionExecutor {
private readonly options: WorkerPosixExecutionExecutorOptions;
constructor(options: WorkerPosixExecutionExecutorOptions) {
if (!options || typeof options.barrier?.verify !== 'function') {
throw new TypeError('Worker POSIX Executor barrier is invalid');
}
this.options = options;
}
async start(launch: WorkerRemoteExecutionLaunch): Promise<
| Readonly<{
status: 'started';
executorHandle: string;
executorStartedAtMs: number;
}>
| Readonly<{ status: 'rejected' }>
> {
let callbackToken: Buffer | undefined;
try {
assertRunDispatchId('offerId', launch.offerId);
assertRunDispatchId('runId', launch.runId);
assertRunDispatchId('attemptId', launch.attemptId);
assertRunDispatchId('logArtifactId', launch.logArtifactId);
if (
(launch.timeoutMs === undefined) !==
(launch.executionDeadlineAtMs === undefined) ||
(launch.executionDeadlineAtMs !== undefined &&
(!Number.isSafeInteger(launch.executionDeadlineAtMs) ||
launch.executionDeadlineAtMs < 0))
) return this.reject(launch);
const outputPlan = workerFileLogOutputPlan(launch.output);
if (
!outputPlan ||
outputPlan.logArtifactId !== launch.logArtifactId
) return this.reject(launch);
callbackToken = Buffer.from(launch.completionCallback.token);
if (callbackToken.byteLength !== 32) return this.reject(launch);
const callbackTokenDigest = createHash('sha256')
.update(callbackToken)
.digest('hex');
const environment: Record<string, string> = {};
for (const entry of launch.environment) {
if (Object.hasOwn(environment, entry.name)) return this.reject(launch);
environment[entry.name] = entry.value;
}
await launch.output.close();
const launcher = new LocalProcessLauncher(
{
register: async (registered: Readonly<{
runId: string;
attemptId: string;
registeredAtMs: number;
}>) => {
if (
registered.runId !== launch.runId ||
registered.attemptId !== launch.attemptId
) throw new Error('Worker POSIX Executor journal authority drifted');
await this.options.barrier.verify(Object.freeze({
offerId: launch.offerId,
runId: launch.runId,
attemptId: launch.attemptId,
callbackSequence: launch.completionCallback.sequence,
callbackTokenDigest,
logArtifactId: launch.logArtifactId,
executorStartedAtMs: launch.executorStartedAtMs,
}));
},
},
{
receiptRoot: this.options.receiptRoot,
...(this.options.launcherPath === undefined
? {}
: { launcherPath: this.options.launcherPath }),
...(this.options.expectedLauncherSha256 === undefined
? {}
: { expectedLauncherSha256: this.options.expectedLauncherSha256 }),
...(this.options.identityProvider === undefined
? {}
: { identityProvider: this.options.identityProvider }),
...(this.options.clock === undefined
? {}
: { clock: this.options.clock }),
...(this.options.createHandleId === undefined
? {}
: { createHandleId: this.options.createHandleId }),
},
);
const command = launch.command.kind === 'argv'
? Object.freeze({
kind: 'argv' as const,
file: launch.command.file,
args: launch.command.args,
})
: Object.freeze({
kind: 'shell' as const,
command: launch.command.command,
...(launch.command.shell === undefined
? {}
: { shell: launch.command.shell as '/bin/sh' | '/bin/bash' }),
});
const handle = await launcher.start({
runId: launch.runId,
attemptId: launch.attemptId,
startedAtMs: launch.executorStartedAtMs,
callbackSequence: launch.completionCallback.sequence,
callbackToken: callbackToken.toString('base64url'),
command,
environment,
...(launch.workingDirectory === undefined
? {}
: { workingDirectory: launch.workingDirectory }),
output: outputPlan,
});
void handle.completion;
return Object.freeze({
status: 'started' as const,
executorHandle: handle.durableHandle,
executorStartedAtMs: handle.startedAtMs,
});
} catch (error) {
await launch?.output?.close?.().catch(() => undefined);
if (
error instanceof LocalProcessLaunchError &&
error.spawnOutcome === 'unknown'
) throw error;
return Object.freeze({ status: 'rejected' as const });
} finally {
callbackToken?.fill(0);
}
}
private async reject(
launch: WorkerRemoteExecutionLaunch,
): Promise<Readonly<{ status: 'rejected' }>> {
await launch.output.close().catch(() => undefined);
return Object.freeze({ status: 'rejected' as const });
}
}