Cache invalidation on metadata change
When a PM saves a change in BACK — adds a column to a form, updates a
permission, registers a new module — the framework drops the cached
interpretation of the old metadata. The cache-clear is synchronous
in the BACK process via Spring's @CacheEvict, NOT a JMS fan-out.
A separate JMS path with similarly-named classes exists for a
different purpose (base-data merge); the two are easy to confuse and
this page calls them out explicitly.
The actual cache-invalidation path (synchronous, in-process)
PM saves in BACK
│
▼
BACK controller (e.g. /business/addUpdateDelBusinessData) calls
BusinessBaseServiceImpl.addBusinessData / updateBusinessData / deleteBusinessData
│
▼
Save service calls businessCleanRedisData.delCleanRedisData(...)
(e.g., BusinessBaseServiceImpl.java:1122, 1224, 1375, 1441, 1597, 1677)
│
▼
BusinessCleanRedisDataImpl.delCleanRedisDataByTableName(<sTable>, ...)
dispatches to one of the named cleaners on CleanRedisServiceImpl
│
▼
CleanRedisServiceImpl.cleanRedisByTableNameGdsModle() (or similar)
fires @CacheEvict against a fixed list of named cache regions
│
▼
Spring CacheManager evicts the named entries
│
▼
Next /business/getModelBysId call re-reads from DB and re-populates
the cache.
The cleaner methods are in
xlyBusinessService/src/main/java/com/xly/service/impl/CleanRedisServiceImpl.java.
A representative one — invoked when gdsmodule rows change — evicts
17 cache regions in a single call:
@CacheEvict(value = {
"getGdsmoduleTree", "getGdsmoduleList", "getModuleTreePro",
"getSysjurisdictionTreePro", "getsDisplayTypeAll",
"businessBaseServiceGetMenuList", "getBuMenu", "getMenu",
"getsAuthsId", "businessCommonServicegetModulelistAll",
"gdsmoduleById", "getSaveProName", "businessParameterGetParameter",
"getPrcName", "getKpiModelByUser", "getUserByFromId",
"getUserByActionId", "getModuleTreeProAll"
}, allEntries = true)
public void cleanRedisByTableNameGdsModle() { … }
Other table-named cleaners on the same class evict the regions
relevant to gdsconfigformmaster, gdsconfigformslave,
gdsconfigtbmaster, gdsformconst, gdsjurisdiction,
gdsconfigcharmaster, login-info, billnosetting, kpimaster,
SysSystemSettings, etc.
What the JMS CHANGE_GDS_MODULE queue actually does (NOT cache-bust)
The framework has a JMS queue
P2pQueue.ERP_JMS_ACTIVEMQ_CHANGE_GDS_MODULE and a consumer thread
ConsumerChangeGdsModuleThread, both of which sound like they should
be doing cache invalidation — but they don't.
ConsumerChangeGdsModuleThread.run() resolves a
changeGdsModuleService bean (ChangeGdsModuleServiceImpl) and calls
changeTableData(sGdsModuleId, sJobId), which invokes the stored
procedure PRO_ERPMERGEBASEGDSMODULE (via proDao.proErpMergeBaseGdsModule,
mapped in ProMapper.xml). That proc consolidates per-tenant
gdsmodule rows into a flattened "base" lookup table — a base-data
merge job, not a cache evict. A grep of xlyErpJmsConsumer/ for
@CacheEvict or cleanRedis* returns zero hits — the consumer side
clears nothing in Redis.
The same goes for the other 23 ERP_JMS_ACTIVEMQ_* queues in
P2pQueue.java: each one drives a
domain-specific base-data merge or fan-out work item, not cache
invalidation.
The cross-node coherence question (open)
@EnableCaching is on EntryApplicationBoot.java:22 and
ApiApplicationBoot.java:24. No custom CacheManager bean is
declared anywhere in the in-scope source (no RedisCacheManager,
no @Bean CacheManager-returning method, no implements CacheManager,
no spring.cache.* property in any application*.yml). Spring Boot
2.2.5 will then auto-pick a CacheManager based on what's on the
classpath; the most likely outcome with spring-boot-starter-data-redis
present is that Spring auto-configures Redis-backed caching, which
would make @CacheEvict clear the shared Redis store and therefore
fan out across nodes implicitly. This has not been confirmed by live
inspection of a running node — it's the natural reading of the
config but worth verifying when a deployment is available. If the
auto-picked manager is in fact ConcurrentMapCacheManager (in-memory,
per-JVM), the multi-node coherence story is broken under this code
path and would need a separate fix.
When you change metadata directly via SQL
Inserts/updates done through MyBatis or BACK trigger
businessCleanRedisData.delCleanRedisData*. Raw UPDATE gdsmodule SET …
against the DB does not trigger any cleaner. The cache will serve
stale metadata until either:
- The cache TTL expires (check the cache config for the actual TTL).
- A bounce of the application servers (one node at a time if the cache is local; once if shared).
- A manual call to one of the
BusinessCleanRedisDataImpl.delCleanRedisDataByTableName(<table>, …)methods is invoked from inside the application (e.g., via a maintenance endpoint). Note this clears whatever the localCacheManageris bound to; if that turns out to be in-memory, the cleanup must run on every node.
Common bug: the cache is the bug
When something looks like "I changed it but the page still shows the old value", check (in this order):
- Did the change actually commit? (Confirm with
SELECTagainst the DB.) - Did the change go through a path that invokes
BusinessCleanRedisData? (Direct DB writes or controllers that bypassBusinessBaseServiceImplwon't.) - Is the cache shared across nodes (Redis-backed) or local
(
ConcurrentMapCacheManager)? Confirm by inspecting the activeCacheManagerbean on a running node. - If the cache is local, did every node get the eviction call?
The five-key composite returned by
getModelBysId in Slice 1
re-runs from the cache; understanding which layer is stale is the key
to the bug.