Document Orleans hosting and enable Actions for the repo
CI / test (push) Failing after 3s Details

This commit is contained in:
MpakobecEx 2026-10-21 16:03:09 +05:00
parent 6c0a6be872
commit 4888983816
3 changed files with 32 additions and 18 deletions

View File

@ -18,7 +18,7 @@
| Мета-API: JWT, коллекция, колоды, магазин, история, реплеи | готово |
| CI (Forgejo Actions) | workflow добавлен, runner нужно привязать |
| EF-миграции | готово (`InitialCreate`) |
| Orleans | не начато |
| Orleans | готово: silo + `MatchActorGrain`, включается `Orleans:Enabled` |
| Клиент и лаунчер | не начато |
## Требования

View File

@ -12,7 +12,7 @@ MVP: бустер **BT01** + триал-деки **TD01/TD02** — 94 карты
Стек:
- сервер — **.NET 10**, планируются **Orleans**, PostgreSQL, Redis;
- сервер — **.NET 10**, **Orleans** (опционально), PostgreSQL, Redis;
- клиент — **Unity 6** (ещё не начат);
- лаунчер — **WPF** (ещё не начат);
- контент — YAML + C#-скрипты карт;
@ -38,7 +38,7 @@ MVP: бустер **BT01** + триал-деки **TD01/TD02** — 94 карты
| Проверка | Результат |
|---|---|
| Тесты | **173/173** (131 правила/контракты + 42 сервер) |
| Тесты | **174/174** (131 правила/контракты + 43 сервер) |
| `validate` | 0 ошибок, 0 предупреждений |
| `translations check` | 94/94 |
| `stress 1000` | 1000/1000 партий, 0 ошибок |
@ -71,7 +71,7 @@ dotnet run --project server\src\Vanguard.Cli -- stress 1000
```
Пройден! : не пройдено 0, пройдено 131 (Vanguard.Rules.Tests)
Пройден! : не пройдено 0, пройдено 42 (Vanguard.Server.Tests)
Пройден! : не пройдено 0, пройдено 43 (Vanguard.Server.Tests)
OK: ошибок 0, предупреждений 0
Переведено: 94/94
Партий: 1000, завершено: 1000, ошибок: 0
@ -231,7 +231,9 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
инстансами), иначе — из памяти.
- **`IMatchGrain`**: 1 матч = 1 экземпляр, все входы (интенты, подключения,
тики) проходят через один семафор; внутри нет ни ASP.NET Core, ни WebSocket —
только `IMatchConnection`. Orleans-хостинг подключается поверх контракта.
только `IMatchConnection`. Orleans-хост (`MatchActorGrain`) подключается
поверх того же контракта; соединения живут в `IMatchConnectionRegistry`,
чтобы состояние грана оставалось сериализуемым.
- **WebSocket** `/game`: handshake, MessagePack, `intentId`, idempotency,
`eventSeq`, `resync`, heartbeat (20 с простоя → ping, ещё 20 с без ответа
→ `4408`), реконнект, захват сессии (`4409`), rate limit 16 интентов/с.
@ -254,6 +256,12 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
- **Конфигурация**: `Storage:Provider` = `None` / `Memory` / `Postgres`,
`Storage:ConnectionString`; `Redis:Configuration`. `Memory` — EF InMemory
для тестов, `Postgres` — постоянное хранилище.
- **Orleans**: `Orleans:Enabled` (`false` по умолчанию, `true` в compose).
Включённый режим поднимает silo с localhost-clustering в том же процессе;
`MatchActorGrain` держит доменный `MatchGrain`, `IMatchConnectionRegistry`
остаётся в процессе. Граны `[KeepAlive]`: WebSocket нельзя потерять при
выгрузке. Один silo на инстанс; для нескольких реплик нужен настоящий
кластер и внешний реестр соединений. Тест кластера — `OrleansMatchTests`.
- **Docker**: `Dockerfile` + `docker-compose.yml` поднимают postgres, redis
(AOF) и сервер; `/health` на `http://localhost:8080` (порт меняется
через `VANGUARD_PORT`, адрес WebSocket — `VANGUARD_WS_URL`).
@ -261,7 +269,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
`translations check` на push/PR. Workflow добавлен, но home-runner
зарегистрирован на другой репозиторий: для этого репозитория нужно
включить Actions и привязать runner.
- **Тесты серверного ядра** (33 из 42 в `Vanguard.Server.Tests`):
- **Тесты серверного ядра** (34 из 43 в `Vanguard.Server.Tests`):
- тикеты: одноразовость и TTL;
- heartbeat: ping после 20 с простоя, закрытие через 20 с, сброс любым кадром;
- grain: attach, чужое место, duplicate intent, чужой ход, сдача,
@ -276,9 +284,11 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
`VANGUARD_TEST_POSTGRES` / `VANGUARD_TEST_REDIS`): реальные PostgreSQL
и Redis, изъятие тикета, очередь;
- E2E: два клиента играют полный матч по WebSocket и сходятся в победителе;
обрыв и возврат по новому тикету с догоном состояния.
обрыв и возврат по новому тикету с догоном состояния;
- Orleans: грана инициализируется, принимает attach и интент, граница
Orleans сериализуется (тестовый `TestCluster`).
Проверено на `ssh home`: 173/173 тестов, включая реальные PostgreSQL и Redis;
Проверено на `ssh home`: 131 + 43 теста, включая реальные PostgreSQL и Redis;
`docker compose up` поднимает стенд, матч создаётся через HTTP-очередь,
а после `docker compose restart server` восстанавливается (`Recovery: 1 of 1`).
@ -322,7 +332,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
11 стартовых событий и кадр, рука противника пуста. После рестарта
сервера матч восстановлен (`matches: 1`).
Счётчик `Vanguard.Server.Tests` вырос до 42: добавлены auth, мета-профиль,
Счётчик `Vanguard.Server.Tests` вырос до 43: добавлены auth, мета-профиль,
мета-E2E (онбординг → бустер → колода → матч → история → реплей),
проверки реплея, явного выхода и истории/реплея после выхода.
@ -364,7 +374,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
/Profiles/ интерфейс MMR
/Dockerfile
/server/tests/Vanguard.Rules.Tests 131 тест
/server/tests/Vanguard.Server.Tests 42 теста, включая E2E и внешние хранилища
/server/tests/Vanguard.Server.Tests 43 теста, включая E2E и внешние хранилища
/content/sets карты
/content/trial-decks.yaml две триал-деки
/content/pack_odds.yaml веса бустера
@ -402,7 +412,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
в таком виде нельзя.
- Состояние сервера: PostgreSQL — снапшот/журнал матчей, Redis — тикеты
и очередь; без конфигурации работает in-memory. `IMatchGrain` отделён
от транспорта, Orleans подключается поверх.
от транспорта, Orleans подключается поверх и включается конфигом.
- Rate limit — 16 интентов в секунду на соединение.
- Скрытый MMR: старт 1000, Эло K = 32, casual рейтинг не меняет.
- Аккаунты: BCrypt, access JWT 15 минут, refresh 30 дней с ротацией;
@ -445,7 +455,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
своя рука и свой выбор полностью, у противника только количества
и открытые зоны, seed и RNG не уходят.
5. **[x] `Docker Compose`** — postgres, redis (AOF) и сервер; `/health`,
порт через `VANGUARD_PORT`.
порт через `VANGUARD_PORT`. Сервер запускается с `Orleans:Enabled=true`.
6. **[x] Персистентность**:
- PostgreSQL — заголовок матча + журнал интентов, восстановление повтором
команд после рестарта, `MatchRecoveryService`;
@ -456,14 +466,17 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
7. **Мета-API** — сделано: JWT, аккаунты, коллекция, колоды, магазин,
экономика, онбординг, матчмейкинг по `deckId`, история и реплеи.
`playerId` остался только как dev-режим при `Game:AllowDevQueue=true`.
8. **Orleans** — заменить in-process регистр на настоящий silo и граны.
`IMatchGrain` уже отделён от транспорта и ASP.NET Core; персистентность
и проекция от хостинга не зависят.
8. **[x] Orleans** — `MatchActorGrain` + `OrleansMatchAdapter`; silo
включается `Orleans:Enabled=true`, в compose включён. `MatchGrain` остался
доменным ядром, соединения вынесены в `IMatchConnectionRegistry`,
`MatchActorGrain` восстанавливается из снапшота на активации. Покрыто
`OrleansMatchTests` (тестовый кластер).
Критерий готовности серверного блока выполнен: два клиента играют полный
матч по сети; kill клиента восстанавливается через новый тикет и
`resumeFromSeq`; рестарт сервера восстанавливает матчи из PostgreSQL.
Остались Orleans и привязка CI-runner.
Остался только CI-runner: workflow есть, но home-runner привязан к другому
репозиторию.
### 7.2. Мета-API
@ -520,7 +533,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
`GameConnection`, логин, коллекция, декбилдер, магазин, очередь,
история и реплей. Сервер и мета-API готовы: `docs/protocol.md`,
`docs/server-api.md`, `Vanguard.Shared`.
4. Orleans и CI-runner — отдельные серверные долги, не блокируют клиент.
4. CI-runner — отдельный серверный долг, не блокирует клиент; Orleans уже готов.
5. После каждого блока: тесты, `validate`, `translations check`, коммит, push.
6. Не ломать `Vanguard.Rules`: сервер должен зависеть от него, а не наоборот.

View File

@ -424,7 +424,8 @@ Redis: тикеты (`GETDEL`, TTL 60 с) и очередь (JSON + sorted set).
История матча пишется, но реплей при `None` недоступен: строки матча нет.
Для истории и реплеев нужен `Memory` или `Postgres`.
- `EnsureCreated` используется только для InMemory; PostgreSQL — миграции.
- Один процесс: Orleans-хостинг ещё впереди; `IMatchGrain` уже отделён.
- Один процесс: Orleans включается `Orleans:Enabled`; по умолчанию матчи
идут через in-process `MatchGrain`, в compose — через silo.
- Нет магазина косметики, квестов, баттлпасса, Draft, друзей и чата.
- Rate limit: 60/мин на auth, 300/мин на остальные мутирующие запросы.
- Dev-очередь включается только `Game:AllowDevQueue=true` и не для production.