Vault защищённое хранилище резервных копий

Чанковая передача

Объект передаётся частями в рамках сессии. Сессия переживает разрыв связи, части можно лить параллельно и в любом порядке, а повторная отправка одной и той же части безопасна.

#Схема

Весь обмен происходит на префиксе /v1/chunks. Четыре обязательных шага и две ветки на случай, когда что-то пошло не так.

  1. POST Открытие

    Сервис выдаёт uploadId и размер части

  2. PUT ×N Части

    Параллельно, в любом порядке, каждая — со своим etag

  3. POST Завершение

    Сверка списка и сборка версии объекта

  4. GET / DELETE Докачка и отмена

    Ветки на случай обрыва или ненужной сессии

#Открытие сессии

Сессия создаётся один раз на объект. В теле передаётся ключ объекта, полный размер в байтах и, если нужно, регион — иначе берётся регион по умолчанию из токена.

POST/v1/chunks201 Created
curl -X POST https://vault.goida.fun/v1/chunks \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb" \
  -H "X-Vault-Space: sp_3f9a21" \
  -H "Content-Type: application/json" \
  -d '{"key":"pg/main.dump","size":45742891008,"region":"ru"}'

{
  "uploadId": "up_5KpR2n8vQz",
  "key": "pg/main.dump",
  "region": "ru",
  "partSize": 8388608,
  "parts": 5451,
  "expiresAt": "2026-07-31T22:14:07Z"
}

partSize — рекомендация, а не требование: сервис подбирает её по размеру объекта и региону. Свой размер задавать можно, но он должен попадать в диапазон из раздела лимитов, иначе первая же часть вернёт E1004.

Сессия живёт 24 часа с момента открытия. Это окно на докачку, а не на передачу: если агент упал и вернулся через сутки, сессия уже закрыта и нужна новая.

#Отправка частей

Номера частей начинаются с единицы. Все части, кроме последней, должны быть одного размера — того, что вернулся при открытии сессии. Последняя может быть меньше.

PUT/v1/chunks/up_5KpR2n8vQz/1200 OK
curl -X PUT https://vault.goida.fun/v1/chunks/up_5KpR2n8vQz/1 \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb" \
  -H "Content-Type: application/octet-stream" \
  -H "Content-Length: 8388608" \
  --data-binary @part-00001.enc

{
  "partNumber": 1,
  "etag": "b71c4e9a02f8",
  "size": 8388608,
  "receivedAt": "2026-07-30T22:15:31Z"
}

Тело части — шифротекст, сервис его не разбирает. Заголовок Content-Length обязателен: без него запрос отклоняется с 411, потому что сервис резервирует место в квоте до начала приёма.

Части независимы, поэтому агент льёт их параллельно. Разумный потолок — 12 одновременных потоков на агента; выше начинается конкуренция за канал, и суммарная скорость падает, а не растёт.

Повторная отправка безопасна. Если часть с таким номером уже принята и её содержимое совпадает, сервис вернёт тот же etag и код 200, ничего не перезаписывая. Если содержимое отличается — 409 и код E1011: значит, агент собрал часть из других данных, и продолжать эту сессию нельзя.

#Завершение

Завершение — это заявление агента о том, из каких именно частей состоит объект. Сервис сверяет присланный список с тем, что принял, и расходится с ним не молча.

POST/v1/chunks/up_5KpR2n8vQz/complete200 OK
curl -X POST https://vault.goida.fun/v1/chunks/up_5KpR2n8vQz/complete \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb" \
  -H "Content-Type: application/json" \
  -d '{"parts":[{"partNumber":1,"etag":"b71c4e9a02f8"},
               {"partNumber":2,"etag":"3d0a8815ce64"}]}'

{
  "key": "pg/main.dump",
  "version": "v_2026073022_41",
  "size": 45742891008,
  "parts": 5451,
  "region": "ru",
  "copies": 3,
  "completedAt": "2026-07-30T23:02:44Z"
}

После завершения версия объекта неизменяема. Новая копия того же ключа создаёт новую версию, старая остаётся доступной по своему идентификатору, пока её не удалит политика хранения пространства.

Поле copies — сколько копий части уже разложено по дисковым узлам региона. Целевое значение три; сразу после завершения там может стоять меньше, недостающие досоздаются в фоне за минуты. Подробнее — в разделе Надёжность хранения.

#Докачка после разрыва

Обрыв связи не требует начинать заново. Запрос состояния сессии возвращает список уже принятых частей — агенту остаётся долить недостающие.

GET/v1/chunks/up_5KpR2n8vQz200 OK
curl https://vault.goida.fun/v1/chunks/up_5KpR2n8vQz \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb"

{
  "uploadId": "up_5KpR2n8vQz",
  "key": "pg/main.dump",
  "partSize": 8388608,
  "parts": 5451,
  "received": 4188,
  "missing": [4189, 4190, 4191],
  "expiresAt": "2026-07-31T22:14:07Z"
}

Список missing усечён до тысячи номеров — на объектах в десятки тысяч частей полный список сам по себе становится тяжёлым ответом. Если частей не хватает больше, запрашивайте состояние повторно по мере долива.

#Отмена

Ненужную сессию лучше закрыть явно: принятые части сразу освобождают место в квоте, не дожидаясь истечения суток.

DELETE/v1/chunks/up_5KpR2n8vQz204 No Content
curl -X DELETE https://vault.goida.fun/v1/chunks/up_5KpR2n8vQz \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb"

Отмена необратима: восстановить закрытую сессию нельзя, части удаляются. Уже завершённые версии объекта отмена не трогает.

#Выгрузка и Range

Восстановление — обратная операция. Объект отдаётся потоком, с поддержкой докачки через Range: восстановление на сотни гигабайт нередко прерывается, и перекачивать всё заново никто не хочет.

GET/v1/objects/pg/main.dump206 Partial Content
curl https://vault.goida.fun/v1/objects/pg/main.dump \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb" \
  -H "X-Vault-Version: v_2026073022_41" \
  -H "Range: bytes=8388608-16777215" \
  -o part.enc

HTTP/2 206
Accept-Ranges: bytes
Content-Range: bytes 8388608-16777215/45742891008
Content-Length: 8388608
X-Vault-Version: v_2026073022_41

Без заголовка X-Vault-Version отдаётся последняя версия ключа. Диапазон за пределами объекта возвращает 416 с кодом E2015.

Выгрузка идёт из того региона, где объект был принят, — в другом регионе его просто нет. Если регион недоступен целиком, ждать придётся его возвращения: копий за пределами региона мы не держим, см. Надёжность хранения.

#Известные ограничения

  • Докачка после истечения суток невозможна: uploadId закрывается вместе с сессией, нужна новая. Мы несколько раз обсуждали продление окна, но длинные сессии держат место в квоте и мешают тем, кто укладывается в норму.
  • Список missing усечён до 1000 номеров.
  • Смена региона у открытой сессии не поддерживается — только через отмену и повторное открытие.
  • Параллельная запись в один ключ из двух агентов создаст две независимые версии. Это не ошибка, но обычно означает, что расписания задвоились.