πŸ•ΈοΈ dicechess-sync β€” слуТСбная справка

dicechess-sync β€” ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ сСрвис бэкфилла: ΠΎΠ½ ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΡ‚ Π³Ρ€Π°Ρ„ ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² dicechess.com, скачиваСт историчСскиС ΠΏΠ°Ρ€Ρ‚ΠΈΠΈ, Π½ΠΎΡ€ΠΌΠ°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ ΠΈΡ… ΠΈ Π·Π°Π»ΠΈΠ²Π°Π΅Ρ‚ Π² Π°Π½Π°Π»ΠΈΡ‚ΠΈΠΊΡƒ, ΠΏΠΎΠΏΡƒΡ‚Π½ΠΎ зСркаля ΡΡ‹Ρ€ΡŒΡ‘ Π² bronze-Π°Ρ€Ρ…ΠΈΠ².

Π­Ρ‚Π° страница β€” опСрационная справка Β«ΠΊΠ°ΠΊ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ ΠΈ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒΒ». Как ΠΎΠ½ΠΎ устроСно ΠΏΠΎ сущСству β€” Π² спайнС:

ΠŸΠΎΡ‡Π΅ΠΌΡƒ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ

Он содСрТит ΠΎΠ±Ρ€Π°Ρ‚Π½ΡƒΡŽ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ (reverse engineering) ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ API dicechess.com β€” ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π½Π° автоматичСский сбор Π½Π΅Ρ‚, поэтому вся спСцифика скрСйпинга ΠΈ ΠΎΠ±Ρ…ΠΎΠ΄Π° Π·Π°Ρ‰ΠΈΡ‚Ρ‹ вынСсСна Π² ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚ΡƒΡ€. ΠŸΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹ (dicechess-analytics, dicechess-engine-scala) ΠΎΡΡ‚Π°ΡŽΡ‚ΡΡ чистыми ΠΎΡ‚ спСцифики сайта β€” ΠΈΡ… Π³Ρ€Π°Π½ΠΈΡ†Π° β€” Π½Π΅ΠΉΡ‚Ρ€Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ POST /api/games.


1. Π‘Ρ‚Π΅ΠΊ ΠΈ устройство


2. CLI-ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹

ВсС Π·Π°ΠΏΡƒΡΠΊΠ°ΡŽΡ‚ΡΡ Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΌ Node Π±Π΅Π· сборки. Π’Ρ€Π΅Π±ΡƒΡŽΡ‚ DICECHESS_JWT Π² ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠΈ.

ΠšΠΎΠΌΠ°Π½Π΄Π°ΠΠ°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅
node src/sync-player.ts <userId> [maxFetch]Бэкфилл ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° end-to-end: enumerate β†’ fetch (≀ maxFetch, ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ∞) β†’ post. Π Π΅Π·ΡŽΠΌΠΈΡ€ΡƒΠ΅ΠΌΠΎ: ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½Ρ‹ΠΉ запуск синхронизированного ΠΈΠ³Ρ€ΠΎΠΊΠ° Π΄Π΅Π»Π°Π΅Ρ‚ ΠΈΠ½ΠΊΡ€Π΅ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ свип ΠΎΡ‚ курсора synced_through_ms.
node src/sync-crawl.ts <maxPlayers> <maxFetch> [seedId,…]ΠžΠ΄Π½ΠΎΡ€Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹ΠΉ ΠΎΠ±Ρ…ΠΎΠ΄ Π³Ρ€Π°Ρ„Π°: seed-ΠΈΠ³Ρ€ΠΎΠΊΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ ΠΊΠ°ΠΊ manual, дальшС спайдСр ΠΏΠΎ ΠΏΡ€ΠΈΠΎΡ€ΠΈΡ‚Π΅Ρ‚Ρƒ. RESYNC_BEFORE=<ISO> ΠΏΠ΅Ρ€Π΅ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈΡ‚ ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΡ… синхронизированных.
node src/crawl-service.tsΠ”ΠΎΠ»Π³ΠΎΠΈΠ³Ρ€Π°ΡŽΡ‰ΠΈΠΉ Π΄Π΅ΠΌΠΎΠ½ (Docker CMD) β€” повторяСт ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹Π΅ ΠΏΡ€ΠΎΡ…ΠΎΠ΄Ρ‹, ΠΏΠΎΠΊΠ° Ρ„Ρ€ΠΎΠ½Ρ‚ΠΈΡ€ Π½Π΅ опустССт, Π·Π°Ρ‚Π΅ΠΌ простаиваСт. Π‘ΠΌ. Β§4.
node src/sync-archive.tsΠžΠ΄Π½ΠΎΡ€Π°Π·ΠΎΠ²ΠΎ Π΄ΠΎΡ‚ΠΎΠ»ΠΊΠ½ΡƒΡ‚ΡŒ всё ΠΊΡΡˆΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠ΅ ΡΡ‹Ρ€ΡŒΡ‘ Π² bronze-Π°Ρ€Ρ…ΠΈΠ² Π½Π° dexus. Π’Ρ€Π΅Π±ΡƒΠ΅Ρ‚ ARCHIVE_DB_URL.

