Skip to content

Storage and Eviction.ru

sikkatu edited this page Nov 2, 2026 · 9 revisions

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

Хранилище и эвикция

Модель бюджета

Дефолт 1.5 ТБ намеренно щедрый: больше истории prefill остаётся на общем диске ВМ, поэтому повторное открытие разговора экономит время и энергию при первой загрузке.

Каким по длине может быть разговор?

Два разных лимита не путайте:

  • Лимит токенов одного разговора (~1M) — ограничен контекстным окном модели (max_position_embeddings в config.json модели), НЕ этим кэшем.
  • Сколько истории кэш держит на диске — ограничено только KVP_CACHE_BUDGET_GB. Токенных лимитов здесь нет: свыше 1 млн токенов имеет значение лишь граница переиспользования одного контекста (см. про переиспользование префикса в разделе «Архитектура»), а не kv-persistence как таковой.
Ограничение По умолчанию Кем обеспечивается
Жёсткий лимит KVP_CACHE_BUDGET_GB=1500 ГБ (1.5 ТБ) max_capacity_gb L2-адаптера — записи сверх ОТКЛОНЯЮТСЯ
Физический потолок объём NVMe-устройства (проверяется preflight) проверка свободного места через KVP_CACHE_DEVICE
Мягкий порог 0.9 бюджета trigger_watermark — здесь начинается автоматическая эвикция
Цель возврата ~0.81 бюджета после одного цикла (0.9 х 0.9 от занятого) eviction_ratio 0.1 занятой ёмкости
Квота пространства опционально, quotas.json слой политик kvp-admin quota

Бюджет покрывает ТОЛЬКО постоянные KV-данные. Веса модели, ОС и прочие файлы здесь не учитываются и ни один скрипт kv-persistence их не трогает.

Учёт ёмкости

  • Один файл на объект KV-чанка прямо в KVP_CACHE_ROOT (временный файл + атомарный rename). Адаптер nixl_store_dynamic пишет плоские файлы *.bin — подкаталогов по пространствам нет; пространство идентичности закодировано в префиксе имени файла (санитизированное имя модели).
  • Отдельного дискового индекса нет — детерминированные имена файлов И есть индекс; при старте он восстанавливается лениво. used_bytes — сумма размеров реальных файлов кэша (плюс устаревшие каталоги kvp-*, если есть); отдельной строки «накладные расходы индекса» нет.
  • Приёмку записей обеспечивает адаптер LMCache через max_capacity_gb (жёсткая граница L2): записи сверх неё отклоняются. Собственный BudgetLedger репозитория считает те же числа для отчётов и планирования, но НЕ подключён к производственному пути записи — не рассчитывайте на него как на блокировку.
  • Учёт после рестарта (NEEDS TESTING, правка ревью): после рестарта сервиса кэша индекс в памяти восстанавливается ЛЕНИВО по мере обращения к файлам — диск на старте не сканируется, поэтому used_bytes начинается с нуля, хотя реальные файлы на месте. Не считайте, что файлы до рестарта учитываются в max_capacity_gb, пока аппаратный прогон не подтвердит, что закреплённая сборка возвращает восстановленные размеры в get_usage(). Обязательный тест: заполнить кэш → перезапустить сервис → продолжать писать → проверить, что used_bytes растёт и остаётся ограниченным.
  • Мусор от краша: после жёсткого убийства (SIGKILL, OOM, отключение питания) адаптер может оставить <final>.bin.tmp.<uuid> — файлы, не успевшие переименоваться. Они НЕ входят в индекс кэша (никогда не возвращаются как KV), но ЕДЯТ диск, поэтому status отчитывается о них отдельно (tmp_files, tmp_files_bytes). Удалить: purge --tmp --yes (сохраняет все настоящие кэш-файлы) или purge --all --yes. Идущие записи защищены реестром активных записей (kvpersist.tmp_guard): каждая запись регистрирует маркер до появления tmp-файла и снимает его после атомарного rename. Маркер считается ЖИВЫМ, пока жив процесс-писатель (проверка по (pid, время старта процесса), повторное использование PID не обманет) — зарегистрированная запись не удаляется, сколько бы она ни шла (запись дольше часа тоже защищена; критерий — живость, а не возраст). Если живость определить нельзя (нет /proc, маркер нечитаем) — маркер сохраняется: лучше держать лишний tmp-файл, чем удалить файл живой записи. Маркеры, чей писатель доказанно завершился, вычищаются через час. Как страховка для писателей без реестра дополнительно действует 15-минутное окно: недавние tmp-файлы пропускаются. Отчёт в любой момент:
