seokmo-yoo-dev commented on PR #23393:
URL: https://github.com/apache/kafka/pull/23393#issuecomment-5568412628

   Some detail that did not belong in the commit message.
   
   The observed failure, from a Flink session cluster where each job gets its 
own
   class loader:
   
   ```
   javax.security.auth.login.LoginException: java.lang.IllegalStateException: 
Trying to access closed classloader
        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:691)
        at javax.security.auth.login.LoginContext.login(LoginContext.java:587)
        at 
org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin.reLogin(ExpiringCredentialRefreshingLogin.java:390)
   ```
   
   The sequence:
   
   1. Deployment A creates a client. Its `LoginManager` is cached and the 
refresh
      thread starts, inheriting A's context class loader because `KafkaThread` 
does
      not set one.
   2. Deployment B creates a client with the same JAAS configuration, receives 
A's
      `LoginManager`, and raises the reference count to two.
   3. A stops and releases its reference. The count drops to one, so
      `login.close()` never runs and the thread keeps going with A's class 
loader.
   4. The container discards A's class loader. `LoginContext` captures the 
context
      class loader at construction and resolves login modules through
      `ServiceLoader`, and `reLogin()` builds its `LoginContext` on that same
      thread, so the class loader never changes and every retry fails 
identically.
   5. The credential expires and clients that need to re-authenticate are 
rejected.
   
   The last step lags the rest, since authentication happens only when a 
connection
   is established. Open connections keep working with an expired token, and 
which
   client breaks first depends on reconnection timing, which makes this hard to
   trace back to the redeployment that caused it.
   
   On why the key rather than somewhere else, and a question for reviewers: the 
key
   has been widened twice before, by KIP-86 for the login and callback classes 
and
   by KAFKA-14676 for the `sasl.*` configs, each time after a dimension that 
must
   not be shared turned out to be missing from it. The class loader looks like 
the
   same pattern to me, but KIP-83 says only that caching is "keyed on the jaas
   configuration object" and does not mention class loaders, so I could not 
settle
   from the design documents whether sharing across class loaders was intended. 
If
   it was, the fix belongs elsewhere, most likely in having
   `ExpiringCredentialRefreshingLogin` set the context class loader on its own
   thread rather than inheriting one.
   
   On retention, since the key now holds a class loader: the reference lives 
only
   as long as the cache entry, and `release()` removes the entry when the last
   caller lets go. Before this change a leaked entry retained the class loader
   anyway through the live refresh thread, which was the defect.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to