> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yume.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Хранение и кэш настроек

> Где хранятся значения настроек компании, как они читаются с подстановкой значений по умолчанию, как работают прогрев и сброс кэша, массовое сохранение и что происходит вне контекста компании.

Полный перечень полей — в [глоссарии хранения и кэша настроек](/ru/logic/settings/storage-cache-glossary).

Настройки компании существуют в двух слоях. Первый — **реестр настроек компании**: заданный в коде список всех известных системе параметров, где у каждого ключа записаны значение по умолчанию, служебная подпись и (не у всех ключей) тип значения; там, где тип не указан, он выводится из значения по умолчанию. Второй — **сохранённые значения**: строки в базе, которые компания завела для тех ключей, где её решение отличается от значения по умолчанию. Между кодом и этими двумя слоями стоит **хранилище настроек** — оно решает, откуда взять значение, держит горячую копию в кэше и сбрасывает её при изменениях.

Всего в реестре порядка ста двадцати ключей.

## Модель данных

Сохранённое значение — это одна строка «компания + ключ + значение».

| Поле           | Тип                            | Назначение                                                                                                      |
| -------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| Идентификатор  | число                          | Первичный ключ строки                                                                                           |
| Компания       | связь                          | Владелец значения; при удалении компании её значения удаляются вместе с ней                                     |
| Ключ настройки | текст (до 255 символов)        | Имя параметра из реестра (плюс общий префикс имён, по умолчанию пустой)                                         |
| Значение       | закодированный слепок значения | Текстовая колонка, в которую помещается что угодно: флаг, число, дробное, строка, список, словарь, длительность |

Пара «компания + ключ» уникальна: у компании не может быть двух значений одного параметра. Ключи, по которым строки нет, работают на значении по умолчанию из реестра — то есть отсутствие строки это не ошибка, а нормальное состояние.

<Info>
  Таблица сохранённых значений — общая для всей платформы: она лежит в общей области данных, а не внутри данных каждой компании, и разделение обеспечивается колонкой «Компания». Это исключение из общей схемы разделения данных, описанной в [«Разделение данных компаний и фоновая обработка»](/ru/logic/infrastructure).
</Info>

### Закодированное значение и его последствия

Значение сохраняется как единый слепок объекта, закодированный в текст (без сжатия, с фиксированной версией формата; исходный объект перед сохранением копируется, чтобы его нельзя было испортить). Плюс — в настройке можно хранить сложные структуры вроде списка вариантов штрафов с длительностями или раскладки карточки аренды. Минус — по значению почти нельзя искать в базе: разрешены только точное совпадение, перечисление вариантов и проверка «значение задано/не задано», а любое другое сравнение (больше, меньше, вхождение подстроки, сортировка, суммирование) завершается ошибкой. И даже точное совпадение сравнивает не смысл, а закодированное представление.

### Значение по умолчанию против сохранённого

Чтение даёт «сохранённое значение, если оно есть, иначе значение по умолчанию». Признаком «значения нет» служит только отсутствие значения в строке. Пустая строка, ноль, выключенный флаг — это полноценные сохранённые значения: они возвращаются как есть.

<Warning>
  Чтение настройки, у которой ещё нет сохранённого значения, **само создаёт строку** со значением по умолчанию. То есть обычный просмотр данных компании постепенно материализует в базе весь реестр: сначала строк нет ни одной, через какое-то время работы их столько, сколько разных настроек успела прочитать система.
</Warning>

<Warning>
  Сохранение со страницы настроек подменяет любое «пустое» значение на пустой текст: выключенный флаг, ноль, нулевая длительность и пустой список записываются в базу одинаково — пустой строкой. Чаще всего это проходит незаметно, потому что пустая строка ведёт себя как «выключено», а страница настроек умеет показывать её обратно как ноль или нулевую длительность. Но код, которому нужно именно число, получает текст: при нулевом округлении штрафа пересчёт падает на преобразовании значения. А прямые проверки по базе вида «настройка выключена» такие строки не находят, потому что ищут логическое «нет», а не пустой текст.
</Warning>

Запись по ключу, которого нет в реестре, невозможна — попытка завершается ошибкой. Массовое обновление сначала проверяет весь переданный набор ключей и только потом начинает писать.

## Как читается настройка

