Files
iistwin/server/gps/sse-throttle.ts
Ильяс Султанов c1d5b1cbee fix(gps): остановлен 429-шторм — throttle SSE gps_asset_moved (3 сек/asset) и клиентских инвалидаций (60 сек), polling позиций 60 сек, статусов 5 мин
Каждая точка ingest (~1/сек) broadcast'илась SSE всем клиентам орг., клиент
на каждое событие делал invalidateQueries без throttle → 2+ GET/сек → исчерпание
per-user бакета apiLimiter (1500/15мин) → все API устройства 429 (формы не грузились).
Модель теперь как у нормальных трекеров: точки пишутся в БД, карта читает по интервалу.
+4 unit-теста троттла (server/gps/sse-throttle.ts)
2026-09-17 21:59:34 +03:00

28 lines
1.5 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.

/**
* In-memory throttle для SSE-событий gps_asset_moved.
* Трекеры присылают точки ~1 раз в секунду; без ограничения каждый открытый
* клиент организации получает broadcast каждую секунду и инвалидирует запросы
* позиций/объектов, что исчерпывает per-user бакет apiLimiter (429).
* Запись позиции в БД не затрагивается — ограничивается только broadcast.
*/
const GPS_MOVED_SSE_MIN_INTERVAL_MS = 3_000;
const MAX_TRACKED_ASSETS = 1_000;
const lastPublishedAt = new Map<number, number>();
/** true — событие публиковать; false — пропустить (с последней публикации прошло меньше интервала) */
export function shouldPublishGpsAssetMoved(assetId: number, now = Date.now()): boolean {
const last = lastPublishedAt.get(assetId);
if (last !== undefined && now - last < GPS_MOVED_SSE_MIN_INTERVAL_MS) return false;
lastPublishedAt.set(assetId, now);
// Защита от неограниченного роста карты: чистим устаревшие записи при превышении лимита
if (lastPublishedAt.size > MAX_TRACKED_ASSETS) {
for (const [id, ts] of lastPublishedAt) {
if (now - ts >= GPS_MOVED_SSE_MIN_INTERVAL_MS) lastPublishedAt.delete(id);
}
}
return true;
}