Π‘ΠΈΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π³Ρ€Π°Ρ„Π°

БСйчас Ρ„Ρ€ΠΎΠ½Ρ‚ΠΈΡ€ сидируСтся Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ β€” SEED_IDS (ΠΈΠ»ΠΈ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ sync-crawl), ΠΈΠ³Ρ€ΠΎΠΊΠΈ ΠΏΠΎΠΏΠ°Π΄Π°ΡŽΡ‚ с discovered_via='manual', дальшС Π³Ρ€Π°Ρ„ расходится спайдСром ΠΏΠΎ ΠΎΠΏΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌ. Π›ΠΈΠ΄Π΅Ρ€Π±ΠΎΡ€Π΄Ρ‹ (leaderboard/x2_leaderboard) β€” Π·Π°Π΄ΡƒΠΌΠ°Π½Π½Ρ‹ΠΉ источник seed’ов ΠΈ Π·Π°Ρ€Π΅Π·Π΅Ρ€Π²ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹ ΠΊΠ°ΠΊ значСния discovered_via, Π½ΠΎ автоматичСский Ρ„Π΅Ρ‚Ρ‡ Π»ΠΈΠ΄Π΅Ρ€Π±ΠΎΡ€Π΄ΠΎΠ² ΠΏΠΎΠΊΠ° Π½Π΅ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½. ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΏΡ€ΠΎ ΠΎΠ±Ρ…ΠΎΠ΄ β€” 04 Π“Ρ€Π°Ρ„ ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² ΠΈ пСрСчислСниС.


3. ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ окруТСния

ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Π°ΡΠŸΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽΠΠ°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅
DICECHESS_JWTβ€” (ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ)Bearer-JWT сайта (ΠΈΠ· Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°, ΠΆΠΈΠ²Ρ‘Ρ‚ ~мСсяцы).
ANALYTICS_BASE_URLhttp://192.168.10.3:8020Π‘Π°Π·Π° ingest-Π°Π½Π°Π»ΠΈΡ‚ΠΈΠΊΠΈ (aurora).
ANALYTICS_INGEST_TOKEN''Bearer для POST /api/games.
DB_PATH./data/sync.dbΠŸΡƒΡ‚ΡŒ ΠΊ SQLite.
ARCHIVE_DB_URL'' (пусто β†’ Π°Ρ€Ρ…ΠΈΠ² пропускаСтся)DSN bronze-Π°Ρ€Ρ…ΠΈΠ²Π° (dexus :5433).
DRY_RUNfalse1/true β†’ enumerate+fetch+cache, Π±Π΅Π· POST.
REQUEST_MIN_DELAY_MS3000Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π» ΠΌΠ΅ΠΆΠ΄Ρƒ запросами ΠΊ сайту. (1500 Π±Ρ‹Π» слишком быстр β€” Π»ΠΎΠ²ΠΈΠ» 429.)
REQUEST_JITTER_MS1500Блучайная Π΄ΠΎΠ±Π°Π²ΠΊΠ° [0, jitter).
REQUEST_MAX_RETRIES5Π Π΅Ρ‚Ρ€Π°ΠΈ запроса ΠΏΡ€ΠΈ throttle (ΠΆΠ΄Ρ‘Ρ‚ cooldown, повторяСт Ρ‚ΠΎΡ‚ ΠΆΠ΅ Π²Ρ‹Π·ΠΎΠ²).
MAX_REQUESTS_PER_HOUR∞ (Π½Π΅ Π·Π°Π΄Π°Π½ΠΎ)Часовой Π±ΡŽΠ΄ΠΆΠ΅Ρ‚ (ΠΎΠΊΠ½ΠΎ 1 Ρ‡). НС Π»ΠΎΠ²ΠΈΡ‚ ΠΌΠΈΠ½ΡƒΡ‚Π½Ρ‹ΠΉ всплСск β€” это Π΄Π΅Π»Π°Π΅Ρ‚ spacing.
ENUMERATE_PAGE_SIZE500Π Π°Π·ΠΌΠ΅Ρ€ страницы player/history. Π“Π»Π°Π²Π½Ρ‹ΠΉ Ρ€Ρ‹Ρ‡Π°Π³ ΠΏΡ€ΠΎΡ‚ΠΈΠ² Π»ΠΈΠΌΠΈΡ‚Π° пСрСчислСния (для ΠΊΠΈΡ‚ΠΎΠ² ΠΏΠΎΠ΄Π½ΠΈΠΌΠ°ΡŽΡ‚ Π΄ΠΎ 1000).
SEED_IDS[]id ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² для посСва Ρ„Ρ€ΠΎΠ½Ρ‚ΠΈΡ€Π° (Π΄Π΅ΠΌΠΎΠ½), Ρ‡Π΅Ρ€Π΅Π· Π·Π°ΠΏΡΡ‚ΡƒΡŽ.
BATCH_PLAYERS25Π˜Π³Ρ€ΠΎΠΊΠΎΠ² Π½Π° ΠΏΡ€ΠΎΡ…ΠΎΠ΄ Π΄Π΅ΠΌΠΎΠ½Π°.
BATCH_FETCH200ΠŸΠ°Ρ€Ρ‚ΠΈΠΉ-Ρ„Π΅Ρ‚Ρ‡Π΅ΠΉ Π½Π° ΠΏΡ€ΠΎΡ…ΠΎΠ΄ Π΄Π΅ΠΌΠΎΠ½Π°.
INTER_PASS_MS0ΠŸΠ°ΡƒΠ·Π° ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π°ΠΌΠΈ, ΠΏΠΎΠΊΠ° Π΅ΡΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Π°.
IDLE_MS300000 (5 ΠΌΠΈΠ½)Π‘ΠΎΠ½, ΠΊΠΎΠ³Π΄Π° Ρ„Ρ€ΠΎΠ½Ρ‚ΠΈΡ€ опустСл.
RESYNC_AFTER_MS0>0: ΠΏΠ΅Ρ€Π΅ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈΡ‚ΡŒ ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ², синхронизированных Π΄Π°Π²Π½Π΅Π΅ этого (ΠΈΠ½ΠΊΡ€Π΅ΠΌ. Ρ€Π΅Ρ„Ρ€Π΅Ρˆ Π΄Π΅ΠΌΠΎΠ½Π°).

