mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 21:56:18 -08:00
* 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
105 lines
3.1 KiB
TypeScript
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 : '';
|
|
}
|