feat(gateway): support per-channel and per-session queue policy overrides

This commit is contained in:
William Valentin
2026-02-16 11:51:26 -08:00
parent f7284a4ef1
commit fbd24d4379
11 changed files with 181 additions and 9 deletions
+13 -4
View File
@@ -47,7 +47,8 @@ export class LaneQueue {
* Returns a promise that resolves with the work's return value
* once it has been executed (which may be immediately if the lane is idle).
*/
async enqueue<T>(laneId: string, work: () => Promise<T>): Promise<T> {
async enqueue<T>(laneId: string, work: () => Promise<T>, policy?: Partial<LaneQueueConfig>): Promise<T> {
const effective = this.resolvePolicy(policy);
let lane = this.lanes.get(laneId);
if (!lane) {
lane = { active: false, queue: [] };
@@ -65,12 +66,12 @@ export class LaneQueue {
}
}
if (this.config.mode === 'steer' || this.config.mode === 'interrupt') {
if (effective.mode === 'steer' || effective.mode === 'interrupt') {
this.rejectPending(lane, 'Superseded by newer request');
}
if (lane.queue.length >= this.config.cap) {
if (this.config.overflow === 'drop_new') {
if (lane.queue.length >= effective.cap) {
if (effective.overflow === 'drop_new') {
return Promise.reject(new Error('Lane queue full (drop_new)'));
}
// drop_old
@@ -131,6 +132,14 @@ export class LaneQueue {
}
}
private resolvePolicy(policy?: Partial<LaneQueueConfig>): LaneQueueConfig {
return {
mode: policy?.mode ?? this.config.mode,
cap: Math.max(1, policy?.cap ?? this.config.cap),
overflow: policy?.overflow ?? this.config.overflow,
};
}
/**
* Process the next queued entry for a lane (called after current work finishes).
* Runs asynchronously so the caller's finally block completes first.