Π£Ρ€ΠΎΠΊΠΈ ΠΈ тонкости Π»ΠΈΠΌΠΈΡ‚ΠΎΠ² (429, асиммСтрия player/history vs game-move-history, pageSize) β€” 06 Π—Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ ΠΈ Cloudflare.


4. Π”Π΅ΠΌΠΎΠ½ crawl-service

Один процСсс, ΠΎΠ΄ΠΈΠ½ RateLimiter (Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ single-flight). На стартС: resetInProgress (Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ ΠΏΠΎΠ΄Π²ΠΈΡΡˆΠΈΡ… in_progress β†’ pending), посСв SEED_IDS. Π¦ΠΈΠΊΠ» ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π° while (!stop):

flowchart TD
    A["crawlFrontier<br/>(≀ BATCH_PLAYERS enumerate,<br/>≀ BATCH_FETCH fetch, Π·Π°Ρ‚Π΅ΠΌ drain post)"] --> B["Π»ΠΎΠ³: players/games/fetch/post/<br/>frontier/cooldown"]
    B --> C["archive push (Ссли ARCHIVE_DB_URL)<br/>НЕ Ρ„Π°Ρ‚Π°Π»ΡŒΠ½ΠΎ β€” Π½Π΅ роняСт Ρ†ΠΈΠΊΠ»"]
    C --> D{"Π±Ρ‹Π» прогрСсс?"}
    D -->|"Π΄Π°"| E["nap INTER_PASS_MS β†’ ΠΏΡ€ΠΎΡ…ΠΎΠ΄ Π·Π°Π½ΠΎΠ²ΠΎ"]
    D -->|"Π½Π΅Ρ‚"| F{"RESYNC_AFTER_MS > 0?"}
    F -->|"Π΄Π°, Π΅ΡΡ‚ΡŒ ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠ΅"| G["requeueStale β†’ ΠΏΡ€ΠΎΡ…ΠΎΠ΄ Π·Π°Π½ΠΎΠ²ΠΎ"]
    F -->|"Π½Π΅Ρ‚"| H["idle: nap IDLE_MS"]
    E --> A
    G --> A
    H --> A

