π ETL Pipeline: dicechess-sync
dicechess-sync β ΡΡΠΎ ΠΏΡΠΈΠ²Π°ΡΠ½ΡΠΉ ΡΠ΅ΡΠ²ΠΈΡ ΡΠ±ΠΎΡΠ° ΠΈ ΠΎΡΠΈΡΡΠΊΠΈ Π΄Π°Π½Π½ΡΡ
(ETL), ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½Π½ΡΠΉ Π΄Π»Ρ ΠΈΡΡΠΎΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΈΠΌΠΏΠΎΡΡΠ° (backfill) ΡΡΠ³ΡΠ°Π½Π½ΡΡ
ΠΏΠ°ΡΡΠΈΠΉ Ρ ΠΎΡΠΈΡΠΈΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠ°ΠΉΡΠ° dicechess.com, ΠΈΡ
Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΠΈ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ Ρ Π±Π°Π·ΠΎΠΉ Π΄Π°Π½Π½ΡΡ
dicechess-analytics.
IMPORTANT
ΠΡΠΈΡΠΈΠ½Π° ΠΏΡΠΈΠ²Π°ΡΠ½ΠΎΡΡΠΈ ΡΠ΅ΠΏΠΎΠ·ΠΈΡΠΎΡΠΈΡ:
Π Π΅ΠΏΠΎΠ·ΠΈΡΠΎΡΠΈΠΉ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ ΠΎΠ±ΡΠ°ΡΠ½ΡΡ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΡ (reverse engineering) ΠΏΡΠΈΠ²Π°ΡΠ½ΠΎΠ³ΠΎ API dicechess.com. ΠΠ° ΡΠ°ΠΉΡΠ΅ Π½Π΅Ρ ΠΏΡΠ±Π»ΠΈΡΠ½ΠΎΠ³ΠΎ ΡΠ°Π·ΡΠ΅ΡΠ΅Π½ΠΈΡ Π½Π° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ±ΠΎΡ Π΄Π°Π½Π½ΡΡ , ΠΏΠΎΡΡΠΎΠΌΡ Π²ΡΠ΅ Π΄Π΅ΡΠ°Π»ΠΈ ΡΠΊΡΠ΅ΠΉΠΏΠΈΠ½Π³Π° ΠΈ ΠΎΠ±Ρ ΠΎΠ΄Π° Π·Π°ΡΠΈΡΡ Π²ΡΠ½Π΅ΡΠ΅Π½Ρ Π² ΠΏΡΠΈΠ²Π°ΡΠ½ΡΠΉ ΠΊΠΎΠ½ΡΡΡ. ΠΡΠ±Π»ΠΈΡΠ½ΡΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΡ (dicechess-analytics,dicechess-engine-scala) ΠΎΡΡΠ°ΡΡΡΡ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΡΠΌΠΈ ΠΈ ΡΠΈΡΡΡΠΌΠΈ ΠΎΡ ΡΠΏΠ΅ΡΠΈΡΠΈΠΊΠΈ API dicechess.com.
1. ΠΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΡΠ΅ Π³ΡΠ°Π½ΠΈΡΡ ΠΏΡΠΎΠ΅ΠΊΡΠ°
Π‘Π΅ΡΠ²ΠΈΡ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΊΠ°ΠΊ ΠΌΠΎΡΡ ΠΌΠ΅ΠΆΠ΄Ρ Π·Π°ΠΊΡΡΡΡΠΌ Π²Π΅Π±-ΠΊΠ»ΠΈΠ΅Π½ΡΠΎΠΌ ΠΈ Π½Π°ΡΠ΅ΠΉ ΠΎΡΠΊΡΡΡΠΎΠΉ Π°Π½Π°Π»ΠΈΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠΎΠΉ.
graph TD subgraph dicechess.com [dicechess.com (ΠΠ½Π΅ΡΠ½ΠΈΠΉ ΡΠ΅ΡΡΡΡ)] API[Private API] end subgraph Private_Pipeline [dicechess-sync (ΠΡΠΈΠ²Π°ΡΠ½ΡΠΉ ΠΊΠΎΠ½ΡΡΡ)] direction TB Crawler[Crawler Daemon / Crawl Service] SQLite[(Local SQLite Cache)] end subgraph Public_Analytics [dicechess-analytics (ΠΡΠ±Π»ΠΈΡΠ½ΡΠΉ ΠΊΠΎΠ½ΡΡΡ)] Ingest[POST /api/games Ingest API] Postgres[(PostgreSQL Analytical DB)] Engine[Scala 3 Engine Validation] end subgraph Backup_Storage [Dexus Home Server (ΠΡΡ ΠΈΠ²)] DexusArchive[(dicechess-raw-archive)] end %% ΠΠ·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΡ API -->|1. Raw JSON (jwt auth)| Crawler Crawler <-->|2. Read/Write Catalog & Blobs| SQLite Crawler -->|3. GameIngestWire (token auth)| Ingest Ingest -->|4. Replay & Validate| Engine Ingest -->|5. Write Games & FENs| Postgres Crawler -->|6. Mirror Raw Cache| DexusArchive
2. ΠΠΎΠΊΠ°Π»ΡΠ½ΠΎΠ΅ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅ Π΄Π°Π½Π½ΡΡ (SQLite)
ΠΠΎΠΊΠ°Π»ΡΠ½Π°Ρ Π±Π°Π·Π° Π΄Π°Π½Π½ΡΡ SQLite ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΏΠΎ ΠΏΡΠΈΠ½ΡΠΈΠΏΡ Bronze-ΡΠ»ΠΎΡ (Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅ ΡΡΡΡΡ Π½Π΅ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Π½ΡΡ ΠΎΡΠ²Π΅ΡΠΎΠ² API). ΠΡΠΎ ΠΊΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈ Π²Π°ΠΆΠ½ΠΎΠ΅ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ½ΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅:
TIP
ΠΡΠ΅ΠΈΠΌΡΡΠ΅ΡΡΠ²ΠΎ ΡΡΡΠΎΠ³ΠΎ ΠΊΡΡΠ°:
ΠΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΏΠ°ΡΡΠΈΠ½Π³Π° Ρ ΠΎΠ΄ΠΎΠ², ΡΠ°ΡΡΠ΅ΡΠ° FEN ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π½Π° ΡΡΠΎΡΠΎΠ½Π΅ Π΄Π²ΠΈΠΆΠΊΠ° ΠΏΠΎΡΡΠΎΡΠ½Π½ΠΎ Π΄ΠΎΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ, ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ ΠΏΠ΅ΡΠ΅ΠΏΠΈΡΠ°ΡΡ ΠΏΠ°ΡΡΠ΅Ρ/Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΎΡ Ρ ΠΎΠ΄ΠΎΠ² ΠΈ ΠΏΠ΅ΡΠ΅ΡΡΠΈΡΠ°ΡΡ Π²ΡΡ ΠΈΡΡΠΎΡΠΈΡ ΠΈΠ³Ρ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ, Π½Π΅ Π΄Π΅Π»Π°Ρ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠ³ΠΎ Π·Π°ΠΏΡΠΎΡΠ° ΠΊ Π·Π°ΡΠΈΡΠ΅Π½Π½ΠΎΠΌΡ ΡΠ°ΠΉΡΡ.
Π’Π°Π±Π»ΠΈΡΡ Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ :
erDiagram players { integer user_id PK "DiceChess ID" text username text player_type "human | bot | guest" integer rating_classic integer rating_x2 integer priority "Max known rating" text sync_status "pending | in_progress | synced | error" integer enumerate_offset "Pagination cursor" integer synced_through_ms "StartTime cursor" } games { text game_id PK "UUID" text status "discovered | fetched | posted | rejected | error" integer white_user_id "FK to players" integer black_user_id "FK to players" integer started_at_ms "Unix timestamp" integer allow_doubling "0/1 (classic/x2)" integer is_cancelled text post_result "created | exists" } raw_game_data { text game_id PK "FK to games" text history_meta_json "Metadata from player history" text move_history_json "Verbatim response from game-move-history" text move_history_fetched_at text archived_at "Timestamp of dexus mirror" } games ||--o| raw_game_data : "has raw"
3. ΠΠ°ΡΠΈΠ½Ρ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΉ (State Machines)
ΠΠ»Ρ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠΉ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ±ΠΎΠ΅Π² ΡΠ΅ΡΠΈ, ΠΎΡΠΈΠ±ΠΎΠΊ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ Π΄Π²ΠΈΠΆΠΊΠ° ΠΈ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²ΠΎΠΊ API, ΠΏΡΠΎΡΠ΅ΡΡΡ ΠΊΡΠ°ΡΠ»ΠΈΠ½Π³Π° ΠΈΠ³ΡΠΎΠΊΠΎΠ² ΠΈ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΠΈΠ³Ρ ΡΠ°Π·Π΄Π΅Π»Π΅Π½Ρ Π½Π° Π΄Π²Π΅ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΡΠ΅ ΠΌΠ°ΡΠΈΠ½Ρ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΉ.
3.1. ΠΠΈΠ·Π½Π΅Π½Π½ΡΠΉ ΡΠΈΠΊΠ» ΠΏΠ°ΡΡΠΈΠΈ (Games)
stateDiagram-v2 [*] --> Discovered : Player history enumeration Discovered --> Fetched : Fetch game-move-history API Discovered --> Error_Fetch : Network / Auth Failure (Retry) Error_Fetch --> Discovered : Backoff timer expired Fetched --> Posted : POST 201 Created / 200 Exists (Ingested) Fetched --> Rejected : POST 422 Unprocessable (Invalid Moves) Fetched --> Error_Post : 5xx / Ingestion Timeout (Retry) Fetched --> Error_Normalize : Parse error / Bug in code Error_Post --> Fetched : Backoff timer expired Error_Normalize --> Fetched : Manual code-fix & DB update Rejected --> Fetched : Manual rebuild on Engine upgrade Posted --> [*]
3.2. ΠΠΈΠ·Π½Π΅Π½Π½ΡΠΉ ΡΠΈΠΊΠ» ΠΊΡΠ°ΡΠ»ΠΈΠ½Π³Π° ΠΈΠ³ΡΠΎΠΊΠ° (Players)
stateDiagram-v2 [*] --> Pending : Discovered via leaderboard or opponent BFS Pending --> In_Progress : Selected by rating priority In_Progress --> Synced : All historical pages parsed In_Progress --> Error : API blocks / Timeout (Retry) Error --> Pending : Backoff timer expired Synced --> Pending : Re-queued for incremental updates (START_DATE)
4. ΠΠΎΠ½Π²Π΅ΠΉΠ΅Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ Π΄Π°Π½Π½ΡΡ (The 5-Step Pipeline)
ΠΡΠΎΡΠ΅ΡΡ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΏΠ°ΡΡΠΈΠΈ ΡΠ°Π·Π΄Π΅Π»Π΅Π½ Π½Π° 5 ΠΈΠ·ΠΎΠ»ΠΈΡΠΎΠ²Π°Π½Π½ΡΡ ΡΠ°Π³ΠΎΠ²:
Π¨Π°Π³ 1. Discover (ΠΠΎΠΈΡΠΊ ΠΈΠ³ΡΠΎΠΊΠΎΠ²)
ΠΠΎΠΈΡΠΊ Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ Ρ Β«ΡΠ΅ΠΌΡΠ½Β» (Seeds): ΡΠΎΠΏ-200 ΠΈΠ³ΡΠΎΠΊΠΎΠ² ΠΈΠ· ΠΊΠ»Π°ΡΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π»ΠΈΠ΄Π΅ΡΠ±ΠΎΡΠ΄Π° ΠΈ Π»ΠΈΠ΄Π΅ΡΠ±ΠΎΡΠ΄Π° X2 (GET /api/leaderboard). ΠΡΠ΅ΡΠ΅Π΄Ρ BFS (ΠΎΠ±Ρ
ΠΎΠ΄ Π² ΡΠΈΡΠΈΠ½Ρ) ΡΠ°ΡΡΠ΅Ρ Π·Π° ΡΡΠ΅Ρ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΡ ΠΎΠΏΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² ΠΈΠ· ΡΡΠ³ΡΠ°Π½Π½ΡΡ
ΠΏΠ°ΡΡΠΈΠΉ. ΠΡΠΈΠΎΡΠΈΡΠ΅Ρ ΠΎΠ±Ρ
ΠΎΠ΄Π° ΠΎΡΠ΄Π°Π΅ΡΡΡ ΠΈΠ³ΡΠΎΠΊΠ°ΠΌ Ρ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΌ ΡΠ΅ΠΉΡΠΈΠ½Π³ΠΎΠΌ.
Π¨Π°Π³ 2. Enumerate (Π‘Π±ΠΎΡ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΡ ΠΏΠ°ΡΡΠΈΠΉ)
ΠΠ»Ρ Π²ΡΠ±ΡΠ°Π½Π½ΠΎΠ³ΠΎ ΠΈΠ³ΡΠΎΠΊΠ° ΠΏΠΎΡΡΡΠ°Π½ΠΈΡΠ½ΠΎ Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°Π΅ΡΡΡ Π΅Π³ΠΎ ΠΈΡΡΠΎΡΠΈΡ (POST /api/player/history).
- ΠΠ°ΠΏΡΠΎΡ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΠ΅ ΠΏΠ°ΡΡΠΈΠΉ (ΡΠ΅ΠΉΡΠΈΠ½Π³ΠΈ ΠΈΠ³ΡΠΎΠΊΠΎΠ² Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ ΠΈΠ³ΡΡ, Π»ΠΈΠΌΠΈΡΡ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ, ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΡ).
- ΠΠΎΠ²ΡΠ΅ ΠΏΠ°ΡΡΠΈΠΈ Π²Π½ΠΎΡΡΡΡΡ Π² ΡΠ°Π±Π»ΠΈΡΡ
gamesΠ² ΡΡΠ°ΡΡΡΠ΅discovered. - ΠΠΏΠΏΠΎΠ½Π΅Π½ΡΡ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡΡΡ Π² ΡΠ°Π±Π»ΠΈΡΡ
playersΠ² ΡΡΠ°ΡΡΡΠ΅pending(Π΅ΡΠ»ΠΈ ΠΈΡ ΡΠ°ΠΌ Π΅ΡΠ΅ Π½Π΅ Π±ΡΠ»ΠΎ).
Π¨Π°Π³ 3. Fetch Raw (Π‘ΠΊΠ°ΡΠΈΠ²Π°Π½ΠΈΠ΅ Π»ΠΎΠ³Π° Ρ ΠΎΠ΄ΠΎΠ²)
ΠΠ»Ρ ΠΏΠ°ΡΡΠΈΠΉ Π² ΡΡΠ°ΡΡΡΠ΅ discovered Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°Π΅ΡΡΡ Π΄Π΅ΡΠ°Π»ΡΠ½ΡΠΉ Π»ΠΎΠ³ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΉ Π΄ΠΎΡΠΊΠΈ (GET /api/game-move-history?gameId=...). ΠΠΎΠ»ΡΡΠ΅Π½Π½ΡΠΉ ΡΡΠΆΠ΅Π»ΡΠΉ JSON-ΠΎΡΠ²Π΅Ρ ΡΠΎΡ
ΡΠ°Π½ΡΠ΅ΡΡΡ Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π² ΡΠ°Π±Π»ΠΈΡΡ raw_game_data, ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄Ρ ΠΈΠ³ΡΡ Π² ΡΡΠ°ΡΡΡ fetched.
Π¨Π°Π³ 4. Normalize (ΠΡΠΈΡΡΠΊΠ° ΠΈ Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Ρ ΠΎΠ΄ΠΎΠ²)
ΠΡΠΈΠ²Π°ΡΠ½ΡΠΉ API game-move-history Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π½Π΅ ΡΠΏΠΈΡΠΎΠΊ Ρ
ΠΎΠ΄ΠΎΠ², Π° Π»ΠΎΠ³ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΉ (gameMoveHistoryStateMap), ΠΊΠΎΡΠΎΡΡΠΉ ΠΈΠΌΠ΅Π΅Ρ ΡΡΠ΄ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎΡΡΠ΅ΠΉ:
- ΠΡΡΡΡΡΡΠ²ΠΈΠ΅ ΡΠ΅ΡΠΊΠΈΡ Π³ΡΠ°Π½ΠΈΡ Ρ ΠΎΠ΄ΠΎΠ²: ΠΎΠ΄ΠΈΠ½ Ρ ΠΎΠ΄ ΠΈΠ³ΡΠΎΠΊΠ° ΡΠΊΠ»Π°Π΄ΡΠ²Π°Π΅ΡΡΡ ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ Π·Π°ΠΏΠΈΡΠ΅ΠΉ β ΡΠΎΠ±ΡΡΠΈΡ Π±ΡΠΎΡΠΊΠ° ΠΊΡΠ±ΠΈΠΊΠΎΠ² ΠΈ ΠΎΡ 1 Π΄ΠΎ 3 ΡΠΎΠ±ΡΡΠΈΠΉ ΠΌΠΈΠΊΡΠΎ-Ρ ΠΎΠ΄ΠΎΠ².
- Π‘ΠΌΠ΅ΡΠ°Π½Π½ΡΠΉ ΡΠ΅Π³ΠΈΡΡΡ ΡΠΈΠ³ΡΡ: ΡΠ²Π΅Ρ ΠΊΠΎΠ΄ΠΈΡΡΠ΅ΡΡΡ ΡΠ΅Π³ΠΈΡΡΡΠΎΠΌ Π±ΡΠΊΠ² Π² ΠΊΡΠ±ΠΈΠΊΠ°Ρ
(
P/N/B/R/Q/Kβ Π±Π΅Π»ΡΠ΅,p/n/b/r/q/kβ ΡΠ΅ΡΠ½ΡΠ΅). - Π‘Π»ΡΠΆΠ΅Π±Π½ΡΠ΅ Π·Π°ΠΏΠΈΡΠΈ: ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ ΡΠ΅Ρ Π½ΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΎΡΠΌΠ΅ΡΠΊΠΈ (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ ΡΠ΄Π²ΠΎΠ΅Π½ΠΈΡ ΡΡΠ°Π²ΠΎΠΊ ΠΈΠ»ΠΈ ΡΠ΄Π°ΡΠ° ΠΏΠ°ΡΡΠΈΠΈ).
ΠΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΎΡ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΡΠ΅Ρ ΡΡΠΎΡ Π»ΠΎΠ³ Π² ΡΠΈΡΡΡΠ΅ Ρ ΠΎΠ΄Ρ: Π±ΡΠΎΡΠ΅Π½Π½ΡΠ΅ ΠΊΡΠ±ΠΈΠΊΠΈ, ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΌΠΈΠΊΡΠΎ-Ρ ΠΎΠ΄ΠΎΠ² Π² ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΠΎΠΌ ΡΠΎΡΠΌΠ°ΡΠ΅ ΠΈ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ Π΄ΠΎΡΠΊΠΈ.
Π¨Π°Π³ 5. Post Ingest (ΠΡΠΏΡΠ°Π²ΠΊΠ° Π² Π°Π½Π°Π»ΠΈΡΠΈΠΊΡ)
Π‘ΡΠΎΡΠΌΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ JSON-ΠΏΠ°ΠΊΠ΅Ρ (GameIngestWire) ΠΎΡΠΏΡΠ°Π²Π»ΡΠ΅ΡΡΡ Π½Π° endpoint POST /api/games Π°Π½Π°Π»ΠΈΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΡΠ΅ΡΠ²ΠΈΡΠ°. ΠΠ½Π°Π»ΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ΅ΡΠ²ΠΈΡ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎ ΠΏΡΠΎΠΊΡΡΡΠΈΠ²Π°Π΅Ρ Ρ
ΠΎΠ΄Ρ ΡΠ΅ΡΠ΅Π· ΠΎΡΠΈΡΠΈΠ°Π»ΡΠ½ΡΠΉ Scala-Π΄Π²ΠΈΠΆΠΎΠΊ, ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΠΈΡ
Π»Π΅Π³Π°Π»ΡΠ½ΠΎΡΡΡ, Π²ΡΡΠΈΡΠ»ΡΠ΅Ρ ΠΊΠΎΠ½ΡΡΠΎΠ»ΡΠ½ΡΠ΅ ΡΡΠΌΠΌΡ ΠΏΠΎΠ·ΠΈΡΠΈΠΉ (xxhash64) Π΄Π»Ρ Π΄Π΅Π΄ΡΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈ ΠΈ Π·Π°ΠΏΠΈΡΡΠ²Π°Π΅Ρ ΠΏΠ°ΡΡΠΈΡ Π² Π±ΠΎΠ΅Π²ΡΡ ΠΠ.
5. ΠΠ°ΡΠΈΡΠ° ΠΎΡ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²ΠΎΠΊ (Rate Limiting & Safety)
ΠΠΎΡΡΠΎΡΠ½Π½ΡΠΉ ΡΠΊΡΠ΅ΠΉΠΏΠΈΠ½Π³ ΠΈΡΡΠΎΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π°ΡΡ
ΠΈΠ²Π° (ΡΠΎΡΠ½ΠΈ ΡΡΡΡΡ ΠΏΠ°ΡΡΠΈΠΉ) Π»Π΅Π³ΠΊΠΎ ΠΌΠΎΠΆΠ΅Ρ ΡΠΏΡΠΎΠ²ΠΎΡΠΈΡΠΎΠ²Π°ΡΡ Π±Π°Π½ ΡΠΎ ΡΡΠΎΡΠΎΠ½Ρ Ρ
ΠΎΡΡΠΈΠ½Π³Π° ΠΈΠ»ΠΈ Cloudflare. Π dicechess-sync Π²ΡΡΡΠΎΠ΅Π½Π° ΠΌΠ½ΠΎΠ³ΠΎΡΡΠΎΠ²Π½Π΅Π²Π°Ρ Π·Π°ΡΠΈΡΠ°:
- Single-flight Queue: ΠΡΠ΅ Π²Π½Π΅ΡΠ½ΠΈΠ΅ Π·Π°ΠΏΡΠΎΡΡ ΠΊ ΡΠ°ΠΉΡΡ ΡΡΡΠΎΠ³ΠΎ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½Ρ. ΠΠ°ΠΏΡΠΎΡΡ Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ Π²ΡΠΏΠΎΠ»Π½ΡΡΡΡΡ ΠΏΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½ΠΎ Ρ ΠΎΠ΄Π½ΠΎΠ³ΠΎ JWT-ΡΠΎΠΊΠ΅Π½Π°.
- Π‘Π»ΡΡΠ°ΠΉΠ½ΡΠΉ Jitter: ΠΠ΅ΠΆΠ΄Ρ Π·Π°ΠΏΡΠΎΡΠ°ΠΌΠΈ Π²ΡΠ΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΡΡΡ ΡΠ»ΡΡΠ°ΠΉΠ½Π°Ρ ΠΏΠ°ΡΠ·Π° (Π΄ΠΆΠΈΡΡΠ΅Ρ) ΠΎΡ 1 Π΄ΠΎ 3 ΡΠ΅ΠΊΡΠ½Π΄ Π΄Π»Ρ ΠΈΠΌΠΈΡΠ°ΡΠΈΠΈ ΡΠ΅Π»ΠΎΠ²Π΅ΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ.
- ΠΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΈΠ΅ Cloudflare Challenge: ΠΡΠ»ΠΈ API Π½Π°ΡΠΈΠ½Π°Π΅Ρ ΠΎΡΠ΄Π°Π²Π°ΡΡ ΡΡΡΠ°Π½ΠΈΡΡ Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΎΠΉ ΠΊΠ°ΠΏΡΠΈ ΠΈΠ»ΠΈ
403 Forbidden, ΠΊΡΠ°ΡΠ»Π΅Ρ Π½Π΅ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΡΡΡ, ΠΎΡΠΏΡΠ°Π²Π»ΡΡ ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ Π°Π΄ΠΌΠΈΠ½ΠΈΡΡΡΠ°ΡΠΎΡΡ, Π²ΠΌΠ΅ΡΡΠΎ ΡΠΎΠ³ΠΎ ΡΡΠΎΠ±Ρ ΠΏΠ΅ΡΠ΅Π³ΡΡΠΆΠ°ΡΡ Π΄ΠΎΠΌΠ΅Π½ Π·Π°ΠΏΡΠΎΡΠ°ΠΌΠΈ. - Exponential Backoff: ΠΡΠΈ ΡΠ΅ΡΠ΅Π²ΡΡ
ΠΎΡΠΈΠ±ΠΊΠ°Ρ
ΠΈΠ»ΠΈ
429 Too Many RequestsΠ²ΡΠ΅ΠΌΡ ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΡ Π΄ΠΎ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠ³ΠΎ Π·Π°ΠΏΡΠΎΡΠ° ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅ΡΡΡ ΡΠΊΡΠΏΠΎΠ½Π΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ (Π΄ΠΎ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ ΡΠ°ΡΠΎΠ²).
6. ΠΠ΅ΡΠΊΠ°Π»ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π°ΡΡ ΠΈΠ²Π° (Archiving)
ΠΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½ΠΎ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΌΡ ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅ΡΡ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΡΠΉ ΡΠΎΠ½ΠΎΠ²ΡΠΉ ΠΏΠΎΡΠΎΠΊ Π°ΡΡ
ΠΈΠ²Π°ΡΠΈΠΈ. ΠΠ½ ΡΡΠΈΡΡΠ²Π°Π΅Ρ ΡΡΡΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· ΡΠ°Π±Π»ΠΈΡΡ raw_game_data ΠΈ Π·Π΅ΡΠΊΠ°Π»ΠΈΡΡΠ΅Ρ ΠΈΡ
Π½Π° ΡΠ΅Π½ΡΡΠ°Π»ΡΠ½ΡΠΉ ΡΠ΅ΡΠ²Π΅Ρ Π±ΡΠΊΠ°ΠΏΠΎΠ² dexus Π² Π±Π°Π·Ρ dicechess-raw-archive. ΠΡΠΎ ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠΈΠ²Π°Π΅Ρ 100% ΡΠΎΡ
ΡΠ°Π½Π½ΠΎΡΡΡ ΠΈΡΡΠΎΡΠΈΠΈ ΠΈΠ³Ρ ΠΏΡΠΎΠ΅ΠΊΡΠ° Π½Π° ΡΠ»ΡΡΠ°ΠΉ ΡΡΠ΅ΡΠΈ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎΠ³ΠΎ Π΄ΠΈΡΠΊΠ° ΠΈΠ»ΠΈ ΠΏΠΎΠ»ΠΎΠΌΠΊΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°.