Repository navigation
Storage and Eviction.ru
Дефолт 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 была бы
разобрана как опция.
- Поправьте
KVP_CACHE_BUDGET_GBвconfigs/generic/cache.env.local. -
bash scripts/stop-cache.sh && bash scripts/start-cache.sh(данные сохраняются). - Проверка:
bash scripts/budget-report.sh status.