@golemio/pid
Version:
Golemio PID Module
605 lines (468 loc) • 28.5 kB
Markdown
# Redis cache
## Cache pro načítání GTFS jízdních řádů
### files:gtfsStatic:*
- Klíč `files:gtfsStatic:*`, kde `*` je název souboru, popř. cesta k souboru v případě ZIP archivu
- Hodnota CSV/JSON jako string
- TTL žádné
- Retence dat až do úspěšného načtení GTFS jíždních řádů
- Cache obsahuje všechny načtené soubory z ROPID FTP (viz [implementační dokumentace](../../implementation_documentation.md#files-gtfs-static#ropid-ftp-obecně))
- Menší soubory jsou ukládány pomocí `SET`, větší jsou streamovány pomocí knihovny [redis-wstream](https://github.com/jeffbski/redis-wstream) (`SET` + `APPEND`)
- Menší soubory jsou následně načteny pomocí `GET`, větší jsou streamovány z Redisu do PSQL pomocí knihovny [redis-rstream](https://github.com/jeffbski/redis-rstream) (`GETRANGE`)
### Flowchart
```mermaid
flowchart TD;
redis_1[("Redis files:gtfsStatic:*")];
redis_2[("Redis files:gtfsStatic:*")];
redis_3[("Redis files:gtfsStatic:*")];
psql[("PSQL ropidgtfs_*_tmp")];
amqp_transformAndSaveData[AMQP transformAndSaveData];
initial["Zpráva z cronu, stažení z ROPID FTP"];
initial--SET malý soubor-->redis_1;
initial--Wstream velký soubor-->redis_1;
initial-->amqp_transformAndSaveData;
amqp_transformAndSaveData--GET malý soubor<-->redis_2;
amqp_transformAndSaveData--Rstream velký soubor<-->redis_2;
amqp_transformAndSaveData--INSERT malý soubor-->psql;
amqp_transformAndSaveData--COPY stream velký soubor-->psql;
continue[Pokračování v načítání JŘ];
amqp_transformAndSaveData-->continue;
cache_manager["DataCacheManager"];
continue-->cache_manager;
cache_manager--Truncate-->redis_3;
```
## Cache pro GTFS-RT feedy
### files:gtfsRt
- Klíč `files:gtfsRt`, pomocný `files:gtfsRt_*_timestamp`, kde `*` je název feedu bez přípony
- Hodnota je hash mapa, kde klíč je název feedu s příponou `.pb`
- `trip_updates.pb`
- `vehicle_positions.pb`
- `pid_feed.pb`
- `alerts.pb`
- Hodnota v hash mapě je binární blob reprezentující specifický GTFS-RT feed
- Hodnota v pomocném klíči je unix timestamp poslední aktualizace daného feed v milisekundách
- TTL žádné
- Retence dat aktivní, vždy uchováváme nejnovější feedy
- Všechny feedy jsou ukládány pomocí `HSET`, pomocné timestampy pomocí `SET`
- Feedy jsou nabízený přes API `/v2/vehiclepositions/gtfsrt/{feed}`, kde `{feed}` je název feedu s příponou `.pb`
- Žádaný feed se načte pomocí `HGET` a vrátí se jako binární blob. Pokud feed neexistuje, vrátí se 404
- Pomocný timestamp se načte pomocí `GET` a vrátí se jako hodnota hlavičky `Last-Modified` převedená na ISO string. Pokud timestamp neexistuje, hodnota hlavičky bude unix timestamp 0 jako ISO string. Využívá se především pro monitoring a alerting
- Feedy se generují nejméně 3x za minutu (rabín 2x) pomocí queue-message
- Používají redis-mutex na zamykání, aby neběželo více shodných tasků v jeden okamžik
- Pokud generování stíhá běžet rychleji, task se snaží do ±7 vteřin svého běhu spustit nové generování souborů (opět přes queue-message).
### Flowchart
```mermaid
flowchart TD;
redis_f_1[("Redis files:gtfsRt")];
redis_t_1[("Redis files:gtfsRt_*_timestamp")];
psql[("PSQL vehiclepositions_*, ropidvymi_*")];
connector[("OG Redis connector")];
repository["IE GtfsRtRedisRepository"];
get_gtfsrt["GET /v2/vehiclepositions/gtfsrt/$feed"];
initial["Zpráva z cronu, vygenerování feedů"];
initial--Select<-->psql;
initial-->repository;
repository--HSET *.pb-->redis_f_1;
repository--SET *_timestamp-->redis_t_1;
get_gtfsrt-->connector;
connector--HGET *.pb<-->redis_f_1;
connector--GET *_timestamp<-->redis_t_1;
```
## Cache pro zpracování RT dat
### gtfsRunSchedule:*
- key = `*${route_id}_${run_number}`
- retence dat aktivne v tride `DataCacheManager`
- TTL podle casu vygenerovane ho z methody `getNextExpireTimestamp`
- hodnota je `string`
- cache obsaguje data z tabulky ropidgtfs_precomputed_trip_schedule
```mermaid
flowchart TD;
psql[("PSQL ropidgtfs_precomputed_trip_schedule")];
redis_trip_schedule[("Redis gtfsRunSchedule:*")];
connector[("OG Redis connector")];
repository["IE RunTripsRedisRepository"];
propagateDelay["propagateDelay"];
repository<--GET*.runTuple--->propagateDelay;
runManager["AbstractGTFSTripRunManager"]
get_gtfsrt["GET /v2/departureboards"];
initial["Zpráva z vozidla"];
initial--process-->runManager;
repository--GET*.runTuple<-->redis_trip_schedule;
runManager--GET*.runTuple<-->repository;
runManager--SET *.runTuple-->repository;
repository--SET *.runTuple-->redis_trip_schedule;
runManager--SELECT if not found in redis<-->psql;
get_gtfsrt-->connector;
connector--**MGET** *runTuple<-->redis_trip_schedule;
```
### gtfsBlockSchedule:*
- key = `*gtfs_block_id`
- retence dat aktivne v tride `DataCacheManager`
- TTL podle casu vygenerovane ho z methody `getNextExpireTimestamp`
- hodnota je `string`
- cache obsahuje data trip_stops podle gtfs block_id
```mermaid
flowchart TD;
psql[("PSQL ropidgtfs_stop_times")];
cache[("Redis gtfsBlockSchedule:*")];
repository["IE BlockStopsRedisRepository"];
updateDelay["updateDelay"];
updateGTFSTripId["updateGTFSTripId"];
tripStopsManager["tripStopsManager"];
initial["Zpráva z vozidla"];
initial--process-->updateGTFSTripId;
updateGTFSTripId<--create cache for trip stops-->tripStopsManager;
updateGTFSTripId-->updateDelay;
updateDelay<--get/update cache for trip stops-->tripStopsManager;
updateDelay-->propagateDelay;
propagateDelay-->propagateTrainDelay;
propagateTrainDelay<--get/update cache for trip stops-->tripStopsManager;
repository--GET*.blockId<-->redis_trip_schedule;
tripStopsManager--GET*.blockId<-->repository;
tripStopsManager--SET *.blockId-->repository;
repository--SET *.blockId-->cache;
tripStopsManager--SELECT if not found in redis<-->psql;
```
### gtfsDelayComputation:*
- key = `*${gtfs_trip_id}`
- TTL nastavovano na valid_to posledni pozice kdyz je znama kdyz ne tak na 30 min
- hodnota je `json`
- cache obsahuje informace o tripu
- stop_times
- shapes_anchor_points
- shapes
### Flowchart
```mermaid
flowchart TD;
updateDelay["UpdateDelay Task"];
saveRunToDb["save*RunToDb Task"];
updateRunsGTFSTripId["updateRunsGTFSTripId Task"]
processRegionalBusPositions["processRegionalBusPositions Task"]
processRegionalBusRunMessages["processRegionalBusRunMessages Task"]
repository["DelayComputationRedisRepository"];
cache["Redis gtfsDelayComputation:*"];
initial["Zprava z vozidla"]
initial-->saveRunToDb
saveRunToDb--common message-->updateRunsGTFSTripId
updateRunsGTFSTripId-->updateDelay;
updateDelay<--getTripPropertiesBatch-->repository;
updateDelay<--cacheTripDataBatch-->repository;
saveRunToDb--ArrivaCity run message-->processRegionalBusRunMessages
processRegionalBusRunMessages-->processRegionalBusPositions
processRegionalBusPositions<--getTripPropertiesBatch-->repository;
processRegionalBusPositions<--cacheTripDataBatch-->repository;
repository--JSON.GET-->cache
repository--JSON.SET-->cache
```
## Cache pro předzpracování a párování dat z Telmaxu (Arriva City)
### vpRegionalBusCisLookup:*
- key = `*${external_trip_id}`
- TTL podle casu vygenerovane ho z methody `getNextExpireTimestamp`
- hodnota je string (JSON.stringify())
- cache obsahuje cis informace o tripu
- cis_line_id
- cis_trip_number
- registration_number
```mermaid
flowchart TD
cache["vpRegionalBusCisLookup:*"]
repo["RegionalBusCisCacheRepository"]
telmax["Telmax (Arriva City)"]
task["saveArrivaCityRunsToDB task"]
telmax-->|bus position message|task
task-->|lookup CIS ID|repo
repo-->|get/set|cache
task-->|update cache|repo
cache-->|return data|repo
```
### vpRegionalBusGtfsLookup:*
- key = `*${cis_line_id}_${cis_trip_number}`
- TTL zadne nema
- retence dat aktivne v tride `DataCacheManager`
- hodnota je string (JSON.stringify())
- cache obsahuje gtfs informace o tripu
- gtfs_trip_id
- route_name
- run_number
- agency_name
- is_wheelchair_accessible
```mermaid
flowchart TD
cache["vpRegionalBusGtfsLookup:*"]
repo["RegionalBusGtfsCacheRepository"]
telmax["Telmax (Arriva City)"]
task["processRegionalBusRunMessages Task"]
psql["gtfs data in DB"]
telmax-->|bus position message|task
task<--No gtfs data for trip in cache-->psql
task-->|lookup GTFS trip|repo
repo-->|get/set|cache
task-->|update cache|repo
cache-->|return data|repo
```
## Cache pro public API
### vpPublicCache:*
- Hlavní komponentou je sorted set `vpPublicCache:$setId`, kde `$setId` je ID vygenerované datové sady (4 náhodné bajty reprentované hexadecimálně)
- Score je GPS souřadnice (`GEOADD`), member je ID vozidla. Sorted set se používá jako základ pro public VP (`GEOSEARCH`)
- ID nejnovější datové sady se propaguje přes PubSub channel `vpPublicCache`
- Další komponenty:
- `vpPublicCache:$setId:trip-$gtfsTripId` - list ID vozidel asociovaných s daným GTFS trip id (možnost více vozidel na jednom spoji)
- `vpPublicCache:$setId:vehicle-$vehicleId` - JSON jako string s informacemi o vozidle a spoji, které vykonává
- `vpPublicCache:$setId:future-trip-$gtfsTripId` - list ID vozidel asociovaných s daným GTFS trip id (možnost více vozidel na jednom spoji). Vozidlo je před trasou a vykonává předchozí spoj na oběhu (nemá se zobrazit na mapě). Využití v public odjezdech
- `vpPublicCache:$setId:future-vehicle-$vehicleId-$gtfsTripId` - JSON jako string s informacemi o vozidle. Vozidlo je před trasou a vykonává předchozí spoj na oběhu (nemá se zobrazit na mapě). Využití v public odjezdech a public VP detailu s GTFS lookupem
- `vpPublicCache:$setId:canceled-trips-$gtfsTripId` - JSON jako string s informacemi o zrušeném spoji. Využití v public odjezdech
- TTL 1 minuta
- Nová datová sada se generuje přibližně jednou za 3 vteřiny a obsahuje všechny RT spoje (aktivní, budoucí a zrušené)
- Populace `future-trip-*`, `future-vehicle-*` a `canceled-trips-*` sub-klíčů je řízena stavem spoje (před trasou / zrušen) a nesouvisí s logikou deduplikace odjezdů v `/v2/public/departureboards` – ta pracuje nad `gtfsPublicDepartureCache` a tuto cache neovlivňuje
### vpPublicStopTimeCache:*
- Klíč `vpPublicStopTimeCache:*`, kde `*` je kombinace ID vozidla a GTFS id spoje (možnost více vozidel na jednom spoji)
- Hodnota je JSON jako string s informacemi o zastávkách a jejich časech
- `sequence` - pořadí zastávky podle GTFS stop times
- `arr_delay` - čas příjezdu na zastávku - reálný čas u projetých zastávek, predikce u budoucích
- `dep_delay` - čas odjezdu ze zastávky - reálný čas u projetých zastávek, predikce u budoucích
- `cis_stop_platform_code` - nástupiště/kolej podle CIS u vlakových zastávek, jinde null
- `platform_code` - označení zastávky podle GTFS
- `stop_id` - GTFS id zastávky
- `stop_name` - název zastávky
- TTL 1 hodina a 5 minut
- Automatické promazání dat po úspěšném načtení GTFS jíždních řádů
- Cca jednou za 5 vteřin se přenačtou stop times pro všechny aktivní spoje. Generování může běžet pouze jednou, po dokončení tasku se spustí znovu (max čekání 4.8 vteřin)
### gtfsPublicDepartureCache:*
- Klíč `gtfsPublicDepartureCache:*`, kde `*` je GTFS id zastávky
- Hodnota je sorted set, kde score je unix timestamp odjezdu v sekundách a member je JSON string s informacemi o odjezdu:
- `stop_id` – GTFS id zastávky
- `departure_datetime` – plánovaný čas odjezdu (ISO 8601)
- `arrival_datetime` – plánovaný čas příjezdu (ISO 8601), nebo `null`
- `route_short_name` – název linky
- `route_type` – typ dopravy (GTFS route_type)
- `trip_id` – GTFS id spoje
- `stop_sequence` – pořadí zastávky v spoji
- `platform_code` – označení nástupiště, nebo `null`
- `trip_headsign` – cílová zastávka spoje (nebo `stop_headsign`, pokud je definován)
- `trip_headsign_icons` – ikony přestupů u cílové zastávky jako řetězec dvouznaková kódů (např. `"MbMcSb"`), nebo `null`
- `connections` – seznam garantovaných přestupů čekajících na tento spoj, nebo `null`; každý záznam obsahuje:
- `from_trip_id` – GTFS id spoje, na který se čeká
- `max_wait_sec` – maximální čekací doba v sekundách
- Přednačítá se z tabulky `ropidgtfs_precomputed_trip_connections` (původně z `transfers.txt` kde `transfer_type = 1`), JOIN na odjezdy přes `to_trip_id` a `to_stop_id`
- `wheelchair_accessible` – přístupnost vozidla pro vozíčkáře z GTFS `trips.txt` (`0` = bez informace, `1` = přístupné, `2` = nepřístupné), nebo `null`
- `direction_id` – směr jízdy z GTFS `trips.txt` (`0` nebo `1`), nebo `null`; využívá se při seskupování odjezdů podle směru a filtrování opačného směru v přestupních tabulích
- `flags` – nepovinný objekt s příznaky spoje, vyplněn pouze pokud má alespoň jeden z příznaků hodnotu `1` (zdroj `ropidgtfs_departures.is_night/is_regional/is_substitute_transport`); v objektu se objeví pouze pravdivé klíče:
- `night` (`1`) – noční spoj
- `reg` (`1`) – regionální spoj
- `subst` (`1`) – náhradní doprava
- Existuje záznam pro každou zastávku v GTFS. Pokud v ní ale není žádný odjezd, je vyplněný pouze jeden prázdný member - detekce validní zastávky bez odjezdu
- TTL 7 hodin
- Retence dat aktivní, jednou za hodinu se smažou všechny odjezdy starší než 3 hodiny
- Jednou za hodinu se vygenerují nové odjezdy pro všechny zastávky v intervalu +5 hodin až +7 hodin, po přenačtení JŘ se přegenerují všechny odjezdy v interval -3 hodiny až +7 hodin
- Ukládá se pomocí `ZADD`, čte se pomocí `ZRANGEBYSCORE` (-inf, now + `minutesAfter`)
- Využití v public odjezdech
- Využití v endpointu přestupních tabulí (`/v4/pid/transferboards`)
### Flowchart (refresh)
```mermaid
flowchart LR;
finish_up_gtfs["Dokončení zpracování GTFS JŘ"];
psql_rt[("PSQL vehiclepositions_*")];
psql_stop_times[("PSQL v_public_vehiclepositions_combined_stop_times")];
psql_departures[("PSQL ropidgtfs_departures")];
redis_pubsub_channel["Redis channel vpPublicCache"];
redis_public_cache[("Redis vpPublicCache:*")];
redis_stop_times[("Redis vpPublicStopTimeCache:*")];
redis_departures[("Redis gtfsPublicDepartureCache:*")];
amqp_refreshPublicTripCache[AMQP refreshPublicTripCache];
%% amqp_refreshPublicTripCache--"Čekání max 2.4 vteřin od spuštění tasku"-->amqp_refreshPublicTripCache;
amqp_refreshPublicStopTimeCache[AMQP refreshPublicStopTimeCache];
%% amqp_refreshPublicStopTimeCache--"Čekání max 4.8 vteřin od spuštění tasku"-->amqp_refreshPublicStopTimeCache;
amqp_refreshPublicGtfsDepartureCache[AMQP refreshPublicGtfsDepartureCache];
cron_public_cache["Cron public VP 1x za minutu (backup)"];
cron_public_cache-->amqp_refreshPublicTripCache;
cron_stop_times["Cron zastávky spojů 1x za minutu (backup)"];
cron_stop_times-->amqp_refreshPublicStopTimeCache;
cron_departures["Cron odjezdy 1x za hodinu"];
cron_departures--"{fromHours: 5, toHours: 7}"-->amqp_refreshPublicGtfsDepartureCache;
finish_up_gtfs--"Truncate"-->redis_stop_times;
finish_up_gtfs--"{fromHours: -3, toHours: 7}"-->amqp_refreshPublicGtfsDepartureCache;
amqp_refreshPublicTripCache--"Aktivní spoje a jejich polohy"<-->psql_rt;
amqp_refreshPublicTripCache--"GEOADD + RPUSH + SET"-->redis_public_cache;
amqp_refreshPublicTripCache--"ID/název nové datové sady"-->redis_pubsub_channel;
amqp_refreshPublicStopTimeCache--"Zastávky u všech aktivních spojů"<-->psql_stop_times;
amqp_refreshPublicStopTimeCache--"MSET"-->redis_stop_times;
amqp_refreshPublicGtfsDepartureCache--"Odjezdy v intervalu"<-->psql_departures;
amqp_refreshPublicGtfsDepartureCache--"ZREMRANGEBYSCORE + ZADD"-->redis_departures;
```
### Flowchart (public API VP)
```mermaid
flowchart TD;
redis_pubsub_channel["Redis channel vpPublicCache"];
redis_subscriber["VPSubscriber"];
redis_public_cache[("Redis vpPublicCache:* (RT info)")];
redis_public_cache_misc[("Redis vpPublicCache:* (aktivní, budoucí a zrušené spoje)")];
redis_repository["OG PublicVehiclePositionsRepository"];
redis_repository<-->redis_public_cache_misc;
redis_repository<-->redis_public_cache;
redis_subscriber--"Přihlášení k odběru"-->redis_pubsub_channel;
redis_pubsub_channel--"ID/název nové datové sady"-->redis_subscriber--"setCurrentSetName"-->redis_repository;
get_vp["GET /v2/public/vehiclepositions"];
get_vp--"GEOSEARCH + vehicle_id lookup pouze aktivních spojů"-->redis_repository;
```
### Flowchart (public API VP detail)
```mermaid
flowchart TD;
redis_pubsub_channel["Redis channel vpPublicCache"];
redis_subscriber["VPSubscriber"];
redis_public_cache[("Redis vpPublicCache:* (RT info)")];
redis_public_cache_misc[("Redis vpPublicCache:* (aktivní, budoucí a zrušené spoje)")];
redis_stop_times[("Redis vpPublicStopTimeCache:*")];
redis_delay_computation[("Redis gtfsDelayComputation:* (pouze shapes)")];
redis_repository["OG PublicVehiclePositionsRepository"];
redis_repository<-->redis_public_cache_misc;
redis_repository<-->redis_public_cache;
redis_subscriber--"Přihlášení k odběru"-->redis_pubsub_channel;
redis_pubsub_channel--"ID/název nové datové sady"-->redis_subscriber--"setCurrentSetName"-->redis_repository;
get_vp_detail["GET /v2/public/vehiclepositions/$vehicle_id"];
get_vp_detail--"Lookup aktivního GTFS spoje"-->redis_repository;
get_vp_detail--"scope[]=stop_times"<-->redis_stop_times;
get_vp_detail--"scope[]=shapes"<-->redis_delay_computation;
```
### Flowchart (public API VP detail with GTFS)
```mermaid
flowchart TD;
redis_pubsub_channel["Redis channel vpPublicCache"];
redis_subscriber["VPSubscriber"];
redis_public_cache[("Redis vpPublicCache:* (RT info)")];
redis_public_cache_misc[("Redis vpPublicCache:* (aktivní, budoucí a zrušené spoje)")];
redis_stop_times[("Redis vpPublicStopTimeCache:*")];
redis_delay_computation[("Redis gtfsDelayComputation:* (pouze shapes)")];
redis_repository["OG PublicVehiclePositionsRepository"];
redis_repository<-->redis_public_cache_misc;
redis_repository<-->redis_public_cache;
redis_subscriber--"Přihlášení k odběru"-->redis_pubsub_channel;
redis_pubsub_channel--"ID/název nové datové sady"-->redis_subscriber--"setCurrentSetName"-->redis_repository;
get_vp_detail_with_gtfs["GET /v2/public/vehiclepositions/$vehicle_id;gtfsTripId=$gtfsTripId"];
get_vp_detail_with_gtfs--"Lookup aktivního nebo budoucího GTFS spoje (exact match)"-->redis_repository;
get_vp_detail_with_gtfs--"scope[]=stop_times"<-->redis_stop_times;
get_vp_detail_with_gtfs--"scope[]=shapes"<-->redis_delay_computation;
```
### Flowchart (public API GTFS static lookup)
:warning: GTFS static lookup neřeší RT informace a jako jediný EP je napojený přímo na PSQL. Kvůli předpokládáně nižšímu využití (detail odjezdů v budoucnosti) to tolik nevadí
```mermaid
flowchart TD;
psql_trip[("PSQL ropidgtfs_trips, ropidgtfs_routes, ropidgtfs_precomputed_trip_schedule")];
psql_stop_times[("PSQL ropidgtfs_stop_times, ropidgtfs_stops")];
psql_shapes[("PSQL ropidgtfs_shapes")];
get_gtfs_detail["GET /v2/public/gtfs/trips/$trip_id"];
get_gtfs_detail--"Lookup GTFS trip id"<-->psql_trip;
get_gtfs_detail--"scope[]=stop_times"<-->psql_stop_times;
get_gtfs_detail--"scope[]=shapes"<-->psql_shapes;
```
### Flowchart (public API departures)
```mermaid
flowchart TD;
redis_pubsub_channel["Redis channel vpPublicCache"];
redis_subscriber["VPSubscriber"];
redis_public_cache[("Redis vpPublicCache:* (RT info)")];
redis_public_cache_misc[("Redis vpPublicCache:* (aktivní, budoucí a zrušené spoje)")];
redis_departures[("Redis gtfsPublicDepartureCache:*")];
redis_repository["OG PublicVehiclePositionsRepository"];
redis_subscriber--"Přihlášení k odběru"-->redis_pubsub_channel;
redis_pubsub_channel--"ID/název nové datové sady"-->redis_subscriber--"setCurrentSetName"-->redis_repository;
get_departures["GET /v2/public/departureboards"];
get_departures--"Odjezdy pro zastávky"-->redis_departures;
get_departures--"Aktivní vozidlo podle GTFS trip id"-->redis_repository<-->redis_public_cache_misc;
rt_info["Obohacení o RT informace"];
get_departures--"Alespoň jedno vozidlo připadá nějakému spoji (aktivní, budoucí i zrušené) na odjezdu"-->rt_info;
rt_info--"RT informace o spoji"-->redis_repository<-->redis_public_cache;
```
## Cache pro API přestupních tabulí (transfer boards)
*(přestupní tabule využívají také cache pro Public API, která je již popsána výše)*
### gtfsStopsCache:*
- Jednotlivé komponenty cache jsou členěny do sad s `$setId`
- `$setId` je id vygenerované datové sady (4 náhodné bajty reprezentované hexadecimálně)
- Nová sada je vytvořena při každém přenačtení cache
- Id `$setId` nejnovější datové sady se propaguje přes PubSub channel `gtfsStopsCache` a je také uloženo v cache jako hodnota položky `gtfsStopsCache:activeSetName`
- Komponenty:
- `gtfsStopsCache:$setId:aswNodeToGtfsStops` - Hash mapa, mapující daný ASW node na seznam pod něj spadajících zastávek (GTFS ids zastávek, odděleny čárkami)
- `gtfsStopsCache:$setId:cisToAswNode` - Hash mapa, mapující dané CIS id zastávky na příslušný ASW node
- TTL žádné (mimo přenačtení cache, kdy je namísto okamžitého promazání původnímu setu nastaveno TTL 1 minuta)
- Cache je celá přenačtena vždy po přenačtení jízdních řádů (tedy 1-2x denně při stavu k červenci 2025)
### jisCache:*
- Jednotlivé komponenty cache jsou členěny do sad s `$setId`
- `$setId` je id vygenerované datové sady (4 náhodné bajty reprezentované hexadecimálně)
- Nová sada je vytvořena při každém přenačtení cache
- Id `$setId` nejnovější datové sady se propaguje přes PubSub channel `jisCache` a je také uloženo v cache jako hodnota položky `jisCache:activeSetName`
- Komponenty:
- `jisCache:$setId:infotexts` - Hash mapa, mapující dané id infotextu na informace o onom infotextu (JSON jako string)
- `jisCache:$setId:stops-infotexts` - Hash mapa, mapující dané GTFS id zástavky na seznam infotextů s ní asociovaných (ids infotextů, odděleny čárkami)
- TTL žádné (mimo přenačtení cache, kdy je namísto okamžitého promazání původnímu setu nastaveno TTL 1 minuta)
- Cache je celá přenačtena při každé aktualizaci infotextů (tento proces je více popsán v [Dokumentaci propisu VYMI (JIS) dat do datové platformy](../../jis/index.md))
### Flowchart (refresh gtfsStopsCache:*)
```mermaid
flowchart LR
finish_up_gtfs("`Dokončení zpracování GTFS JŘ`");
psql_ropidgtfs_cis_stops[("`PSQL ropidgtfs_cis_stops`")];
psql_ropidgtfs_stops[("`PSQL ropidgtfs_stops`")];
redis_gtfsStopsCache[("`Redis gtfsStopsCache:\*`")];
amqp_refreshGtfsStopsCache["`AMQP refreshGtfsStopsCache`"];
task_RefreshGtfsStopsCacheTask["`RefreshGtfsStopsCacheTask`"];
finish_up_gtfs --> amqp_refreshGtfsStopsCache;
amqp_refreshGtfsStopsCache --> task_RefreshGtfsStopsCacheTask;
task_RefreshGtfsStopsCacheTask <-- "`ASW ids a CIS ids zastávek`" --> psql_ropidgtfs_cis_stops;
task_RefreshGtfsStopsCacheTask <-- "`ASW nodes a GTFS ids zastávek`" --> psql_ropidgtfs_stops;
task_RefreshGtfsStopsCacheTask -- "`HSET gtfsStopsCache:$setId:aswNodeToGtfsStops a gtfsStopsCache:$setId:cisToAswNode, EXPIRE staré sady`" --> redis_gtfsStopsCache;
```
### Flowchart (refresh jisCache:*)
```mermaid
flowchart LR
refresh_infotexts("`Aktualizace infotextů`");
psql_jis_infotexts[("`PSQL jis_infotexts`")];
psql_jis_infotexts_ropidgtfs_stops[("`PSQL jis_infotexts_ropidgtfs_stops`")];
redis_jisCache[("`Redis jisCache:\*`")];
amqp_refreshJISInfotextsCache["`AMQP refreshJISInfotextsCache`"];
task_RefreshJISInfotextsCacheTask["`RefreshJISInfotextsCacheTask`"];
refresh_infotexts --> amqp_refreshJISInfotextsCache;
amqp_refreshJISInfotextsCache --> task_RefreshJISInfotextsCacheTask;
task_RefreshJISInfotextsCacheTask <-- "`Data infotextů`" --> psql_jis_infotexts;
task_RefreshJISInfotextsCacheTask <-- "`GTFS ids zastávek a ids infotextů`" --> psql_jis_infotexts_ropidgtfs_stops;
task_RefreshJISInfotextsCacheTask -- "`HSET jisCache:$setId:infotexts a jisCache:$setId:stops-infotexts, EXPIRE staré sady`" --> redis_jisCache;
```
### gtfsTripStopsCache:*
- Klíč `gtfsTripStopsCache:*`, kde `*` je GTFS `trip_id`
- Hodnota je JSON string s polem zastávek seřazených podle `stop_sequence`:
- `stops` – pole dvojic `[stop_id, stop_name]`
- TTL 6 hodin (musí přesáhnout maximální dobu jízdy spoje ~3,5h + interval tasku 1h)
- Cache se přegeneruje jednou za hodinu (cron přes `EnsureCacheTask`) a po přenačtení GTFS jízdních řádů (přes `CheckSavedRowsAndReplaceTablesTask`)
- SQL dotaz vybírá všechny spoje, jejichž první zastávka (`stop_sequence = 1`) má `departure_datetime` v okně `now() − 1h` až `now() + 2h`, a pro každý takový spoj agreguje všechny zastávky
- Zapisuje `GtfsTripStopsCacheRepository` (IE) v dávkách po 200 klíčích přes Redis pipeline; před zápisem se validují data přes AJV schéma, nevalidní záznamy se přeskočí a zalogují
- Čte `GtfsTripStopsRepository` (OG) přes `MGET` — využívá `TransferFacade.findRelevantTripIdsFromLines()` pro filtrování přestupních linek
### Flowchart (v4 transferboards API)

link na .drawio [soubor](../../assets/V4Transferboards.drawio)
## Cache pro konfiguraci
### config:notPublicVehicles
- Klíč `config:notPublicVehicles` (jednoduchý klíč, žádný wildcard)
- Hodnota je JSON string struktury `INotPublicVehicles`:
- `tram.registrationNumbers` – seznam evidenčních čísel tramvají bez platného GTFS spoje
- `road.registrationNumbers` – seznam evidenčních čísel autobusů a trolejbusů bez platného GTFS spoje (`road` pokrývá obě kategorie, protože sdílejí číselnou řadu)
- `routeIds` – whitelist čísel linek (např. `"861"` pro náhradní autobus X-C při výluce metra C) zobrazovaných i bez platného JŘ spoje
- TTL žádné – hodnota se přepíše při každém spuštění `SaveStaticDataTask` (po načtení nových JŘ)
- Zapisuje `NotPublicVehiclesRedisRepository` (IE), čte `TripsRepository` / `PositionsMapper` (IE) a `UpdateRunsGtfsTripIdTask` (IE) pro povolení průchodu vozidel bez GTFS spoje
## Manuální refresh cache (EnsureCacheTask)
`EnsureCacheTask` slouží jako manuální spouštěč obnovy všech 5 caches, jejichž data nejsou real-time obnovována. Po přijetí prázdné zprávy na frontě `vehicle-positions.ropidgtfs.ensureCache` odešle paralelně prázdné zprávy do všech 5 cílových front. Dvě z nich míří do různých workerů (cross-worker routing přes RabbitMQ exchange), tři míří do stejného workeru (TimetableWorker).
### Flowchart
```mermaid
flowchart LR
trigger["`Zpráva do fronty ensureCache`"]
task["`EnsureCacheTask (TimetableWorker)`"]
exchange_vp["`Exchange ${RABBIT_EXCHANGE_NAME}.vehiclepositions (VPWorker)`"]
exchange_self["`Exchange this.queuePrefix (TimetableWorker)`"]
exchange_jis["`Exchange ${RABBIT_EXCHANGE_NAME}.jis (JISWorker)`"]
q1["`AMQP refreshGTFSTripData`"]
q2["`AMQP refreshPublicGtfsDepartureCache`"]
q3["`AMQP refreshGtfsStopsCache`"]
q4["`AMQP refreshGtfsTripStopsCache`"]
q5["`AMQP refreshJISInfotextsCache`"]
trigger --> task
task -- cross-worker --> exchange_vp --> q1
task -- same-worker --> exchange_self --> q2
exchange_self --> q3
exchange_self --> q4
task -- cross-worker --> exchange_jis --> q5
```