mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 18:08:20 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import { MAX_PRIMARY_CANCELLATION_BATCH_SIZE } from '../ports/primaryCancellationSource';
|
||||
import type { PrimaryCancellationCursor } from '../ports/primaryCancellationSource';
|
||||
import type {
|
||||
PrimaryCancellationDispatcher,
|
||||
PrimaryCancellationDispatchSummary,
|
||||
} from './primaryCancellationDispatcher';
|
||||
|
||||
export const MAX_PRIMARY_CANCELLATION_PAGES_PER_CYCLE = 64;
|
||||
|
||||
export type PrimaryCancellationCycleStopReason =
|
||||
| 'complete'
|
||||
| 'page_limit'
|
||||
| 'unsafe_attempt_overflow'
|
||||
| 'cursor_stalled';
|
||||
|
||||
export interface PrimaryCancellationCycleSummary
|
||||
extends Omit<
|
||||
PrimaryCancellationDispatchSummary,
|
||||
'truncated' | 'unsafeAttemptOverflow' | 'nextCursor'
|
||||
> {
|
||||
pages: number;
|
||||
stopReason: PrimaryCancellationCycleStopReason;
|
||||
remaining: boolean;
|
||||
nextCursor?: PrimaryCancellationCursor;
|
||||
}
|
||||
|
||||
export interface PrimaryCancellationCycleOptions {
|
||||
cursor?: PrimaryCancellationCursor;
|
||||
pageSize?: number;
|
||||
maxPages?: number;
|
||||
}
|
||||
|
||||
function sameCursor(
|
||||
left: PrimaryCancellationCursor | undefined,
|
||||
right: PrimaryCancellationCursor,
|
||||
): boolean {
|
||||
return (
|
||||
left !== undefined &&
|
||||
left.requestedAtMs === right.requestedAtMs &&
|
||||
left.runId === right.runId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a bounded recovery cycle. It deliberately owns no timer or process hook;
|
||||
* edge and cluster deployments choose their own cadence and lifecycle.
|
||||
*/
|
||||
export class PrimaryCancellationSupervisor {
|
||||
constructor(
|
||||
private readonly dispatcher: Pick<
|
||||
PrimaryCancellationDispatcher,
|
||||
'dispatchBatch'
|
||||
>,
|
||||
) {}
|
||||
|
||||
async runCycle(
|
||||
options: PrimaryCancellationCycleOptions = {},
|
||||
): Promise<PrimaryCancellationCycleSummary> {
|
||||
const pageSize = options.pageSize ?? 32;
|
||||
const maxPages = options.maxPages ?? 4;
|
||||
if (
|
||||
!Number.isSafeInteger(pageSize) ||
|
||||
pageSize < 1 ||
|
||||
pageSize > MAX_PRIMARY_CANCELLATION_BATCH_SIZE
|
||||
) {
|
||||
throw new RangeError(
|
||||
'pageSize must be between 1 and MAX_PRIMARY_CANCELLATION_BATCH_SIZE',
|
||||
);
|
||||
}
|
||||
if (
|
||||
!Number.isSafeInteger(maxPages) ||
|
||||
maxPages < 1 ||
|
||||
maxPages > MAX_PRIMARY_CANCELLATION_PAGES_PER_CYCLE
|
||||
) {
|
||||
throw new RangeError(
|
||||
'maxPages must be between 1 and MAX_PRIMARY_CANCELLATION_PAGES_PER_CYCLE',
|
||||
);
|
||||
}
|
||||
|
||||
const total: PrimaryCancellationCycleSummary = {
|
||||
pages: 0,
|
||||
scanned: 0,
|
||||
claimed: 0,
|
||||
terminationRequested: 0,
|
||||
alreadyExited: 0,
|
||||
pending: 0,
|
||||
ambiguous: 0,
|
||||
blocked: 0,
|
||||
deferred: 0,
|
||||
alreadyResolved: 0,
|
||||
notEligible: 0,
|
||||
failed: 0,
|
||||
stopReason: 'complete',
|
||||
remaining: false,
|
||||
};
|
||||
let cursor = options.cursor;
|
||||
|
||||
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
||||
const page = await this.dispatcher.dispatchBatch({
|
||||
...(cursor === undefined ? {} : { cursor }),
|
||||
limit: pageSize,
|
||||
});
|
||||
total.pages += 1;
|
||||
total.scanned += page.scanned;
|
||||
total.claimed += page.claimed;
|
||||
total.terminationRequested += page.terminationRequested;
|
||||
total.alreadyExited += page.alreadyExited;
|
||||
total.pending += page.pending;
|
||||
total.ambiguous += page.ambiguous;
|
||||
total.blocked += page.blocked;
|
||||
total.deferred += page.deferred;
|
||||
total.alreadyResolved += page.alreadyResolved;
|
||||
total.notEligible += page.notEligible;
|
||||
total.failed += page.failed;
|
||||
|
||||
if (page.unsafeAttemptOverflow) {
|
||||
total.stopReason = 'unsafe_attempt_overflow';
|
||||
total.remaining = true;
|
||||
return total;
|
||||
}
|
||||
if (!page.truncated) return total;
|
||||
if (!page.nextCursor || sameCursor(cursor, page.nextCursor)) {
|
||||
total.stopReason = 'cursor_stalled';
|
||||
total.remaining = true;
|
||||
return total;
|
||||
}
|
||||
cursor = page.nextCursor;
|
||||
if (pageNumber === maxPages - 1) {
|
||||
total.stopReason = 'page_limit';
|
||||
total.remaining = true;
|
||||
total.nextCursor = cursor;
|
||||
return total;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user