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

События и синхронизация

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

#Зачем long-poll

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

Мы сознательно не стали брать сюда веб-сокеты: канал односторонний и редкий, а держать его через корпоративные шлюзы и межсетевые экраны обычным HTTPS-запросом гораздо спокойнее. Обычный GET, который долго не отвечает, проходит везде.

#Канал v3

Агент отправляет запрос и ждёт. Сервер отвечает сразу, если события уже накопились, иначе удерживает соединение до истечения wait и возвращает пустой список — это не ошибка, а нормальное завершение цикла.

GET/api/v3/sync200 OK
curl -N "https://vault.goida.fun/api/v3/sync?cursor=ev_918447&wait=1800" \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb" \
  -H "X-Vault-Agent: db-01"

{
  "cursor": "ev_918452",
  "events": [
    {
      "id": "ev_918449",
      "type": "job.scheduled",
      "at": "2026-07-30T21:00:03Z",
      "job": 4127,
      "key": "pg/main.dump",
      "region": "ru"
    },
    {
      "id": "ev_918452",
      "type": "retention.changed",
      "at": "2026-07-30T21:04:18Z",
      "keep": 30
    }
  ]
}

Параметр wait задаётся в секундах, максимум — 3600. Значение по умолчанию 900: оно проходит через большинство промежуточных шлюзов, которые рвут молчащее соединение на пятнадцатой минуте.

#Курсор и порядок

Каждый ответ несёт cursor — позицию агента в ленте событий. Следующий запрос должен прийти с этим значением, иначе агент получит события заново.

События упорядочены и не теряются: лента хранится 72 часа, поэтому агент, отсутствовавший сутки, получит всё пропущенное по своему курсору. Если курсор старше срока хранения, сервер вернёт 410 с кодом E1203 — это сигнал сделать полную сверку состояния вместо чтения ленты.

Гарантия

Доставка — «как минимум один раз». Одно и то же событие может прийти дважды, если агент упал между получением и сохранением курсора. Обрабатывайте события идемпотентно, ориентируясь на поле id.

#Типы событий

Типы событий канала синхронизации
ТипКогда приходитЧто делает агент
job.scheduledнаступило время копии по расписаниюоткрывает сессию передачи
job.cancelledзадание снято администраторомотменяет сессию, если успел открыть
restore.requestedзапрошено восстановление версииначинает выгрузку с Range
token.revokedтокен агента отозванпрекращает работу, чистит кэш
retention.changedизменена политика храненияобновляет локальную копию политики
region.readonlyрегион переведён в режим чтенияоткладывает копии, восстановление продолжает

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

#Легаси-канал v2

/api/v2/sync работает и будет работать до 1 марта 2027 года. Он нужен агентам версии 1.3 и старше — их всё ещё заметно на контурах, где обновление проходит раз в год по регламенту.

GET/api/v2/syncлегаси
curl -N "https://vault.goida.fun/api/v2/sync?since=2026-07-30T20:00:00Z" \
  -H "Authorization: Bearer vlt_ag_7Q2m4Xd9pKcRt6Yb"

Отличий три, и все они — причины, по которым появился v3:

  • вместо курсора — метка времени since; после разрыва агент переприсылает себе события за сутки и обрабатывает их повторно;
  • потолок удержания соединения — 300 секунд, дальше обязательное переподключение;
  • нет типов region.readonly и retention.changed — старые агенты о них просто не узнают.
План

После 1 марта 2027 канал начнёт отвечать 410. Отдельного «дня отключения» с сюрпризом не будет: за два месяца до даты в ответы v2 добавится заголовок X-Vault-Sunset, и мы напишем администраторам пространств, где такие агенты ещё живы.

#Настройка таймаутов

Если между агентом и сервисом стоит промежуточный шлюз, его таймаут на простаивающее соединение должен быть больше, чем wait. Иначе картина выглядит так: агент ждёт полчаса, шлюз рвёт связь на пятнадцатой минуте, агент считает это ошибкой сети и переподключается — и так по кругу.

wait по умолчанию
900 с
wait максимум
3600 с
удержание в v2
300 с
хранение ленты
72 ч
пауза перед повтором
от 2 до 45 с
каналов на агента
1

Второй одновременный канал на тот же токен получит 409: лента у агента одна, и два читателя с разными курсорами неизбежно разъедутся.