bash scripts/budget-report.sh status

Поля: used_bytes, free_bytes, usage_fraction, soft_pressure, per_namespace_bytes, evictions, reclaimed_bytes, rejected_writes, tmp_files, tmp_files_bytes.

Автоматическая эвикция (обязательна, всегда включена)

  • Фоновый поток следит за get_usage(). Выше trigger_watermark политика вычищает до снижения ёмкости на eviction_ratio.
  • Политика, которая реально работает в продакшене — LRU по сохранённым ключам чанков (нативная для адаптера, внутри LMCache). Длинные часто используемые истории защищены ТОЛЬКО правилом свежести LRU — дополнительной ценностной защиты пока нет.
  • Ценностное ранжирование (implementation/kvpersist/retention.py) — это отдельный механизм планирования, НЕ интегрированный в производственный путь эвикции: он упорядочивает записи по свежести (спад 24 ч), числу переиспользований, длине префикса и стоимости хранения и покрыт unit-тестами — но чанки сегодня удаляет собственный LRU LMCache. Не рассчитывайте на то, что длинные ценные префиксы выживают благодаря retention.py.
  • Предупреждения о давлении на хранилище: soft_pressure: true в отчёте; лаунчер также предупреждает на старте, если свободного места на ФС меньше бюджета.

Ручная очистка — только явно

bash scripts/budget-report.sh status                    # реальные имена пространств
bash scripts/budget-report.sh purge --namespace=NAME --yes
bash scripts/budget-report.sh purge --all --yes
bash scripts/budget-report.sh purge --tmp --yes         # только мусор от краша

Раскладка кэша ПЛОСКАЯ: файлы *.bin лежат прямо в KVP_CACHE_ROOT, а пространство идентичности — это префикс имени файла (санитизированное имя модели, например --models--MyModel--a84fadf1). Реальные имена берите из status → namespaces, не угадывайте. Так как реальные имена часто НАЧИНАЮТСЯ с -, передавайте значение как --namespace=NAME или позиционно (purge --namespace=NAME --yes / purge NAME --yes), но не как --namespace NAME. Сопоставление ТОЧНОЕ (без подстрочного совпадения). Требуется --yes. Удаление ограничено файлами кэша внутри KVP_CACHE_ROOT — всё остальное отклоняется с кодом 2. Старт НИКОГДА не очищает кэш автоматически.

Дедупликация и общие блоки

LMCache хранит чанки под ключами от содержимого: идентичное содержимое чанка ⇒ идентичный ключ ⇒ один сохранённый объект, а не два. Два разговора с одним стабильным префиксом физически делят объекты:

  • Одно и то же пространство совместимости ⇒ общие ключи; разные пространства никогда не сталкиваются.
  • Эвикция одной записи снимает только её ключи; общие объекты остаются, пока на них указывает любой другой ключ. Если общий объект в итоге вычищен (LRU считает ключ неиспользуемым), корректность при параллельности сохраняется: промах просто пересчитывается — компромисс в живости, но не в корректности.
  • Выигрыш от дедупликации измерим по used_bytes до/после: модель content-ключей покрывает tests/unit/test_dedup_model.py (симуляция, без LMCache), а реальные файлы L2 считает tests/integration/test_l2_dedup_real.py против живого lmcache server (общие чанки префикса пишутся один раз).

Пространства и политика очистки

Раскладка кэша ПЛОСКАЯ: один файл *.bin на KV-чанок прямо в KVP_CACHE_ROOT, а пространство идентичности — это префикс имени файла (санитизированное имя модели, например --models--MyModel--a84fadf1). Смена ревизии создаёт НОВОЕ пространство (новый префикс имён); старые НИКОГДА не удаляются автоматически (задокументированная политика: очистка — явное решение администратора):

bash scripts/budget-report.sh status      # список имён пространств (поле "namespaces")
bash scripts/budget-report.sh purge --namespace=NAME --yes

Используйте форму --namespace=NAME (=) или позиционную передачу — реальные имена пространств часто НАЧИНАЮТСЯ с -, и запись --namespace NAME была бы разобрана как опция.

Как изменить лимит

  1. Поправьте KVP_CACHE_BUDGET_GB в configs/generic/cache.env.local.
  2. bash scripts/stop-cache.sh && bash scripts/start-cache.sh (данные сохраняются).
  3. Проверка: bash scripts/budget-report.sh status.

Clone this wiki locally