Skip to content

Persistence and Recovery.ru

sikkatu edited this page Nov 2, 2026 · 6 revisions

English | Русский | 中文

Персистентность и восстановление

Приёмочный критерий проекта: после перезапуска тот же разговор должен переиспользовать KV-блоки, восстановленные с NVMe — заметно быстрее, и с доказательством на уровне диска, а не только по скорости.

Чек-лист доказательств

После каждого перезапуска с тем же промптом должны выполняться ВСЕ пункты:

  1. usage.prompt_tokens_details.cached_tokens > 0 в ответе (движок переиспользовал уже посчитанную работу prefill).
  2. В KVP_CACHE_ROOT/lmcache-server.log после перезапуска появляются строки события чтения L2/диска (строки по шаблону retriev|loaded|restore с L2|disk|secondary|file) — кэш реально отдаёт из своего хранилища, что подтверждается также физическими счётчиками чтения NVMe.
  3. Байты L2 НЕ выросли, пересоздавая те же данные (bash scripts/budget-report.sh status до/после).
  4. TTFT/prefill уменьшились относительно сопоставимой холодной базовой линии.
  5. Текст ответа корректен (визуальная проверка).

Одного ускорения для закрытия вехи НЕ достаточно.

Строгий протокол (v2) — что проверяет test-recovery.sh

Просмотренный протокол, механически реализованный в scripts/test-recovery.sh (PASS/FAIL — машинный вердикт, код 0 только когда выполняются все проверки):

  1. Маркеры позиции лога — до каждого запроса фиксируется смещение в байтах в lmcache-server.log; доказательствами для сценария считаются только события после этого маркера. Ничего из прошлых прогонов просочиться не может.
  2. Запись до рестарта — прогревочный запрос должен ЗАПИСАТЬ блоки в хранилище L2 (байты каталога растут + события записи в логе). Если данных не записано, позднейшее «восстановление» ничего не доказывает — тест падает.
  3. Различие L2 и L1 — после каждого рестарта тест требует события чтения ИМЕННО с дискового уровня (а не любые строки «Retrieved», которые могут быть попаданиями в RAM). Одного cached_tokens > 0 явно НЕДОСТАТОЧНО.
  4. Жёсткий отказ готовности — если API не отозвалось за отведённое время после рестарта, тест немедленно падает; на мёртвом сервисе он не продолжает работу.
  5. Исключение альтернативных объяснений — переиспользование без событий дискового чтения после рестарта классифицируется как только RAM/prefix cache и помечается провалом.
  6. Машинный вердикт — PASS ⇒ exit 0; любая неудачная проверка ⇒ FAIL с exit 1 и failures.txt со списком причин.

Сценарий A — перезапускается vLLM, кэш-сервис жив

bash scripts/test-recovery.sh

Скрипт разогревает кэш длинным промптом, перезапускает контейнер vLLM и отправляет идентичный запрос. Сводки по фазам — в benchmarks/results/<UTC-ts>-recovery/.

Ручной эквивалент:

# разогрев
bash scripts/benchmark.sh cold 3000
# перезапуск только vLLM, ожидание повторной готовности API
docker restart mimo-pro-tp4-kvp
until curl -fsS --max-time 3 -H "Authorization: Bearer $(head -n1 live/api_key | tr -d '[:space:]')" http://127.0.0.1:30007/v1/models >/dev/null 2>&1; do sleep 10; done
bash scripts/benchmark.sh after-vllm-restart 3000     # cached_tokens должен быть > 0

Сценарий B — перезапускаются оба сервиса, данные NVMe сохранены

test-recovery.sh продолжает: stop-cache.sh → start-cache.sh → docker restart → тот же запрос. Здесь оба сервиса поднимаются с нуля: восстановление доказывает ленивое пересоздание дискового индекса, задокументированное для nixl_store_dynamic. Скорость восстановления/поиска фиксируйте из лога кэша.

Холодный / тёплый / восстановленный — как различить

Наблюдение Значение
cached_tokens = 0 холодный (без переиспользования)
cached_tokens > 0, БЕЗ событий чтения L2/диска тёплый — отдано из L1/RAM (сервис кэша выжил). Это ОЖИДАЕМЫЙ исход сценария A: запрос переиспользует KV, всё ещё находящийся в L1, поэтому с NVMe ничего читать не нужно. Это memory hit, а не отказ персистентности.
cached_tokens > 0 И события чтения из L2 ПОСЛЕ ПОЛНОГО перезапуска (оба сервиса, L1 пуста) восстановлен с NVMe (именно это доказывает персистентность)

Ожидания сценариев A и B (раунд 18): после перезапуска только vLLM сервис кэша и его L1 живы, поэтому исправная система пишет Prefetch request completed (L1+L2): N/N retained keys (N L1, 0 L2) — чистое попадание в L1. Приёмочный скрипт НЕ должен падать на этом; только сценарий B (полный перезапуск) требует доказательств L2 prefetch load completed / (N L1, N L2).

Что инвалидирует кэш (по дизайну)

Изменение любого поля идентичности — ревизия модели, токенизатор, KV-тип, геометрия блоков, TP, чат-шаблон — переводит деплой в НОВОЕ пространство: старые данные не адресуются. Это изоляция, не удаление. Откат изменения возвращает переиспользование старых данных.

Повторные прогоны

scripts/test-recovery.sh создаёт свежий каталог результатов на каждый запуск; ничего не перезаписывается. Итоговое число — медиана ≥3 повторов на конфигурацию (см. страницу бенчмарков).

Clone this wiki locally