Точка доступа к настройкам — **ленивый объект настроек**: он общий на весь процесс и создаётся при первом обращении, а не при запуске приложения (иначе приложение лезло бы в базу ещё до того, как известно, с какой компанией работает запрос). Собственного состояния у него нет: всё зависит от того, в контексте какой компании выполняется текущий код. В тот же момент — при первом обращении — подключается и автоматический сброс кэша при изменении строки настройки.

Порядок разрешения одного значения:

1. **Нет контекста компании** — чтение сразу возвращает пусто, и вызывающий код получает значение по умолчанию.
2. **Кэш.** Имя ключа складывается из идентификатора компании, подчёркивания и имени настройки (плюс общий префикс имён, по умолчанию пустой); сверху общий механизм кэша добавляет к ключу имя области данных текущей компании. Если значение найдено — оно и возвращается.
3. **Промах — попытка прогрева.** Хранилище пробует разложить по кэшу сразу все настройки компании и читает ключ повторно.
4. **Всё ещё пусто — база.** Делается точечный запрос за одной строкой. Результат кладётся в кэш «только если там ещё пусто», чтобы не затереть значение, которое параллельно успел записать кто-то другой. Если строки нет, в кэш попадает пустое значение — при следующем чтении это неотличимо от промаха.
5. **Строки нет** — берётся значение по умолчанию, и оно тут же сохраняется в базу (см. предупреждение выше).

<Note>
  Каждое обращение к настройке — это отдельный поход в кэш. Результат не запоминается в пределах запроса, поэтому обработчик, читающий полтора десятка настроек, делает полтора десятка обращений к кэшу. Это дёшево, но не бесплатно, и именно поэтому прогрев так важен.
</Note>

## Прогрев кэша

Прогрев — главный приём этого модуля: вместо того чтобы ходить в базу за каждым ключом, хранилище одним запросом достаёт **все** сохранённые значения компании и раскладывает их по кэшу.

* Срок жизни разложенных значений — **сутки** (настраиваемый параметр модуля).
* Рядом кладётся служебная **метка прогрева**. Пока она жива, повторный прогрев не выполняется, даже если конкретного ключа в кэше нет.
* Если срок жизни выставлен в ноль, прогрев отключается полностью и всё читается точечно.
* Вне контекста компании прогрев молча пропускается.

Прогрев запускается в трёх случаях: при создании хранилища (то есть при самом первом обращении к настройкам в процессе), при промахе кэша и после каждого полного сброса.

<Warning>
  Прогрев раскладывает по кэшу только те ключи, по которым **уже есть сохранённые строки**. Ключи, живущие на значении по умолчанию, метка прогрева не покрывает: пока их первое чтение не создаст строку, каждое обращение к ним будет уходить в базу отдельным запросом.
</Warning>

## Как записывается настройка

### Одиночная запись

Запись одного значения делает три вещи подряд: сохраняет строку в базе (создаёт или обновляет), при обновлении существующей строки автоматически запускается полный сброс кэша компании, а затем новое значение кладётся в кэш. Порядок важен: сброс происходит **до** записи свежего значения, поэтому новое значение переживает собственную инвалидацию. При создании новой строки сброс пропускается.

### Массовое обновление

Сохранение целой формы настроек идёт иначе, чтобы не сбрасывать кэш сотню раз подряд:

1. включается **подавление реакций на сохранение** — автоматические обработчики на время записи замолкают;
2. значения пишутся по одному в базу, но уже без побочных эффектов;
3. подавление снимается, и выполняется **один** полный сброс кэша компании;
4. свежие значения раскладываются по кэшу.

Подавление действует только внутри текущего запроса или фоновой задачи и не влияет на параллельную работу других.

<Note>
  На последнем шаге значения раскладываются дважды: сначала под правильными именами (с идентификатором компании), затем ещё раз под именами без идентификатора. Второй набор ключей никем не читается — это лишние записи в кэш, по одной на каждое сохранённое значение.
</Note>

<Warning>
  Массовое сохранение не обёрнуто в общую транзакцию, а кэш сбрасывается только в самом конце. Если запись оборвётся на середине, часть значений уже окажется в базе, а кэш ещё будет отдавать старые — до истечения срока жизни записей.
</Warning>

### Полный сброс

