48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import type { MollifierBuffer } from "@trigger.dev/redis-worker";
|
|
import { logger } from "~/services/logger.server";
|
|
import type { GateInputs, TripDecision, TripEvaluator } from "./mollifierGate.server";
|
|
|
|
export type TripEvaluatorOptions = {
|
|
windowMs: number;
|
|
threshold: number;
|
|
holdMs: number;
|
|
};
|
|
|
|
export type CreateRealTripEvaluatorDeps = {
|
|
getBuffer: () => MollifierBuffer | null;
|
|
options: () => TripEvaluatorOptions;
|
|
};
|
|
|
|
export function createRealTripEvaluator(deps: CreateRealTripEvaluatorDeps): TripEvaluator {
|
|
return async (inputs: GateInputs): Promise<TripDecision> => {
|
|
const buffer = deps.getBuffer();
|
|
if (!buffer) return { divert: false };
|
|
|
|
const opts = deps.options();
|
|
|
|
try {
|
|
const { tripped, count } = await buffer.evaluateTrip(inputs.envId, opts);
|
|
if (!tripped) return { divert: false };
|
|
|
|
return {
|
|
divert: true,
|
|
reason: "per_env_rate",
|
|
count,
|
|
threshold: opts.threshold,
|
|
windowMs: opts.windowMs,
|
|
holdMs: opts.holdMs,
|
|
};
|
|
} catch (err) {
|
|
// Deliberate: no error counter here. Shadow mode means a silent miss is
|
|
// harmless — fail-open is the safe direction. The error log + Sentry
|
|
// capture is sufficient operability while this runs in shadow mode. Revisit
|
|
// once buffer writes are the primary path and a missed evaluation has cost.
|
|
logger.error("mollifier trip evaluator: fail-open on error", {
|
|
envId: inputs.envId,
|
|
err: err instanceof Error ? err.message : String(err),
|
|
});
|
|
return { divert: false };
|
|
}
|
|
};
|
|
}
|