Files
iptvnator/apps/xtream-mock-server/src/app/performance-occurrences.ts
T
4gray a2fafcfc08 test(performance): add end-to-end Xtream benchmark harness (#1300)
* docs(performance): plan Xtream benchmark

* feat(xtream-mock-server): add deterministic 100k fixture

* style(xtream-mock-server): apply repository formatting

* fix(xtream-mock-server): harden performance fixture data

* feat(xtream-mock-server): add performance control plane

* docs(performance): correct Xtream capture plan

* fix(xtream-mock-server): harden performance controls

* fix(xtream-mock-server): harden control lifecycle

* feat(performance): add Xtream preload markers

* feat(performance): trace Xtream main phases

* feat(performance): mark Xtream store publications

* feat(performance): trace Xtream database phases

* feat(performance): trace Xtream delete cancellation

* feat(performance): capture Xtream phase attribution

* feat(performance): mark Sources Xtream refresh

* test(performance): define Xtream benchmark evidence contracts

* test(performance): add Xtream benchmark runner

* test(performance): surface failure evidence writes

* test(performance): align database read clock

* test(performance): preserve capture failure contracts
2026-07-28 08:08:07 +02:00

105 lines
3.1 KiB
TypeScript

import type { Request } from 'express';
import {
canonicalObservedAction,
isAllowedPerformanceCategory,
PerformanceControlError,
} from './performance-control-validation.js';
import {
PERFORMANCE_CONTROL_LIMITS,
type PerformanceAction,
type PerformanceIdentity,
type PerformanceOccurrenceState,
type PerformanceRuleMatch,
type PerformanceTransport,
} from './performance-control.types.js';
import { getScenario } from './scenarios.js';
export class PerformanceOccurrenceTracker {
private readonly entries = new Map<string, PerformanceOccurrenceState>();
next(
request: Request,
transport: PerformanceTransport,
epoch: number
): PerformanceIdentity {
const username = stringQuery(request.query['username']);
const password = stringQuery(request.query['password']);
const scenario = getScenario(username, password).name;
const action = canonicalObservedAction(request.query['action']);
const rawCategoryId = stringQuery(request.query['category_id']);
const categoryId =
scenario === 'performance-100k' &&
action !== 'unknown' &&
rawCategoryId !== 'all' &&
isAllowedPerformanceCategory(action, rawCategoryId)
? rawCategoryId
: 'all';
const base = { scenario, transport, action, categoryId };
const key = baseIdentityKey(base);
const previous = this.entries.get(key);
if (
!previous &&
this.entries.size >= PERFORMANCE_CONTROL_LIMITS.occurrences
) {
throw new PerformanceControlError(503, 'control-occurrence-limit');
}
const count = (previous?.count ?? 0) + 1;
this.entries.set(key, { ...base, count });
return { epoch, ...base, occurrence: count };
}
count(match: PerformanceRuleMatch): number {
return this.entries.get(baseIdentityKey(match))?.count ?? 0;
}
snapshot(): PerformanceOccurrenceState[] {
return [...this.entries.values()];
}
clear(): void {
this.entries.clear();
}
}
export function ruleMatchKey(match: PerformanceRuleMatch): string {
return JSON.stringify([
match.scenario,
match.transport,
match.action,
match.categoryId,
match.occurrence,
]);
}
export function identityKey(identity: PerformanceIdentity): string {
return JSON.stringify([
identity.scenario,
identity.transport,
identity.action,
identity.categoryId,
identity.occurrence,
]);
}
function baseIdentityKey(
identity:
| Omit<PerformanceRuleMatch, 'occurrence'>
| {
scenario: string;
transport: PerformanceTransport;
action: PerformanceAction | 'unknown';
categoryId: string;
}
): string {
return JSON.stringify([
identity.scenario,
identity.transport,
identity.action,
identity.categoryId,
]);
}
function stringQuery(value: unknown): string {
return typeof value === 'string' ? value : '';
}