Сброс работает по идентификатору компании: он удаляет из кэша ключи всех настроек реестра плюс метку прогрева, после чего сразу же запускает прогрев заново. То есть цена изменения одной настройки — удаление порядка ста двадцати ключей, один запрос в базу за всеми значениями компании и повторная раскладка их по кэшу.

<Note>
  Сброс перебирает именно **ключи реестра**. Значение по ключу, который из реестра убрали, останется в базе, не будет вычищено из кэша и не будет попадать в прогрев.
</Note>

### Автоматическая реакция на сохранение

На сохранение строки настройки подписаны две реакции, и они делают разное:

| Реакция                    | Когда срабатывает                                                                                  | Что делает                                                                |
| -------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Сброс кэша компании        | При **изменении** существующей строки; пропускается при создании новой и при включённом подавлении | Полностью чистит и заново прогревает кэш компании                         |
| Обновление значения в кэше | При любом сохранении, кроме подавленного                                                           | Переключается в область данных компании-владельца и кладёт значение в кэш |

Пропуск сброса при создании строки — не оплошность: новой строки в кэше по определению ещё нет, а свежее значение вызывающий код запишет туда сам.

<Warning>
  Вторая реакция кладёт значение в кэш под именем ключа **без идентификатора компании**, тогда как чтение ищет ключ с идентификатором. Эта запись фактически никем не читается, зато в большинстве случаев требует дополнительно подтянуть из базы компанию-владельца. Реальную инвалидацию делает только сброс кэша в хранилище.
</Warning>

<Note>
  Сброс кэша при изменении строки подключается в тот момент, когда процесс впервые обращается к настройкам через хранилище. Процесс, который только пишет строки настроек и ни разу их не читал, этой реакции не имеет.
</Note>

## Сроки жизни записей в кэше

| Что кладётся                                  | Срок жизни                                   |
| --------------------------------------------- | -------------------------------------------- |
| Значения, разложенные прогревом               | сутки                                        |
| Метка прогрева                                | сутки                                        |
| Значение, записанное при сохранении настройки | общий короткий срок кэша проекта — 15 секунд |
| Значение, прочитанное из базы при промахе     | те же 15 секунд                              |

Асимметрия здесь не случайна, но её последствия стоит понимать. Только что изменённая настройка живёт в кэше пятнадцать секунд, а метка прогрева — сутки. Поэтому после того как короткий срок истечёт, повторный прогрев не запустится (метка ещё жива), и каждое обращение к этой настройке будет уходить в базу отдельным запросом — до тех пор, пока не истекут сутки и кэш не прогреется целиком заново. На корректность это не влияет, на нагрузку — немного да.

## Поведение вне контекста компании

Когда код выполняется не от имени конкретной компании, вместо неё подставляется **заглушка компании**. Хранилище настроек реагирует на неё предельно тихо:

* **чтение** возвращает пусто — а значит, вызывающий код получает значение по умолчанию из реестра;
* **запись** молча игнорируется, ошибки не будет;
* **прогрев** пропускается;
* **сброс кэша** удалит ключи, но повторный прогрев тут же завершится ничем.

<Warning>
  Это самая опасная особенность модуля. Код, который переключил только область данных, но не контекст компании, увидит не настройки компании, а **значения по умолчанию**: выключенными окажутся штрафы, бонусы и налог, а валюта и цвета статусов станут стандартными. Ошибки при этом не будет ни одной. Общая механика контекста компании описана в [«Разделение данных компаний и фоновая обработка»](/ru/logic/infrastructure).
</Warning>

Отдельный случай той же природы — платформенная админка: она работает вне контекста конкретной компании, а ключи кэша дополнительно разделены по области данных. Поэтому правка настроек компании из админки чистит ключи «не той» области, повторный прогрев там завершается ничем, и компания продолжает читать старое значение до истечения срока жизни записи — то есть в худшем случае до суток.

## Устойчивость к сбоям

Все обращения модуля к кэшу идут через обёртки **безопасной работы с кэшем**: при любой ошибке кэша в журнал пишется предупреждение, а вызов возвращает значение по умолчанию или признак неуспеха. Ни чтение, ни запись настройки не роняют запрос из-за недоступного кэша.