SIGTERM/SIGINT ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°ΡŽΡ‚ сон ΠΈ ΠΎΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°ΡŽΡ‚ Ρ†ΠΈΠΊΠ» Π°ΠΊΠΊΡƒΡ€Π°Ρ‚Π½ΠΎ; Π² finally Π·Π°ΠΊΡ€Ρ‹Π²Π°ΡŽΡ‚ΡΡ Π°Ρ€Ρ…ΠΈΠ² ΠΈ Π‘Π”. Архив-ΠΏΡƒΡˆ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π° ΠΈΠ΄Π΅ΠΌΠΏΠΎΡ‚Π΅Π½Ρ‚Π΅Π½ ΠΈ ΠΎΠ±Ρ‘Ρ€Π½ΡƒΡ‚ Π² try/catch β€” Π½Π΅Π΄ΠΎΡΡ‚ΡƒΠΏΠ½ΠΎΡΡ‚ΡŒ dexus Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ Π²Π°Π»ΠΈΡ‚ ΠΊΡ€Π°ΡƒΠ»ΠΈΠ½Π³ (ΡΡ‹Ρ€ΡŒΡ‘ остаётся Π² локальном кэшС). Полная стадия Π°Ρ€Ρ…ΠΈΠ²Π°Ρ†ΠΈΠΈ β€” 05 ΠšΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€ ΠΏΠ°Ρ€Ρ‚ΠΈΠΉ ΠΈ состояния.


5. Π”Π΅ΠΏΠ»ΠΎΠΉ

  • ΠžΠ±Ρ€Π°Π· ghcr.io/rabestro/dicechess-sync:latest (+ :vX.Y.Z), ΠΌΡƒΠ»ΡŒΡ‚ΠΈ-Π°Ρ€Ρ‡ (Π² Ρ‚.Ρ‡. arm64 ΠΏΠΎΠ΄ Raspberry Pi).
  • docker-compose: restart: unless-stopped, Ρ‚ΠΎΠΌ sync_data:/app/data (Π΄ΠΎΠ»Π³ΠΎΠ²Π΅Ρ‡Π½ΠΎΠ΅ состояниС), env_file: .env.
  • ΠšΡ€ΡƒΡ‚ΠΈΡ‚ΡΡ Π½Π° rpi4 (residential IP β€” обязатСлСн для прохоТдСния Cloudflare; с Π΄Π°Ρ‚Π°-Ρ†Π΅Π½Ρ‚Ρ€ΠΎΠ²ΠΎΠ³ΠΎ/Π½ΠΎΡƒΡ‚Π±ΡƒΡ‡Π½ΠΎΠ³ΠΎ IP curl Π»ΠΎΠ²ΠΈΡ‚ 403).

6. Π Π΅Π·ΡŽΠΌΠΈΡ€ΡƒΠ΅ΠΌΠΎΡΡ‚ΡŒ ΠΈ ΠΈΠ΄Π΅ΠΌΠΏΠΎΡ‚Π΅Π½Ρ‚Π½ΠΎΡΡ‚ΡŒ (ΠΊΡ€Π°Ρ‚ΠΊΠΎ)

Π’Π΅ΡΡŒ прогрСсс β€” Π² SQLite, любой энтрипоинт ΠΈ Π΄Π΅ΠΌΠΎΠ½ рСстартятся свободно: resetInProgress Ρ‡ΠΈΠ½ΠΈΡ‚ подвисшСС, enumerate_offset Ρ€Π΅Π·ΡŽΠΌΠΈΡ€ΡƒΠ΅Ρ‚ пСрСчислСниС ΠΊΠΈΡ‚Π°, synced_through_ms ΠΏΡ€Π΅Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½Ρ‹ΠΉ запуск Π² Π΄Π΅ΡˆΡ‘Π²Ρ‹ΠΉ ΠΈΠ½ΠΊΡ€Π΅ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ свип. Π˜Π΄Π΅ΠΌΠΏΠΎΡ‚Π΅Π½Ρ‚Π½ΠΎΡΡ‚ΡŒ: ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³ INSERT OR IGNORE (партия ΠΈΠ· историй ΠΎΠ±ΠΎΠΈΡ… ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² заводится ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·), ingest 201/200, Π°Ρ€Ρ…ΠΈΠ² first-writer-wins + archived_at. Π Π΅ΠΏΠ»Π΅ΠΉ ΠΈΠ· ΡΡ‹Ρ€ΡŒΡ (replayRejected / replayNormalizeFailures) ΠΏΠ΅Ρ€Π΅ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ Π±Π΅Π· обращСния ΠΊ сайту. Π”Π΅Ρ‚Π°Π»ΠΈ β€” 05 ΠšΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€ ΠΏΠ°Ρ€Ρ‚ΠΈΠΉ ΠΈ состояния.


БвязанноС: 03 Бинхронизация β€” ΠΎΠ±Π·ΠΎΡ€ ΠΈ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π°, Raw-Π°Ρ€Ρ…ΠΈΠ² β€” bronze-слой, АпгрСйд aurora β€” ΠΏΠ»Π°Π½ ΠΈ Ρ‡Π΅ΠΊ-лист, SSH-Ρ‚ΡƒΠ½Π½Π΅Π»ΡŒ ΠΊ сСрвСрному Postgres.