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 — every running node has to drop its cached interpretation of the old metadata. xly does this through JMS, not by polling.
The path
PM saves in BACK
│
▼
BACK controller writes the changed gds_* row
│
▼
Controller publishes a JMS "module changed" message
│
▼
Every node's xlyErpJmsConsumer receives it
│
▼
ConsumerChangeGdsModuleThread.run() clears the relevant Redis keys
│
▼
Next /business/getModelBysId call on any node re-reads the table
and re-populates the cache with the new value
The handler is in
xlyErpJmsConsumer/src/main/java/com/xly/xlyerpjmsconsumer/thread/ConsumerChangeGdsModuleThread.java.
Why JMS, not poll-and-bust
xly often runs across multiple nodes (xlyEntry, xlyApi, xlyInterface each on their own JVM, sometimes scaled horizontally). Polling for "has the metadata changed?" would either be slow (the change isn't visible until the next poll) or chatty (constant heartbeats). JMS fans out the invalidation to every node within milliseconds.
xly uses both ActiveMQ and RocketMQ in the codebase, but the
metadata-change path documented here is the ActiveMQ / JMS one:
xlyErpJmsConsumer listens on P2pQueue.ERP_JMS_ACTIVEMQ_CHANGE_GDS_MODULE
with @JmsListener, and ConsumerChangeGdsModuleThread handles the
cache-bust work. RocketMQServiceImpl exists for other integration flows.
Which keys get cleared
The Redis cache holds:
- Module metadata by
sId. - Form metadata by
sId. - Field-list slaves keyed by form
sId. - Per-tenant overlay merges (a derived cache).
- Permission rules per (module, role).
The consumer thread receives the changed row's IDs and clears each cache key family that could plausibly include it. Over-invalidating is the safe option here — the cost of an extra DB read on the next request is far smaller than the cost of serving stale metadata.
When you change metadata directly via SQL
Inserts/updates done through MyBatis or BACK trigger the JMS event.
Inserts/updates done by an engineer running raw UPDATE gdsmodule SET
... against the production DB do not trigger it. 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.
- A manual JMS message is sent (see
BusinessCleanRedisDataImplinxlyBusinessService).
The third option is the supported workaround; option (2) is the brute-force fallback.
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.) - Is the JMS broker reachable from the BACK node? (If not, the invalidation event silently isn't published.)
- Are all consumer nodes running? (If a node is paused, it'll serve stale metadata until restarted.)
- Did the change happen via raw SQL? (Then no JMS event was published — manually trigger it.)
The "five-table read" of Slice 1 re-runs from the cache; understanding which layer is stale is the key to the bug.