Точно так же подавляются и ошибки базы при чтении: если запрос за настройками не проходит (например, во время миграций), вместо исключения система просто останется на значениях по умолчанию.

<Warning>
  Обратная сторона: недоступность кэша или базы внешне выглядит как «у компании сбросились настройки» — возможности выключаются, валюта и цвета возвращаются к стандартным. Первым делом стоит смотреть предупреждения в журнале, а не искать, кто изменил настройки.
</Warning>

## Как это используется

* **Бизнес-логика** — аренды, клиенты, инвентарь, метрики, фоновые задачи — читает настройки, как правило, через ленивый объект настроек, то есть через описанную выше цепочку «кэш → прогрев → база → значение по умолчанию».
* **Часть фоновых задач и интеграций** обращается к строкам напрямую: отбирает компании по сохранённому значению настройки или включает возможности, записывая строки. Такие обращения идут мимо кэша и находят только те компании, у которых строка действительно сохранена.
* **Страница настроек в API** читает значения иначе: она берёт значения по умолчанию из реестра и накладывает поверх сохранённые строки компании **напрямую из базы, минуя кэш**. Сохранение с этой же страницы идёт через массовое обновление, то есть с одним сбросом кэша в конце.
* **Платформенная админка** пишет строки настроек напрямую, по одному ключу за раз, и умеет удалять строку целиком («сбросить настройку»).
* **Создание компании** сразу записывает несколько значений строками в базу: «Название организации», «Адрес организации», «Валюта» и «Язык компании» — остальное компания получает на значениях по умолчанию.
* **Подключение платных модулей** включает связанные с ними возможности, записывая строки настроек напрямую.

<Tip>
  Если изменение настройки «не подействовало», проверяйте в таком порядке: сохранилась ли строка в базе (страница настроек показывает именно её), не сбрасывался ли кэш в чужой области данных (правка из платформенной админки или удаление строки), и не читается ли настройка кодом, работающим вне контекста компании.
</Tip>

## Подводные камни

<Warning>
  **Чтение меняет данные, а пустые значения теряют тип**

  * Чтение настройки, у которой ещё нет сохранённого значения, само создаёт в базе строку со значением по умолчанию. Обычный просмотр данных приводит к записи, и со временем реестр материализуется в базе целиком.
  * Сохранение со страницы настроек подменяет любое «пустое» значение на пустой текст: выключенный флаг, ноль, нулевая длительность и пустой список ложатся в базу одинаково. Код, ожидающий число, получает текст — при нулевом округлении штрафа пересчёт падает на преобразовании значения.
  * Признаком «значения нет» служит только отсутствие значения в строке; пустая строка, ноль и выключенный флаг возвращаются как есть. Поэтому прямые проверки по базе «настройка выключена» не находят строки, сохранённые со страницы настроек, — они ищут логическое «нет», а там лежит пустой текст.
</Warning>

<Warning>
  **Тишина вне контекста компании и промахи инвалидации**

  * Вне контекста компании чтение любой настройки молча возвращает значение по умолчанию, а запись игнорируется без ошибки. Код, переключивший только область данных, но не контекст компании, работает на выключенных возможностях и стандартной валюте.
  * Ключи кэша дополнительно разделяются по области данных текущей компании, а сброс кэша выполняется там, где идёт сохранение. Правка настроек компании из платформенной админки идёт вне контекста этой компании: чистятся чужие ключи, повторный прогрев завершается ничем, и компания видит старое значение до суток.
  * Сброс отдельной настройки в платформенной админке удаляет строку, но реакции на удаление нет вообще — кэш не чистится, и система продолжает применять удалённое значение до истечения срока жизни записи.
</Warning>

Менее критичные особенности, о которых стоит знать:

