Перейти к основному содержимому

cash_loader

Кеш блокировок: BlockingCacheLoader

Назначение

BlockingCacheLoader — это компонент (Spring Bean), который отвечает за загрузку и поддержание в памяти актуального набора блокировок для мгновенной проверки возможности движения ТМЦ по любым объектам (SKU, ячейка, контейнер и т.д.) без обращения к БД.


Как работает

  1. Загрузка активных блокировок

    • При старте приложения и далее периодически (@Scheduled) или по событию (@EventListener) из таблиц blocking_doc и blocking_doc_item загружаются все записи со статусом ACTIVE.
    • Каждая строка документа преобразуется в пару (BlockingKey, набор операций).
  2. Структура кеша

    • Используется потокобезопасная структура:

      ConcurrentHashMap<BlockingKey, Set<BlockedOperation>>
    • Где BlockingKey — комбинация типа объекта (BlockingObjectType) и его идентификатора (referenceId).

  3. Публичный интерфейс

    • Проверка блокировки:

      public boolean isBlocked(BlockingObjectType type, String referenceId, BlockedOperation op)
    • Возвращает true, если операция заблокирована для объекта.

  4. Обновление кеша

    • Кеш обновляется либо по расписанию, либо после событий изменения документов (BlockingDocChangedEvent).
    • После обновления старые данные полностью заменяются новыми (atomic replace).

Пример реализации

@Service
public class BlockingCacheLoader {

private final BlockingDocRepository docRepo;
private final ConcurrentHashMap<BlockingKey, Set<BlockedOperation>> cache = new ConcurrentHashMap<>();

public boolean isBlocked(BlockingObjectType type, String refId, BlockedOperation op) {
var key = new BlockingKey(type, refId);
return cache.getOrDefault(key, Set.of()).contains(op);
}

@PostConstruct
@Scheduled(fixedDelay = 60000) // обновлять каждые 60 сек
public void reload() {
var newCache = new ConcurrentHashMap<BlockingKey, Set<BlockedOperation>>();
List<BlockingDocEntity> docs = docRepo.findAllActive();
for (var doc : docs) {
for (var item : doc.getItems()) {
var key = new BlockingKey(item.getType(), item.getReferenceId());
newCache.computeIfAbsent(key, k -> new HashSet<>()).addAll(item.getOperations());
}
}
cache.clear();
cache.putAll(newCache);
}

@EventListener
public void onBlockingDocChanged(BlockingDocChangedEvent event) {
reload();
}
}