Andrey Epifancev 3363291801 perf: сценарий памяти свода 2 (GO «все» → RF «все» → GO)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:30:12 +04:00

82 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** Чистые помощники замеров свода 2 (без импортов, стираемый TS: проверяются `node --test`). */
export const SVOD2_DATA_ROW_SELECTOR = 'tbody tr:not([data-svod2-placeholder])';
export const SVOD2_PLACEHOLDER_SELECTOR = 'tbody tr[data-svod2-placeholder]';
/** Ответы свода 2: страница `/svod/2/<sheet>/...` и `/svod/2/<sheet>/<year>/facets`. */
export function svod2RespRe(sheet: string): RegExp {
return new RegExp(`/api/v1/svod/2/${sheet}/`);
}
/** Запрос свода 2 с параметром search= (серверный поиск). */
export function isSvod2SearchUrl(url: string): boolean {
const q = url.indexOf('?');
if (q < 0 || !/\/api\/v1\/svod\/2\//.test(url.slice(0, q))) return false;
return url
.slice(q + 1)
.split('#')[0]
.split('&')
.some((p) => p === 'search' || p.startsWith('search='));
}
/** Число запросов страниц (offset= в query). */
export function countPagesRequested(urls: string[]): number {
let n = 0;
for (const u of urls) {
const q = u.indexOf('?');
if (q < 0) continue;
if (u.slice(q + 1).split('#')[0].split('&').some((p) => p === 'offset' || p.startsWith('offset='))) n++;
}
return n;
}
/** Доля кадров, где видна хотя бы одна заглушка. */
export function placeholderShare(samples: boolean[]): number {
if (samples.length === 0) return 0;
let k = 0;
for (const s of samples) if (s) k++;
return k / samples.length;
}
/** Байты ответов по requestId: sheet - максимум одного ответа, total - сумма, first - сумма до markFirstRows. */
export class ByteCounter {
perRequest: Map<string, number> = new Map();
totalBytes = 0;
firstBytes = 0;
marked = false;
add(requestId: string, bytes: number): void {
this.perRequest.set(requestId, (this.perRequest.get(requestId) ?? 0) + bytes);
this.totalBytes += bytes;
if (!this.marked) this.firstBytes += bytes;
}
markFirstRows(): void {
this.marked = true;
}
get sheet(): number {
let m = 0;
for (const v of this.perRequest.values()) if (v > m) m = v;
return m;
}
get total(): number {
return this.totalBytes;
}
get first(): number {
return this.firstBytes;
}
}
/** filter_result_ms: от fill до стабилизации; ответ поиска (если был) не должен быть позже конца; таймаут ожидания (respAt=null) не учитывается. */
export function filterResultMs(startAt: number, stableAt: number, respAt: number | null): number {
return Math.max(stableAt, respAt ?? 0) - startAt;
}
/** Точки замера памяти свода 2: GO «все» -> RF «все» -> (опц. прокрутка RF) -> снова GO. */
export function memoryPoints(env: Record<string, string | undefined>): string[] {
return env.PERF_SVOD2_MEM_SCROLL === '1' ? ['go', 'rf', 'rf_scrolled', 'final'] : ['go', 'rf', 'final'];
}