8000 New Infinispan node joining the cluster becomes a command target/coordinator before its cache components are ready - ISPN000136 failures during Keycloak rolling restarts · Issue #52088 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

New Infinispan node joining the cluster becomes a command target/coordinator before its cache components are ready - ISPN000136 failures during Keycloak rolling restarts #52088

Closed

Description

@corujito

Before reporting an issue

  • I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

Area

infinispan

Describe the bug

Running Keycloak 26.6.4 (Infinispan 16.0.8), embedded caching, cache-stack=jdbc-ping, on Kubernetes with a canary rolling-update strategy (4 replicas). During every rolling deploy, new pods join the JGroups/Infinispan cluster at JVM startup - independent of and before Kubernetes readiness. Once joined, Infinispan begins reassigning ownership of clustered cache segments (actionTokens, authenticationSessions) to the new members while state transfer is still in progress. During this window we see real ISPN000136 command failures (PutKeyValueCommand/GetKeyValueCommand) logged by the new pods themselves, e.g.:

ISPN000136: Error executing command PutKeyValueCommand on Cache 'actionTokens', writing keys [refreshLock::]

Some instances carry org.infinispan.commons.TimeoutException at CommandAckCollector$BaseCollector.call(), suggesting the new pod may itself be coordinating the write and timing out waiting for an ack from another (possibly also still-syncing) owner - though we're not fully certain of the exact RPC direction, which is part of why we're asking here.

We confirmed this is not a Kubernetes-readiness problem. We tried delaying the readiness probe (initialDelaySeconds) so new pods wouldn't receive external HTTP traffic until well after our observed rebalance window, and the failures reproduced identically. Cluster membership and cache-topology reassignment happen at the Infinispan/JGroups level regardless of the Kubernetes probe state, so gating HTTP traffic to the new pod doesn't touch the actual mechanism.