* Реакция на сохранение кладёт значение в кэш под именем ключа без идентификатора компании, а чтение ищет ключ с идентификатором. Эта запись никем не читается, зато в большинстве случаев требует дополнительно подтянуть из базы компанию-владельца.
* Массовое сохранение раскладывает свежие значения по кэшу дважды: под правильными именами и ещё раз под именами без идентификатора компании. Второй набор ключей никем не читается.
* Прогрев кладёт значения на сутки, а точечная запись и чтение из базы — на общий короткий срок кэша проекта (15 секунд). Поскольку метка прогрева живёт сутки, только что изменённая настройка после этих 15 секунд читается из базы отдельным запросом при каждом обращении.
* Прогрев раскладывает по кэшу только ключи, по которым уже есть сохранённые строки. Ключи на значениях по умолчанию метка прогрева не покрывает, и каждое обращение к ним уходит в базу, пока первое чтение не создаст строку.
* Массовое сохранение не обёрнуто в транзакцию и сбрасывает кэш только в самом конце: обрыв на середине оставит часть значений в базе, а кэш — со старыми.
* Сброс кэша и прогрев перебирают только ключи из реестра, поэтому значение по ключу, убранному из реестра, останется в базе, не будет вычищено из кэша и не попадёт в прогрев.
* Массовое сохранение из платформенной админки идёт по одному ключу и без подавления реакций: каждое изменённое поле запускает отдельный сброс, который к тому же чистит ключи не в той области данных.
* Автоматический сброс кэша при изменении строки подключается в момент первого обращения к настройкам через хранилище в данном процессе. Процесс, который только пишет строки и ни разу их не читал, этой реакции не имеет.
* Ошибки кэша и ошибки базы при чтении подавляются с записью предупреждения в журнал, поэтому сбой хранилища внешне выглядит как самопроизвольный сброс настроек компании, а не как авария.

<Info>
  Дополнительные наблюдения:

  * Страница настроек в API читает значения напрямую из базы, минуя кэш, поэтому она может показывать уже новое значение, тогда как бизнес-логика ещё применяет закэшированное старое.
  * Значение хранится закодированным слепком в текстовой колонке, и по нему разрешены только точное совпадение, перечисление вариантов и проверка «задано/не задано»; любое другое сравнение завершается ошибкой.
  * Тип значения указан не у всех ключей реестра: часть записей содержит только значение по умолчанию и подпись, а тип выводится из значения по умолчанию.
  * Каждое обращение к настройке — отдельный поход в кэш: результат не запоминается в пределах запроса, поэтому обработчик, читающий десяток настроек, делает десяток обращений.
  * При создании компании четыре настройки — название, адрес, валюта и язык — записываются строками напрямую, минуя обычный слой записи; подключение платных модулей точно так же напрямую включает связанные возможности.
</Info>

## Связанные страницы

<CardGroup cols={2}>
  <Card title="Глоссарий: Хранение и кэш настроек" icon="book" href="/ru/logic/settings/storage-cache-glossary">
    Полный перечень полей и терминов слоя хранения настроек.
  </Card>

  <Card title="Настройки компании" icon="sliders" href="/ru/logic/settings">
    Обзорная страница модуля со всеми его разделами.
  </Card>

  <Card title="Реестр настроек компании" icon="sliders" href="/ru/logic/settings/config">
    Полный перечень параметров компании: что можно включить или выключить, какие значения стоят по умолчанию и как настройки читаются и сохраняются.
  </Card>

  <Card title="Дополнительные поля" icon="list-check" href="/ru/logic/settings/custom-fields">
    Собственные поля компании для клиентов, инвентаря, аренд и других справочников: типы, обязательность, отображение в таблице и фильтрах.
  </Card>

  <Card title="Переименование сущностей" icon="language" href="/ru/logic/settings/labels">
    Как компания меняет названия разделов и статусов под свою нишу, включая формы слова по падежам и числам.
  </Card>

  <Card title="Настройки таблиц" icon="table-columns" href="/ru/logic/settings/tables">
    Сохранённый состав, порядок и ширина колонок для таблиц интерфейса — отдельно по каждой таблице компании.
  </Card>

  <Card title="Промо-баннеры" icon="bullhorn" href="/ru/logic/settings/promo">
    Показ рекламных и информационных баннеров платформы внутри системы: частота показа, отметка о просмотре и исключение компаний.
  </Card>

  <Card title="Сквозная автонумерация" icon="hashtag" href="/ru/logic/settings/counters">
    Общий счётчик компании, из которого берутся номера клиентов, артикулы создаваемых единиц инвентаря и номера документов.
  </Card>

  <Card title="Настройки в админ-панели" icon="screwdriver-wrench" href="/ru/logic/settings/admin">
    Служебный экран платформы для просмотра и правки настроек конкретной компании со сбросом значений к умолчанию.
  </Card>
</CardGroup>
