dotCMS / dotCMS/core

App secret changes only invalidate cached clients on the node that saved them

Open
#37,623 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Type : Defect
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Description

App secret changes evict cached clients on the node that handled the save, and on no other node. On a cluster, the remaining nodes keep serving clients built with the previous credentials until their cache TTL expires.

AppsAPIImpl.save publishes the change with localSystemEventsAPI.asyncNotify(new AppSecretSavedEvent(...)). LocalSystemEventsAPI is node-local — there is no cluster propagation. Every subscriber therefore reacts on one node only:

  • AIAppListenerLangChain4jAIClient.flushCachesForHost(...), which clears the chat, streaming-chat, embedding and image model caches (Caffeine, maximumSize(128), 1-hour TTL, keyed by host + ":" + providerConfigHash)
  • AnalyticsAppListener
  • ContentAnalyticsAppListener

Impact. After a credential rotation in the Apps portlet, a request routed to any other node can still be served by a model holding the old key for up to an hour. Where the rotation was a response to a leaked or revoked key, the revoked key stays in use on those nodes for that window. It also makes rotation non-deterministic to verify: whether the new key is in effect depends on which node answered.

Acceptance Criteria
  • An app secret change invalidates the affected caches on every node in the cluster, not only the node that handled the save
  • Covers all three current subscribers (AIAppListener, AnalyticsAppListener, ContentAnalyticsAppListener), not just the AI one
  • Invalidation is scoped to the affected host rather than flushing every entry
  • A test demonstrates that a secret change on one node evicts on another
Additional Context
  • Found during review of #37559 by @wezell, who asked whether these caches need cluster-wide invalidation. They do.
  • Pre-existing behaviour — not introduced by #37559. That PR reuses the existing listener and flush deliberately, so that a second cache could not keep serving a revoked key; the :inference cache keys it adds are covered by the existing host-prefix flush.
  • AppsAPIImpl publish site: localSystemEventsAPI.asyncNotify(new AppSecretSavedEvent(...))
  • Flush implementation: LangChain4jAIClient.flushCachesForHost(String)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the AppsAPIImpl publish site and trace LocalSystemEventsAPI, AppSecretSavedEvent, AIAppListener, AnalyticsAppListener, ContentAnalyticsAppListener, and LangChain4jAIClient.flushCachesForHost(String). Review how events cross cluster nodes and how the host-scoped caches are flushed. Done means a secret change on one node invalidates the affected caches on another, all three subscribers are covered, and a test demonstrates this behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.