We'd appreciate help understanding: (a) whether a newly-joined node is expected to become a command target/coordinator for these caches before its own cache components have completed startup, and (b) whether there's a supported way to delay a node's inclusion in the consistent hash until it's actually able to serve as an owner - analogous to how 26.7.0 now blocks shutdown until topology is stable (#44620), but for the join side.

Version

26.6.4

Regression

  • The issue is a regression

Expected behavior

A node joining the cluster should not become a target for cache commands on segments it hasn't yet received, and/or in-flight commands touching a segment mid-transfer should not fail/timeout - regardless of Kubernetes readiness state.

Actual behavior

New pods produce real ISPN000136 write/read failures in the 1-3 minutes after joining, visible to end users as failed token refresh / broken login.

How to Reproduce?

  1. Run Keycloak with KC_CACHE=ispn, KC_CACHE_STACK=jdbc-ping, 4+ replicas, embedded caching, behind a Kubernetes rolling/canary deploy.
  2. Trigger a rolling update replacing all replicas.
  3. Observe logs during rollout: ISPN000136 errors on actionTokens/authenticationSessions correlated with pods that just joined.
  4. We ruled out readiness-probe timing as a factor - delaying initialDelaySeconds well past the observed rebalance window did not prevent the failures.
  5. In our environment, a single rollout produced 500+ such failures over roughly 60-100 seconds, reproduced across three separate rollouts over several days.

Anything else?

Related/prior art:

Activity

  1. added theissue type on Aug 26, 2026
  2. krzysztofpozoga commented on Sep 2, 2026

    @krzysztofpozoga

    I want to follow up on this issue:

    Every rolling deployment (and separately any Kubernetes-initiated node replacement that recycles a pod outside of a deploy) has a window where surviving/new pods return real, client-facing 500s on POST /token. We've traced this back across multiple occurrences over several months, most recently ~425 real errors (84% on /token) in a single deployment. We are using rolling deployment strategy with 4 replicas, numOwners at default (floored to 2 for the affected caches) with persistent-user-sessions feature enabled but per CacheConfigurator, this does not cover actionTokens/authenticationSessions/loginFailures.

    Observed log signatures, always together:

    • ISPN000427: Timeout after 15 seconds waiting for acks — the stock remote-timeout default
    • ISPN000136: cache write/read failure on actionTokens/authenticationSessions
    • ISPN000208: No live owners found for segments ... of cache authenticationSessions — during the departing-pod window
    • ISPN000299: Unable to acquire lock after 10 seconds for key refreshLock:... — downstream of the above

    Traced the exact code path for the ISPN000427 case: RefreshTokenGrantType.process → TokenManager.refreshAccessToken → createTemporaryExclusiveLockForTokenRefreshOperation → InfinispanSingleUseObjectProvider.putIfAbsent on actionTokens.

    What we've already tried in our own deployment (all still in place, none fully close the gap alone)

    1. Extended the pod's graceful-shutdown period so JGroups gets to broadcast LEAVE before termination — fixed the shutdown-side race in isolation, but a startup-side race (see below) remained.
    2. Adjusted our rollout strategy so old pods are replaced one at a time rather than all at once — reduced blast radius but did not eliminate real errors under live testing.
    3. Standard Kubernetes protections against non-deploy-triggered node churn (PodDisruptionBudget, eviction protection) — addresses only the non-deploy node-churn trigger, not deploys themselves.
    4. A custom rebalance-aware readiness check was designed but abandoned before implementation: gating the new pod's own readiness doesn't help, because the disruptive writes come from other already-ready pods routing cache traffic to the new member
      over JGroups — a control plane entirely separate from the Kubernetes readiness gate.

    So none of the above touch the actual ack-wait: the stock remote-timeout="15000" on these three caches is tight enough that, during active rebalancing right around pod departure, real writes still exceed it.

    The two paths we're considering and we would like your input before doing anything:

    1. Tune remote-timeout (and add a state-transfer timeout) for actionTokens/authenticationSessions/loginFailures via a custom cache-ispn.xml

    We can replicate what CacheConfigurator.getCacheConfiguration() already sets programmatically (DIST_SYNC mode, APPLICATION_OBJECT_TYPE encoding) so we're not silently changing anything else — but we're hesitant to ship a hand-maintained cache topology definition that has to keep tracking Keycloak's internal defaults release over release, and we can't validate it under real production-scale load in a lower environment.

    • Is tuning remote-timeout for these three specific caches something that is considered safe and intended for this use case (rolling Kubernetes deployments), or does it risk assumptions made elsewhere in the codebase?
    • Would Keycloak consider exposing this as a first-class, documented config option (e.g. an env var) rather than requiring a raw Infinispan XML override, given it's already effectively necessary for any HA Kubernetes deployment doing rolling updates?

    2. Externalize these caches to a remote/external Infinispan cluster instead of embedded, accepting the operational cost, so cache membership is decoupled from Keycloak pod lifecycle entirely

    • Is this the direction the project would recommend for exactly this failure mode? Any known caveats specific to Jump to bottom (click) ↓ ationSessions/loginFailures(all in-memory-only, never DB-backed even withpersistent-user-sessions`)?
  3. self-assigned this
    on Sep 3, 2026
  4. ahus1 commented on Sep 3, 2026

    @ahus1
    Member

    Sorry to hear this is causing you problems, and thanks for reaching out.

    The recommendation is to use a stateful set for deploying Keycloak, which will enforce the one-pod-rollout-at-a-time. This also ensures that no data is lost, as the caches for authentication sessions have two owners.

    Q: Can you please check the startup log of your nodes - does it state that virtual threads are enabled? Please share the startup-log of your node here.

    Q: As you're mentioning the loginFailures cache - how many entries does your cache have, and how much time does the rebalancing take? Do you have persistent user sessions enabled? Do you use offline sessions?

    In general: With 26.6, once a node joins, it is part of Infinispan read/write requests. While the rebalancing is in progress, a node will target both the old node and the new node for fetching data, so there should be no error shown to the user.

    Q: You reported errors in the log. To double check: Did you see also user facing errors (like 500 errors in the HTTP server log, or prolonged response times)?

    The graceful shutdown in any Keycloak release depends on Keycloak receiving the signal. If you build your custom containers, with possibly your own entrypoint, see https://www.keycloak.org/server/containers#custom-entrypoint-shell-scripts for how to write them in a way that Keycloak receives the signals.

    Q: To verify the clean shutdown, please share the shutdown log here.

    Now to your other questions how we think about the future here:

  5. pruivo commented on Sep 3, 2026

    @pruivo
    Member

    behind a Kubernetes rolling/canary deploy.

    @corujito, what does this mean? Are you using the Keycloak Operator or other 3rd party tool? If it is deployed as a Deployment instead of a StatefulSet, it explains the errors.

    Please share your configuration and the logs from all Pods during the event. Thank you.

  6. corujito commented on Sep 4, 2026

    @corujito
    Author

    @ahus1 @pruivo
    Thanks for helping on this.

    I shared shutdown/startup logs via email that probably answers most of the questions.

    Virtual threads support: disabled
    persistent user sessions enabled
    rebalance happens in milliseconds

    Not a StatefulSet - we use an Argo Rollouts canary with dynamicStableScale: true, which scales the old ReplicaSet down as the new one scales up. With 4 replicas, in practice this increments one pod at a time, but it's not strictly serialized like a StatefulSet's OrderedReady.

  7. ahus1 commented on Sep 6, 2026

    @ahus1
    Member

    Thank you for the log files. I did some drilling with the help of AI and this is what I think is happening - let's confirm with @pruivo next week:

    1. There seems to be a race condition on shutdown: When node shuts down, it cache leaves gracefully with the leader, but the new view might not be present on all other nodes immediately. Still the cache terminates, and some requests might arrive at the terminated cache, leading to a 15sec timeout for those requests.
    2. The timeout of 15s is larger than the 10s retry timeout for locking a refresh token as you indicated above. The errors are a consequence of (1).
    3. There is one more error message on the terminating node that might happens when Keycloak processes a HTTP request when the caches have already been shut down.

    For (1), I'll submit a fix that we'll backport for 26.7, but there are no community releases for 26.6 any more. You'll benefit from it once a patch release is available and you upgrade. While this is not available, the next paragraph should provide you a workaround.

    For (2), I'll submit a fix with a default of 3 seconds remote timeout. As you suggested above, you can override this setting via the XML file. Once you upgrade, this override should no longer be necessary.

    For (3), I assume you're might be using TLS Passthrough? Or your loadbalancer takes a bit longer than 1 second to take the Keycloak Pod out of rotation? I suggest to enlarge shutdown-delay to 10 or 30 seconds (see https://www.keycloak.org/server/reverseproxy.html#_when_to_adjust_shutdown_timeouts) to allow for a cleaner shutdown.

    The message ISPN000312 seems to be noise in the case of Keycloak's distributed caches with two owners, I'll see that we'll suppress it.

  8. 1 remaining item

  9. added a commit that references this issue on Sep 8, 2026
    5e7ba7e
  10. ahus1 commented on Sep 8, 2026

    @ahus1
    Member

    @corujito - the change is now in main. I don't think the change will make it into the next 26.7 patch release, but the one after.

    Note that after feedback from @pruivo I updated also the lock timeout.

    Sorry for this bug, and thanks for reporting it with a clear description together with anonymized log files that allowed us to hunt this down and make Keycloak better for everyone.

  11. added 2 commits that reference this issue on Sep 18, 2026
    7832450
    c152ea4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Fields

No fields configured for bug.

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    0