Вернуть crash-safe запись AddBlock через tmp_bch

This commit is contained in:
AidarKC
2026-06-26 17:05:37 +04:00
parent 1ced351ea2
commit 71fdee0cfd
10 changed files with 468 additions and 216 deletions
+8
View File
@@ -1,5 +1,13 @@
# История изменений документации блокчейна
## 2026-06-26 17:03:22 +0400
- Базовый коммит-ориентир: `TBD`.
- Обычный `AddBlock` переведён на crash-safe схему через временный кандидат `<blockchainName>.tmp_bch`, sidecar `<blockchainName>.write_check` и marker `<blockchainName>.write_pending`.
- `BlockchainTmpRecoveryOnStartup` теперь разбирает marker-driven recovery для обычной записи блока:
- если marker есть, recovery либо завершает swap tmp -> main, либо удаляет мусор;
- если marker нет, временные артефакты считаются мусором и удаляются.
- В `Dev_Docs/Blockchain/sync-between-servers.md` добавлено описание обычного `AddBlock` recovery и разделение между `write_pending` и `resync_pending`.
## 2026-05-24 11:40:00 +0300
- Базовый коммит-ориентир: `abdce05`.
- `TEXT_REPOST (subType=30)` оставлен как зарезервированный формат, но новые блоки репоста временно отключены на уровне `AddBlock`.
+1
View File
@@ -30,4 +30,5 @@
## Обязательное сопровождение
- При любом изменении формата/правил блокчейна в коде документы этого каталога обновляются в том же наборе изменений.
- Обычный `AddBlock` сейчас пишет через `<blockchainName>.tmp_bch`, `<blockchainName>.write_check` и `<blockchainName>.write_pending`; эта схема и `BlockchainTmpRecoveryOnStartup` должны быть описаны в актуальной документации по синхронизации и recovery.
- Каждое обновление документов фиксируется в `CHANGELOG.md` с датой/временем и хэшем коммита-основания.
+22 -5
View File
@@ -113,11 +113,28 @@ Full resync запускается только тогда, когда:
- full resync не трогает DM-таблицы и `solana_users`;
- висячие cross-chain ссылки считаются допустимым поведением системы.
### 4.5 Startup recovery по marker-file
### 4.5 Как работает обычный `AddBlock` и его recovery
Обычная запись блока теперь тоже идёт через временные артефакты:
1. собирается `<blockchainName>.tmp_bch` как полный кандидат на замену основного файла;
2. пишется маленький sidecar `<blockchainName>.write_check` с `blockNumber` и `blockHash`;
3. только после этого создаётся пустой marker `<blockchainName>.write_pending`;
4. выполняется SQL-транзакция;
5. после `commit` tmp атомарно ставится на место основного `.bch`;
6. marker и sidecar удаляются.
На старте `BlockchainTmpRecoveryOnStartup` смотрит именно на эту пару:
- если `write_pending` есть, recovery проверяет sidecar и БД, а затем либо завершает swap, либо чистит временные файлы;
- если `write_pending` нет, а `tmp_bch` или `write_check` остались, это мусор и он удаляется;
- `resync_pending` сюда не относится, это отдельный recovery-поток.
### 4.6 Startup recovery по marker-file
При старте сервер идёт в таком порядке:
1. `BlockchainTmpRecoveryOnStartup` для `*.tmp_bch`;
1. `BlockchainTmpRecoveryOnStartup` для `*.write_pending` и orphan `*.tmp_bch` / `*.write_check`;
2. `BlockchainResyncRecoveryOnStartup` для `*.resync_pending`;
3. только потом поднимается обычный сервер и запускается `PeriodicBlockchainSyncService`.
@@ -127,7 +144,7 @@ Full resync запускается только тогда, когда:
- recovery снова выполняет cleanup и replay с нуля;
- если recovery не завершился, marker остаётся, и сервер не переходит к обычному режиму для этой chain.
### 4.6 Зачем понадобился `GetSyncUserProfile`
### 4.7 Зачем понадобился `GetSyncUserProfile`
Изначально подготовка локальной цепочки делалась через Solana:
@@ -145,7 +162,7 @@ Full resync запускается только тогда, когда:
Это временная практическая заплатка, чтобы clean-start sync не зависел от rate limit внешнего Solana endpoint.
### 4.7 Что делает настройка `sync.importUserProfileFromPartner.enabled`
### 4.8 Что делает настройка `sync.importUserProfileFromPartner.enabled`
- `false` — стандартный режим, подготовка локального пользователя идёт через Solana PDA;
- `true` — sync-режим обхода Solana, локальный пользователь создаётся по server-to-server `GetSyncUserProfile`.
@@ -209,6 +226,7 @@ Full resync запускается только тогда, когда:
| Публичный `GetSyncUserProfile` | ✅ Реализовано |
| Плановый blockchain sync при старте + каждые 12 часов | ✅ Реализовано |
| Обход Solana RPC через `sync.importUserProfileFromPartner.enabled` | ✅ Реализовано |
| Обычный `AddBlock` через `tmp_bch`/`write_check`/`write_pending` | ✅ Реализовано |
| Межсерверный постоянный WebSocket-канал | Нужна реализация |
| Push новых DM партнёрам | Нужна реализация |
| Push блоков блокчейна партнёрам | ✅ Реализована базовая one-shot версия |
@@ -221,5 +239,4 @@ Full resync запускается только тогда, когда:
Не реализованы ещё DM-sync и постоянные server-to-server соединения.
Следующие отдельные шаги после текущего этапа:
- вернуть обычному `AddBlock` настоящую `tmp_bch`-схему записи и recovery при резком рестарте.
- отдельно проверить full-resync и startup-recovery на реальном тестовом прогоне после ручного удаления БД/файлов.
-1
View File
@@ -30,7 +30,6 @@
- `near/2026-05-25_1106_telegram_agent_players.md` - разрешённые пользователи Telegram для агента, отдельные папки игроков, персональные истории и публикация краткого вопроса/ответа в общий канал.
- `near/2026-05-25_1106_wallet_topup_solana_arweave.md` - пополнение Solana и Arweave через внешний сервис покупки с подсказкой и копированием адреса.
- `near/2026-06-26_1215_tmp_bch_для_обычного_addblock.md` - вернуть обычному `AddBlock` настоящую crash-safe схему через `.tmp_bch` и привести writer к модели startup-recovery.
### Среднесрочные
@@ -1,45 +0,0 @@
# Вернуть настоящую tmp_bch-схему для обычного AddBlock
## Зачем нужна эта доработка
Сейчас обычный `AddBlock` пишет данные так:
1. в SQL-транзакции добавляет блок в БД и обновляет `blockchain_state`;
2. делает `commit`;
3. только после этого дописывает основной файл `<blockchainName>.bch`.
Это рабочая схема, но она не идеально crash-safe. Если сервер резко упадёт между `commit` БД и записью файла, можно получить состояние:
- в БД новый блок уже есть;
- в `.bch` файла этого блока ещё нет.
При этом в проекте уже есть логика startup-recovery через `*.tmp_bch`, но текущий `BlockchainWriter` её полноценно не использует. Из-за этого writer и recovery сейчас живут в разных моделях.
## Что планируем сделать
Нужно вернуть единую и понятную схему:
1. обычный `AddBlock` работает через временный файл `<blockchainName>.tmp_bch`;
2. после успешной подготовки нового содержимого выполняется атомарная подмена `tmp -> main`;
3. `BlockchainTmpRecoveryOnStartup` и `BlockchainWriter` используют одну и ту же модель;
4. при резкой перезагрузке сервер на старте может корректно добрать или откатить незавершённый файловый шаг.
## Что уже есть в коде
- recovery-класс: `SHiNE-server/src/main/java/server/ws/BlockchainTmpRecoveryOnStartup.java`
- файловые helper-методы под `tmp_bch`: `SHiNE-server/shine-server-blockchain/src/main/java/utils/files/FileStoreUtil.java`
- текущий writer: `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler_utils/BlockchainWriter.java`
## Что надо будет изменить
1. Переписать `BlockchainWriter`, чтобы он больше не писал сразу в основной `.bch`.
2. Привести порядок записи и замены файла в соответствие с `BlockchainTmpRecoveryOnStartup`.
3. Проверить сценарии:
- обычный `AddBlock`;
- резкий restart до commit БД;
- резкий restart после commit БД, но до замены файла;
- recovery на старте при наличии `*.tmp_bch`.
## Почему это отложено отдельно
Это отдельная задача от server-to-server resync. Для divergence-resync сейчас важнее сначала закончить безопасный SQL cleanup одной цепочки, а усиление crash-safety обычного `AddBlock` делать следующим самостоятельным шагом.
@@ -0,0 +1,47 @@
# Crash-safe запись обычного `AddBlock` через `tmp_bch`
## Кратко
Обычный `AddBlock` переведён на схему:
1. сборка `<blockchainName>.tmp_bch`;
2. запись sidecar `<blockchainName>.write_check` с `blockNumber` и `blockHash`;
3. создание пустого marker `<blockchainName>.write_pending`;
4. SQL-транзакция;
5. атомарная подмена `tmp -> main`;
6. удаление временных файлов.
## Что проверить
1. Обычный `AddBlock` на свежей цепочке.
2. Падение до SQL-commit:
- должны остаться только временные файлы;
- на старте они должны быть удалены.
3. Падение после SQL-commit, но до `atomicReplaceBlockchainFile(...)`:
- на старте recovery должен довести swap до конца.
4. Падение после `atomicReplaceBlockchainFile(...)`, но до удаления marker/sidecar:
- на старте recovery должен просто подчистить хвост.
5. Сценарий без marker:
- `tmp_bch` / `write_check` считаются мусором и удаляются.
## Ожидаемый результат
- БД и файловая версия цепочки остаются согласованными.
- Повторный старт сервера не ломает chain и не требует ручной правки файлов.
- `BlockchainTmpRecoveryOnStartup` корректно обрабатывает и живые остатки, и мусор.
## Статус
`pending`
## Что уже сделано
- В коде есть `tmp_bch`, `write_check` и `write_pending`.
- `BlockchainWriter` пишет обычный `AddBlock` через временные артефакты.
- `BlockchainTmpRecoveryOnStartup` умеет добивать или чистить незавершённую запись.
## Что ещё перепроверить
- ручной crash-test на тестовом сервере;
- совместимость с уже существующими `resync_pending` marker-файлами;
- отсутствие ложных срабатываний на старых временных файлах.