Repository navigation
Conversation
There was a problem hiding this comment.
Code Review
This pull request reverts the mTLS certificate rotation and channel refreshing features across several libraries, including the removal of helper utilities like AgentIdentityUtils and RefreshingHttpJsonChannel, and simplifies the channel pool and call context classes. Feedback on these changes highlights several critical resource leak vulnerabilities in ChannelPool due to missing exception handling and unreleased entries during early cancellations. Additionally, unconditionally shutting down the HttpTransport in ManagedHttpJsonChannel poses a risk of prematurely closing shared, user-managed transports.
| Entry entry = getRetainedEntry(affinity); | ||
| try { | ||
| return new ReleasingClientCall<>( | ||
| entry.channel.newCall(methodDescriptor, callOptions), entry); | ||
| } catch (Throwable t) { | ||
| entry.release(); | ||
| throw t; | ||
| } | ||
|
|
||
| return new ReleasingClientCall<>(entry.channel.newCall(methodDescriptor, callOptions), entry); |
There was a problem hiding this comment.
Reverting the try-catch block here reintroduces a potential resource leak. If entry.channel.newCall(methodDescriptor, callOptions) throws a RuntimeException or Error (for example, due to an invalid state or interceptor failure), the acquired entry (via getRetainedEntry) will never be released, leading to a channel entry leak.
Entry entry = getRetainedEntry(affinity);
try {
return new ReleasingClientCall<>(
entry.channel.newCall(methodDescriptor, callOptions), entry);
} catch (Throwable t) {
entry.release();
throw t;
}References
- Catching
Throwableinstead ofExceptionis acceptable at Future boundaries or during stream cleanup in asynchronous operations to ensure all failures are captured and propagated.
| if (cancellationException != null) { | ||
| throw new IllegalStateException("Call is already cancelled", cancellationException); | ||
| } |
There was a problem hiding this comment.
If the call is cancelled before start() is called, cancellationException will be non-null. Throwing IllegalStateException here without releasing the acquired entry will result in a silent resource leak of the channel entry, as onClose() will never be invoked to release it.
if (cancellationException != null) {
if (wasReleased.compareAndSet(false, true)) {
entry.release();
}
throw new IllegalStateException("Call is already cancelled", cancellationException);
}| } catch (Exception e) { | ||
| // In case start failed, make sure to release | ||
| if (wasReleased.compareAndSet(false, true)) { | ||
| entry.release(); | ||
| } else { | ||
| LOG.log( | ||
| Level.WARNING, | ||
| "The entry is already released. This indicates that onClose() has already been called previously"); | ||
| } | ||
| throw e; | ||
| } |
There was a problem hiding this comment.
Catching only Exception here will miss any Throwable that is an Error (such as LinkageError, NoClassDefFoundError, or AssertionError) thrown during super.start(). If an Error is thrown, the entry will not be released, leading to a resource leak. It is safer to catch Throwable to ensure the entry is always released on any failure.
| } catch (Exception e) { | |
| // In case start failed, make sure to release | |
| if (wasReleased.compareAndSet(false, true)) { | |
| entry.release(); | |
| } else { | |
| LOG.log( | |
| Level.WARNING, | |
| "The entry is already released. This indicates that onClose() has already been called previously"); | |
| } | |
| throw e; | |
| } | |
| } catch (Throwable t) { | |
| // In case start failed, make sure to release | |
| if (wasReleased.compareAndSet(false, true)) { | |
| entry.release(); | |
| } else { | |
| LOG.log( | |
| Level.WARNING, | |
| "The entry is already released. This indicates that onClose() has already been called previously"); | |
| } | |
| throw t; | |
| } |
References
- Catching
Throwableinstead ofExceptionis acceptable at Future boundaries or during stream cleanup in asynchronous operations to ensure all failures are captured and propagated.
| if (usingDefaultTransport) { | ||
| httpTransport.shutdown(); | ||
| } | ||
| httpTransport.shutdown(); |
There was a problem hiding this comment.
Unconditionally calling httpTransport.shutdown() here will shut down any custom HttpTransport provided by the user (e.g., via the builder). If the user shares the HttpTransport instance across multiple clients or manages its lifecycle externally, shutting it down here will cause other clients to fail. It is safer to only shut down the transport if it was created by default by the channel itself.
DO NOT MERGE. Diagnosis only; not a proposal to revert #14668 on main.
Purpose: check whether the Storage Integration hang (ITAppendableUploadTest, ~3h Kokoro timeout since Oct 19) is related to #14668.
Contents:
Expected: if Storage ITs finish normally with no WATCHDOG output, #14668 is implicated; if they still hang, it is not.