SHA256
Архивация в Arweave
This commit is contained in:
@@ -168,19 +168,78 @@ BigBlock 70
|
||||
|
||||
---
|
||||
|
||||
# 6. Периодичность
|
||||
# 6. Расписание публикации
|
||||
|
||||
Частота архивной публикации конфигурируется отдельно от существующей межсерверной синхронизации.
|
||||
Архивная публикация запускается один раз в сутки в заданное локальное время. Она НЕ использует интервал «каждые N минут».
|
||||
|
||||
Пример:
|
||||
|
||||
```properties
|
||||
archive.publish.enabled=true
|
||||
archive.publish.intervalMinutes=720
|
||||
archive.publish.initialDelayMinutes=15
|
||||
archive.publish.time=00:00
|
||||
archive.publish.zoneId=
|
||||
```
|
||||
|
||||
`720` минут = раз в 12 часов.
|
||||
Для v1.0 значение по умолчанию:
|
||||
|
||||
```text
|
||||
00:00
|
||||
```
|
||||
|
||||
То есть новый snapshot и новый большой архивный блок создаются один раз в сутки в полночь.
|
||||
|
||||
Если `archive.publish.zoneId` пуст, используется системная timezone сервера. При необходимости её можно задать явно, например `Europe/Warsaw`. Это сохраняет публикацию ровно в указанное локальное время даже при переходах летнего/зимнего времени.
|
||||
|
||||
Незавершённый archive job после рестарта не ждёт следующей полуночи: сервер продолжает именно его сразу. Новый snapshot при старте вне назначенного времени не создаётся.
|
||||
|
||||
## 6.1. Первая архивная публикация
|
||||
|
||||
Если у данного archive publisher ещё нет подтверждённых архивных курсоров, первая публикация берёт ВСЁ локально известное состояние:
|
||||
|
||||
```text
|
||||
для каждой blockchain_name:
|
||||
source block 0 .. current local head
|
||||
```
|
||||
|
||||
То есть первый большой архивный блок содержит все SHiNE-блоки, которые сервер успел узнать к моменту первого суточного snapshot. После успешной публикации следующие большие блоки содержат только дельту относительно подтверждённых курсоров.
|
||||
|
||||
## 6.2. Локальная папка и имена файлов
|
||||
|
||||
Перед любой сетевой загрузкой большой блок сначала полностью создаётся на локальном диске. По умолчанию каталог:
|
||||
|
||||
```text
|
||||
data/archive/
|
||||
```
|
||||
|
||||
До получения Arweave TX ID файл имеет временное имя:
|
||||
|
||||
```text
|
||||
<login>.<00001>.<дд.мм.гг>.tmp.SHiNE-archive
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
```text
|
||||
archive01.00001.11.09.26.tmp.SHiNE-archive
|
||||
```
|
||||
|
||||
Дата — реальная дата создания/freeze snapshot большого блока в timezone archive publisher-а. Номер имеет минимальную ширину 5 цифр. Пять цифр — форматирование, а не лимит: блок `100000` получает шестизначный номер.
|
||||
|
||||
После успешной загрузки Arweave возвращает реальный TX ID. Сервер сначала надёжно сохраняет TX ID в БД, затем атомарно переименовывает тот же локальный файл в:
|
||||
|
||||
```text
|
||||
<login>.<00001>.<дд.мм.гг>.<ARWEAVE_TX_ID>.SHiNE-archive
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
```text
|
||||
archive01.00001.11.09.26.Xm32...kP9.SHiNE-archive
|
||||
```
|
||||
|
||||
Поле `<ARWEAVE_TX_ID>` — не слово `trx`, а настоящий Base64URL TX ID загруженного объекта в Arweave. Финальный файл остаётся локально как постоянная копия.
|
||||
|
||||
Crash recovery обязан продолжать работу с этим же файлом. Если TX ID уже сохранён, но процесс упал до rename, при следующем запуске сервер вычисляет финальное имя из сохранённого TX ID и завершает переименование без повторной сборки дельты.
|
||||
|
||||
---
|
||||
|
||||
@@ -380,7 +439,7 @@ Offsets и sizes внутри `SHINE-ARCHIVE v1.0` используют `u32`.
|
||||
archive.maxFileBytes=4000000000
|
||||
```
|
||||
|
||||
Если данных больше, один scheduler-run формирует несколько последовательных больших блоков.
|
||||
Если собранный frozen job превышает этот лимит, v1.0 останавливает публикацию с явной ошибкой `ArchiveTooLargeException` и не двигает курсоры. Практически лимит очень велик; для такого сервера следует уменьшить объём данных между суточными закрытиями или реализовать деление snapshot на несколько big blocks. Автоматическое деление одного snapshot на несколько big blocks оставлено как совместимое будущее расширение.
|
||||
|
||||
---
|
||||
|
||||
@@ -480,15 +539,17 @@ bytes[N] creator_login UTF-8
|
||||
u32
|
||||
```
|
||||
|
||||
Рекомендуемая нумерация:
|
||||
Рекомендуемая нумерация опубликованных больших блоков:
|
||||
|
||||
```text
|
||||
genesis = 0
|
||||
next = 1
|
||||
next = 2
|
||||
first = 1
|
||||
next = 2
|
||||
next = 3
|
||||
...
|
||||
```
|
||||
|
||||
Первый реально публикуемый большой блок имеет номер `1`, поэтому его локальное имя содержит `00001`. Неудачная незавершённая попытка не становится частью опубликованной archive-цепочки.
|
||||
|
||||
---
|
||||
|
||||
# 20. `created_at_ms`
|
||||
@@ -530,9 +591,9 @@ Writer v1.0 MUST использовать `FULL`.
|
||||
BigBlock #365
|
||||
|
||||
References:
|
||||
#0
|
||||
#1
|
||||
#2
|
||||
#3
|
||||
...
|
||||
#364
|
||||
```
|
||||
@@ -595,7 +656,7 @@ u32
|
||||
|
||||
Индекс непосредственного родителя внутри reference table.
|
||||
|
||||
Для genesis:
|
||||
Для первого большого блока (`#1`), у которого нет родителя:
|
||||
|
||||
```text
|
||||
0xFFFFFFFF
|
||||
@@ -1061,7 +1122,7 @@ last_chunk_size = new_chunk_size
|
||||
|
||||
# 54. Arweave service
|
||||
|
||||
Старый `TestFreeAvatarArweaveService` больше не нужен как avatar-specific сервис.
|
||||
Старый `TestFreeAvatarArweaveService` удаляется из активного протокола; archive publisher использует отдельный `ArweaveArchiveService`.
|
||||
|
||||
Его следует переделать/переименовать, например в:
|
||||
|
||||
@@ -1307,8 +1368,9 @@ derivePublic(client_private) == UserPDA.client_key
|
||||
|
||||
```properties
|
||||
archive.publish.enabled=false
|
||||
archive.publish.intervalMinutes=720
|
||||
archive.publish.initialDelayMinutes=15
|
||||
archive.publish.time=00:00
|
||||
archive.publish.zoneId=
|
||||
archive.workDir=data/archive
|
||||
|
||||
archive.maxFileBytes=4000000000
|
||||
|
||||
@@ -1321,6 +1383,9 @@ archive.arweave.confirmTimeoutMinutes=180
|
||||
archive.solana.rootKeyPath=/opt/shine/secrets/root.key
|
||||
archive.solana.clientKeyPath=/opt/shine/secrets/client.key
|
||||
archive.solana.commitment=finalized
|
||||
|
||||
# Отдельного archive.solana.rpcUrl нет.
|
||||
# Используется solana.users.sync.rpcUrl, а если он пуст — обычный solana.rpcUrl.
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1381,54 +1446,55 @@ Lock не удерживается во время Arweave/Solana ожидани
|
||||
|
||||
## Database
|
||||
|
||||
- [ ] `archive_chain_cursor`
|
||||
- [ ] `archive_publish_job`
|
||||
- [ ] `archive_publish_job_chain`
|
||||
- [ ] schema migration
|
||||
- [ ] crash recovery
|
||||
- [x] `archive_chain_cursor`
|
||||
- [x] `archive_publish_job`
|
||||
- [x] `archive_publish_job_chain`
|
||||
- [x] schema migration
|
||||
- [x] crash recovery
|
||||
|
||||
## Archive writer
|
||||
|
||||
- [ ] magic `SHINE-ARCHIVE`
|
||||
- [ ] major/minor version
|
||||
- [ ] fixed header
|
||||
- [ ] creator login
|
||||
- [ ] FULL previous big block table
|
||||
- [ ] one chunk per `blockchain_name`
|
||||
- [ ] many raw records inside one chunk
|
||||
- [ ] one backlink per chunk
|
||||
- [ ] closer login
|
||||
- [ ] SHA-256
|
||||
- [ ] Ed25519 signature
|
||||
- [ ] max file size < 4 GiB
|
||||
- [x] magic `SHINE-ARCHIVE`
|
||||
- [x] major/minor version
|
||||
- [x] fixed header
|
||||
- [x] creator login
|
||||
- [x] FULL previous big block table
|
||||
- [x] one chunk per `blockchain_name`
|
||||
- [x] many raw records inside one chunk
|
||||
- [x] one backlink per chunk
|
||||
- [x] closer login
|
||||
- [x] SHA-256
|
||||
- [x] Ed25519 signature
|
||||
- [x] max file size < 4 GiB
|
||||
- [x] local `.tmp.SHiNE-archive -> .<ArweaveTX>.SHiNE-archive` lifecycle in `data/archive`
|
||||
|
||||
## Arweave
|
||||
|
||||
- [ ] rename/refactor `TestFreeAvatarArweaveService`
|
||||
- [ ] remove avatar-specific logic
|
||||
- [ ] large/chunked upload
|
||||
- [ ] confirmation polling
|
||||
- [x] rename/refactor `TestFreeAvatarArweaveService`
|
||||
- [x] remove avatar-specific logic
|
||||
- [x] large/chunked upload
|
||||
- [x] confirmation polling
|
||||
|
||||
## Solana
|
||||
|
||||
- [ ] add PDA block type `100`
|
||||
- [ ] update Rust codec
|
||||
- [ ] update Java codec
|
||||
- [ ] update JS codec/UI writer
|
||||
- [ ] use ordinary `update_user_pda`
|
||||
- [ ] server-side transaction writer
|
||||
- [ ] root signature
|
||||
- [ ] client fee payer
|
||||
- [ ] wait for `finalized`
|
||||
- [x] add PDA block type `100`
|
||||
- [x] update Rust codec
|
||||
- [x] update Java codec
|
||||
- [x] update JS codec/UI writer
|
||||
- [x] use ordinary `update_user_pda`
|
||||
- [x] server-side transaction writer
|
||||
- [x] root signature
|
||||
- [x] client fee payer
|
||||
- [x] wait for `finalized`
|
||||
|
||||
## Scheduler
|
||||
|
||||
- [ ] `archive.publish.enabled`
|
||||
- [ ] `archive.publish.intervalMinutes`
|
||||
- [ ] `archive.publish.initialDelayMinutes`
|
||||
- [ ] no concurrent jobs
|
||||
- [ ] split files > max size
|
||||
- [ ] skip when no new blocks
|
||||
- [x] `archive.publish.enabled`
|
||||
- [x] `archive.publish.time`
|
||||
- [x] `archive.publish.zoneId`
|
||||
- [x] no concurrent jobs
|
||||
- [ ] future: автоматическое split > max size (v1.0 сейчас безопасно останавливается без cursor commit)
|
||||
- [x] skip when no new blocks
|
||||
|
||||
---
|
||||
|
||||
@@ -1506,10 +1572,10 @@ BigBlock #100
|
||||
| creator = server-A
|
||||
|
|
||||
+-- References
|
||||
| #0 -> BigBlock #0 / hash / TX
|
||||
| #1 -> BigBlock #1 / hash / TX
|
||||
| ref[0] -> BigBlock #1 / hash / TX
|
||||
| ref[1] -> BigBlock #2 / hash / TX
|
||||
| ...
|
||||
| #99 -> BigBlock #99 / hash / TX
|
||||
| ref[98] -> BigBlock #99 / hash / TX
|
||||
|
|
||||
+-- alice-001 chunk
|
||||
| records x4
|
||||
|
||||
Reference in New Issue
Block a user