Skip to content

0004 — Первое исполняемое ядро и границы standalone ​

Статус: accepted. Дата: 2026-09-22.

Контекст ​

Нужен проверяемый путь от HTTP-загрузки до устойчивого содержимого и повторного скачивания. HA backend, матрица внешних клиентов и топология пока не выбраны. Первое ядро не должно притворяться готовым промышленным хранилищем.

Решение ​

  • Node.js 24, TypeScript strict, ESM-сборка каждого workspace в dist с declarations. Условия arkvory-source используются только для разработки; runtime импортирует JS. Fastify и node-postgres добавлены с реальным сценарием.
  • PostgreSQL хранит миграции, descriptor, владельца, ключ идемпотентности, размер и pending | available | cancelled. UUID загрузки становится ID неизменяемого артефакта. Имя — свойство каталога, не физический путь. UPack пока сохраняется как исходный файл, без интерпретации архива.
  • POST uploads резервирует логическую ёмкость. Ключ идемпотентности ограничен repository + principal; другой descriptor с тем же ключом даёт конфликт. Поля descriptor канонизируются. Размеры в JSON — десятичные строки; текущий предел 5 GiB.
  • PUT content передаёт весь файл потоково. Размер и SHA-256 обязательны заранее. Сначала уникальный staging-файл, затем fsync файла, hard link в immutable namespace, fsync каталога на POSIX, после этого публикация в БД. Существующий blob нельзя перезаписать. Staging и blobs обязаны находиться на одной локальной файловой системе с hard links.
  • Между файловой системой и PostgreSQL нет общей транзакции. После сбоя до commit blob остаётся недоступным для download. POST complete повторно проверяет сохранённый blob и завершает запись; если publication уже available, возвращает её. Клиент после потери ответа сначала запрашивает статус.
  • На одну загрузку используется session advisory lock. SQL-транзакция на время передачи не удерживается; выделяется ограниченное число соединений. Публикация/отмена выполняется именно на соединении, владеющем lock: потерявший соединение обработчик не может записать результат через другой pool. Внутренний hash ключа lock может только ложно сериализовать разные UUID при коллизии, но не смешивает данные.
  • Профиль допускает один API-процесс на БД: отдельный advisory lock блокирует второй запуск. После потери этого соединения новые запросы получают 503 до перезапуска. Маркер storage-id связывает БД с каталогом байтов и включается в backup. Переключение на случайно пустой каталог запрещено. Это предохранитель конфигурации, не кластерный failover.
  • Приём и раздача имеют отдельные лимиты параллелизма. Перегрузка возвращает 503 + Retry-After. Это admission control без очереди ожидания и без bandwidth shaping. Каталог ограничен 100 000 записями, метаданные и JSON ограничены, ёмкость учитывает резервы.
  • Отмена скрывает pending-объект, но не освобождает физические данные и резерв. Безопасный GC — следующий отдельный этап с fencing, grace period и тестами. Не удаляем автоматически blob по одному отсутствующему commit. Остатки staging после убийства процесса также сохраняются до будущей очистки; обычные ошибки удаляют собственный temp-файл.
  • Локальные ключи хранятся как SHA-256 в отдельном конфигурационном файле, имеют стабильный principal ID и явные repository/read/write scopes. Ключи читаются при старте; ротация/отзыв требуют перезапуска. Автор загрузки управляет своей сессией; чтение published доступно всем с read на repository.

Гарантии и ограничения ​

Проверяем перезапуск/убийство процесса, а не аппаратную потерю питания. На Windows Node не предоставляет переносимого fsync каталога; на POSIX гарантия зависит от файловой системы, диска и настройки PostgreSQL. synchronous_commit и fsync PostgreSQL должны быть включены; отключение durability ради benchmark недопустимо.

Нет multipart/resume upload: незавершённый PUT повторяется с нуля в той же сессии. Download поддерживает одиночный Range и сильный ETag; multi-range игнорируется и возвращает весь объект. HEAD игнорирует Range. If-Range принимает совпадающий сильный ETag; даты не используются и дают полный ответ. Опубликованные bytes не изменяются; удаления published в этом API нет.

Нет полной UPack-модели, внешнего протокола, версионирования путей, редактора metadata, SDK, UI, очередей, HA, автоматической очистки и backup orchestration. Эти направления сохраняются в roadmap. Это не промышленный релиз и не основание мигрировать рабочие 4 ТБ.

Источники ​

  • Fastify Content-Type Parser: raw stream обрабатывается собственным ограниченным циклом; JSON bodyLimit не заменяет проверку размера byte stream.
  • node-postgres transactions: операции одной транзакции выполняются на одном client.
  • PostgreSQL advisory locks: session locks привязаны к соединению.
  • RFC 9110: условные запросы и byte ranges.

Arkvory · ProAnimaStudio