perf(backend): индексы, кэш чартов, пропуск upsert, фикс N+1 обогащения

- track_plays(played_at,track_id,station_id) покрывающий + tracks(first_seen_at) WHERE
  pending частичный (применены CONCURRENTLY на проде + миграция idempotent)
- ChartsService.getTopTracks: in-memory TTL-кэш 90с по (period,genre,limit) → детальная
  страница и параллельные запросы не пересчитывают тяжёлые агрегации
- NowPlayingService.ingest: не пишем now_playing и не шлём сокет, если трек не изменился
  (было ~20k бесполезных upsert/час)
- enrichNowPlaying: вместо N+1 (300 upsert/мин) — один batched findMany по normKey

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nk
2026-06-06 16:20:25 +03:00
parent 924a4a0ab1
commit a3434ed894
5 changed files with 72 additions and 13 deletions

View File

@@ -149,6 +149,17 @@ export class NowPlayingService {
where: { stationId: stationDbId },
});
// Ничего не изменилось (станцию опросили, трек тот же) — не пишем в БД и не
// шлём сокет: иначе ~20k бесполезных upsert/час и лишний churn индексов.
if (
prev &&
prev.song === song &&
prev.artist === artist &&
prev.coverUrl === coverUrl
) {
return;
}
const updated = await this.prisma.nowPlaying.upsert({
where: { stationId: stationDbId },
create: { stationId: stationDbId, song, artist, coverUrl },