Реализованное клиентское восстановление при обрывах описано отдельно: TRANSFER_RECOVERY. Оно дополняет сохранение multipart-частей на сервере, но не заменяет резервирование узлов.
Реализованные профили и их границы перечислены в следующем разделе; разделы после него — проектные требования, по которым эти профили построены и проверяются.
Standalone: один узел с backup, без доступности при его потере. HA одной площадки (ADR 0072): весь корень установки (PostgreSQL, содержимое, конфигурация, релизы) на одном синхронно реплицируемом томе, переключение решает Pacemaker с fencing. Внешний NAS, БД или балансировщик, вынесенные из такого тома, остаются единственной точкой отказа. Потерю площадки закрывают зеркала с ручным переходом (ADR 0058, SECOND_SITE) и резервные копии.
Standalone (Windows, Linux, Docker Compose).
- Один сервер: его отказ или обновление — перерыв; клиенты продолжают передачи через resume/retry.
- На Windows Node.js не даёт переносимого fsync каталога (ADR 0004):
LocalBlobStoreсинхронизирует сам файл, но не запись каталога (hard link вblobs/, переименование). Внезапная потеря питания вскоре после публикации может оставить в каталоге БД артефакт без файла; заметит этоscrub, восстановление — из резервной копии или зеркала. Проверяется перезапуск и убийство процесса, а не аппаратная потеря питания. На Linux гарантия зависит от файловой системы, диска и включённыхfsync/synchronous_commitPostgreSQL. - Содержимое и vault — локальные каталоги; S3 не реализован ни для содержимого, ни для vault (ADR 0071 — предложение).
- Несколько томов содержимого (ADR 0083, N11): каждый том — отдельный экземпляр
local-storageс теми же гарантиями записи, что уdefault; тома не дублируют друг друга, потеря тома — потеря его содержимого (восстановление из копии). Том, который не открылся или не прошёл проверку идентичности, не останавливает процесс: его upload отвечают 503unavailable, новые загрузки идут вdefault(метрикаarkvory_storage_placement_fallback_total), очистка его файлов ждёт его возвращения. Идентичность тома проверяется заново перед каждой записью и удалением и после записи, поэтому несмонтированный или подменённый между проверками том не получает данных, а его файлы не считаются удалёнными; каталог тома и его файлstorage-idслужбы не создают: том помечает только команда хостаarkvory volume-init(проверка точки монтирования). Том с данными, удалённый из конфигурации, видно по метрикеarkvory_storage_backend_unconfiguredи оповещению. В кластере каждый том должен лежать на реплицируемой файловой системе каталога данных, иначе API не стартует. Готовность иwritableследуют только заdefault. Опубликованное содержимое переносится между томами командой хостаarkvory storage rehome(ADR 0083, N2): worker копирует blob в цель с проверкой размера и SHA-256, переключает хранилище upload короткой транзакцией под его блокировкой и удаляет копию источника обычным порядком удаления ядра (блокировки upload и содержимого, допуск барьера копии и pins); скачивание, начатое до переключения, и точка копии, закрепившая upload, дочитывают источник, удаление ждёт их и никогда не ждёт само. Каждый шаг повторяем после падения, прогресс записывается с fencing по generation, а переключение и удаление копии проверяют аренду под блокировкой upload: держатель, потерявший аренду, ничего не меняет, копия, из которой upload читается, не удаляется; общей транзакции БД и хранилища нет. Освобождение источника после переключения не завершаетсяfailed, а повторяется; источник только для чтения (эвакуация) или недоступный ждёт без блокировок. Upload, отменённый до переключения, пропускается, и копия в цели удаляется тем же порядком.
HA одной площадки (ADR 0072, эксплуатация — wiki «High availability cluster»).
- Только Linux с нативными пакетами: DRBD 9 (protocol C), Pacemaker + Corosync. Windows и Docker Compose остаются standalone.
- Fencing обязателен:
stonith-enabled=true,fencing resource-and-stonithв DRBD, агент питания или гипервизора для каждого узла с данными; без негоcluster-checkне проходит, а переключение не происходит. SSH-остановка fencing'ом не считается. - Профиль
ha-2(два узла с данными) требует отдельного свидетеля без данных (corosync-qnetdи diskless tiebreaker DRBD) на машине, которая не делит питание и хост с узлами данных: иначе одиночный отказ лишает кворума.ha-3свидетеля не требует. - Стенды CI (
deployment-ha*) проверяют сценарии на гостевых ВМ KVM сfence_virsh. Агент fencing площадки (IPMI, Redfish, PDU, гипервизор) этим не проверен: его доказывает на местеarkvory cluster-fence-testдля каждого узла до ввода в работу и после замены узла. - 2xx на изменение означает минимум две полные копии (RPO = 0 для подтверждённого). При нехватке копий запись получает 503
replication_degraded, чтение продолжается; явное решениеcluster-single-copyна срок снимает это правило, и потеря активного узла в это время теряет записи после решения. - Потеря двух узлов из трёх участников кворума останавливает Arkvory целиком. Автоматического failback нет. Потеря площадки не покрывается.
Зеркала и импорт (ADR 0058, эксплуатация — wiki «Mirrors and a second site»).
- Асинхронный забор по публичному API: RPO при потере источника равен отставанию зеркала (
syncedAt). Переход — ручное отсоединение, без fencing старого источника и без обратной синхронизации. - Не копируются учётные записи, ключи и права, ссылки удержания (references), вложения (attachments), журналы аудита, политики хранения и история путей до первой синхронизации. Зеркало — не резервная копия.
- Засев только добавляет и обновляет: после восстановления источника из копии артефакты, которых на источнике больше нет, на зеркале остаются, а удалённое зеркалом не возвращается.
- Конфликт версии пакета или занятый ID откладывается (
conflicts, метрикаarkvory_mirror_conflicts) и не останавливает ленту; конфликт в записях реестров (образы, Git LFS, npm) и повреждённые байты источника останавливают синхронизацию. - Импорт по стадиям не переносит образы, Git LFS и npm.
Временное содержимое → проверка → устойчивый blob → фиксация доступной версии. Между blob-store и БД нет общей транзакции. Reconciliation восстанавливает промежуточные состояния; cleanup удаляет только проверенные осиротевшие объекты после grace period.
Подтверждённая часть и публикация должны переживать заявленный класс отказа. Local staging на единственном шлюзе не выполняет HA-гарантию. При асинхронной репликации нельзя обещать нулевую потерю подтверждённых записей.
При потере безопасного кворума новые публикации приостанавливаются. Автоматическое снижение долговечности запрещено без явной политики. Failover БД и storage реализуется проверенными инфраструктурными средствами с fencing, а не собственным голосованием приложения.
Реализованная локальная часть: TRAFFIC_CONTROL — общий и per-principal payload budget, bounded admission, отмена и preClose, агрегаты readiness. Фиксированные доли download-бюджета и конечные leases уже реализованы для общего storage: READ_GATEWAYS. Очередь скачиваний с сессиями, классами, оценкой начала и подписанными талонами реализована в памяти процесса записи (ADR 0076, этапы 1–2 и часть 3); у шлюзов чтения очередь своя на процесс, общая очередь в PostgreSQL для шлюзов и HA (этап 4) не реализована. Динамическое перераспределение полосы между процессами остаётся целевым.
Раздельные upload/download/background очереди. PostgreSQL сохраняет задания, попытки, резервы и leases; короткие транзакции захвата, TTL, heartbeat, fencing generation и повторяемые операции. Фактическая передача не удерживает SQL-транзакцию.
Планировщик учитывает полосу площадки/шлюза/клиента, чтение/запись дисков, staging и параллелизм. Справедливость — по принципалу, а не только по IP. Приоритеты имеют aging и минимальную долю ресурса, чтобы миграция или большие файлы не голодали бесконечно.
Очередь ограничена по размеру и времени. Неактивные резервы истекают. При сетевом разделении просроченный lease не должен позволять старому шлюзу работать безлимитно параллельно с новым владельцем.
Streams с backpressure/отменой и ограниченным буфером на поток. Раздача не проходит через внешние приложения. Least connections недостаточно для распределения байт/с; глобальный лимит нельзя заменить per-request лимитом Nginx.
Измеряемый первый алгоритм: допуски по параллелизму и консервативные бюджеты на узел/класс с перераспределением. Upload shaping и суммарные ограничения всех потоков одного клиента проверяются отдельным прототипом. Число шлюзов не увеличивает скорость общего uplink.
При падении узла TCP-поток обрывается; новый запрос может обслужить другой узел, а клиент продолжает через Range/If-Range. Не обещаем незаметный перенос соединения. API поддерживает задачи и resume; клиенты без Range/retry проверяются отдельно.
Ранее выданные ограниченные допуски могут работать в пределах TTL при недоступности API/БД, если это разрешено профилем. Задержка отзыва прав должна быть явно принята. Новым запросам без допуска нельзя выдавать байты в обход ACL.
Код процесса — только релиз, который он запустил: разрешение модулей ограничено каталогом релиза и встроенными модулями Node.js с первой инструкции каждой точки входа. Необязательная зависимость, которой нет в релизе, остаётся отсутствующей, даже если файл с её именем лежит в родительском node_modules, домашнем каталоге или NODE_PATH; переключение релиза при обновлении меняет и разрешённый каталог. ADR 0031, дополнение 2026-10-18.
При недостатке места блокируем новые uploads до повреждения рабочей ёмкости. Резерв — байты (ARKVORY_STORAGE_RESERVE_BYTES) и inode (ARKVORY_STORAGE_RESERVE_INODES, 100 000): каждый blob и каждая часть — файл, и том может кончиться по inode раньше, чем по байтам; ФС без счётчика inode (NTFS) проверяются только по байтам. Каталог данных модуля (ADR 0083, N6) учитывает каждую запись квотой байтов и файлов модуля и тем же резервом тома; превышение отклоняет запись, а не останавливает процесс. Чтение и управление должны сохранять резерв ресурсов. Метрики/аудит не требуют записи в БД каждого блока файла.
Службы поднимаются сами после падения, незапрошенного выхода, потери владения хранилищем или аренды и зависания главного потока: systemd, службы Windows и Docker перезапускают процесс, а зависание превращает в выход сторожевой поток. Матрица по платформам — ADR 0067.
Нужны live/ready health, глубина и возраст очередей, p95/p99 управления, throughput, память на поток, свободное место/staging, отказы допуска, checksum errors, лаг репликации, возраст backup и результат restore.
Backup БД и содержимого согласован по точке восстановления; cleanup не уничтожает нужные старые blob. Backup хранится вне основного домена отказа. Регулярно восстанавливаемся на чистую среду: репликация не защищает от логического удаления.
Испытания: остановка процессов в каждой фазе upload; потеря ответа после commit; отказ узла БД/storage; сетевое разделение; заполненный staging; повтор части; конфликт перезаписи; отзыв доступа; удаление во время download; работа при отключённых внешних приложениях.
Last-N автоочистка фиксирует только tombstones и audit под repository gate; physical bytes остаются до успешного online GC; блокировки читателей и гарантии. Квоты сериализуют upload reservations с policy update и учитывают retired bytes. Scheduler bounded, с live key recheck; диагностика best-effort с ограничением памяти, не блокирует transfer на ожидании записи логов. Гарантии и ограничения.
Чтение частей через upload-read держит shared upload lock: он исключает изменение, завершение и очистку временных частей. Обычного content pin недостаточно. SQL-транзакция на время I/O не удерживается; утрата владения, AbortSignal и отзыв request scope прекращают выдачу между порциями. Закрытие итератора освобождает lock, но уже выполняющийся вызов backend должен завершиться до освобождения. Неподтверждённые байты активного PUT не читаются. См. дополнение 2026-10-19 к ADR 0083.
Для опубликованного содержимого отзыв scope немедленно запрещает дальнейшую выдачу, но ContentHandle сохраняет content pin и общую блокировку удаления до завершения всех начатых backend operations и закрытия итераторов, включая оставленные потребителем на yield. Ошибка очистки одного итератора не сокращает ожидание остальных. Отмена backend I/O остаётся кооперативной.
Core cleanup для module storage удерживает глобальную эксклюзивную блокировку
18471/23 и общую блокировку барьера резервного копирования 18471/18. Активные
content readers держат общую 18471/23; любое закрепление в arkvory_backup_pins
или закрытый барьер откладывает очистку. Chunk ID нельзя передавать в
admitUnlink(uploadId): другие uploads могут ссылаться на тот же chunk.
Модуль отдельно отвечает за отсутствие живых ссылок на удаляемые данные.
Разрешение действует внутри ожидаемого collect и прекращается при завершении
или потере защищающей сессии. Это глобальный консервативный допуск: чтение
несвязанного объекта тоже может задержать очистку.
Все writable API и worker используют PostgreSQL coordinator одной установки и общий файловый ledger для data, cache и tmp. Dirty intent предшествует изменению файлов; незавершённый intent, отсутствующий или повреждённый ledger требуют перестроения по диску. Новый handle также сверяет диск. Потерявший PG-сессию coordinator не продолжает запись через переподключение. Это не общая транзакция БД и файловой системы; ограничения синхронизации каталогов Windows сохраняются (ADR 0004).
Orphan reclamation касается только tmp/coordinated-v1/<owner UUID> и требует захвата owner lock прежнего владельца в PostgreSQL. Возраст и PID не используются для решения об удалении. Неизвестные и legacy временные файлы автоматически не удаляются и остаются в учёте квоты. Перед переходом на этот протокол нужно остановить все legacy writers; смешанная запись не поддерживается. Reader и backup работают только на чтение. Подробности ledger, закрытия sinks и восстановления — ADR 0083, N6. Throughput этого протокола не измерен.