Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 27, 2026

Issue describing the changes in this PR

DurableClientContext creates a new gRPC client per function invocation but never closes the underlying channel, causing resource leaks and "ManagedChannel was not shutdown properly" warnings.

Changes

Made DurableClientContext implement AutoCloseable:

  • Added close() method that properly shuts down the underlying DurableTaskClient
  • Made close() idempotent by nulling the client reference after shutdown
  • Added synchronized to both getClient() and close() for thread safety

Fixed getClient() to prevent multiple client instantiations:

  • Now caches and reuses client instance instead of creating new one on each call
  • Eliminates additional channel leaks from repeated calls

Usage:

// Functions worker can now use try-with-resources
try (DurableClientContext context = ...) {
    DurableTaskClient client = context.getClient();
    // ... use client
} // channel properly closed

// Or explicit cleanup
DurableClientContext context = ...;
try {
    // ... use context
} finally {
    context.close();
}

Pull request checklist

  • My changes do not require documentation changes
  • My changes are added to the CHANGELOG.md
  • I have added all required tests (Unit tests, E2E tests)
    • Note: No existing test infrastructure in azurefunctions module

Additional information

  • Backward compatible - existing code without close() calls continues to work
  • CodeQL security scan: 0 alerts
  • Addresses customer-reported warnings from gRPC channel orphaning

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • rt.services.visualstudio.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>DurableClientContext gRPC channels are never closed after function invocation</issue_title>
<issue_description>## Summary
Each function invocation creates a new DurableClientContext with its own gRPC client/channel. When the invocation ends, close() is never called, leaving the channel orphaned.

Customer Impact

Customers see repeated warnings in logs:

 Previous channel ManagedChannelImpl{logId=X, target=localhost:4001} was not shutdown properly!!!
java.lang.RuntimeException: ManagedChannel allocation site 

at io.grpc.internal.ManagedChannelOrphanWrapper$ManagedChannelReference.<init>(ManagedChannelOrphanWrapper.java:102) 

Repro

Any Java Durable Functions app using DurableClientContext.getClient() will eventually see these warnings.

Root Cause

DurableClientContext does not implement AutoCloseable and never calls client.close().
PR #249 added proper shutdown logic to DurableTaskGrpcClient.close(), but that method is never called because:

  1. DurableClientContext does not implement AutoCloseable
  2. DurableClientContext has no close() method
  3. The Java Functions worker has no mechanism to close the client after invocation

Suggested fixes

  1. Close the client after each invocation — Make DurableClientContext implement AutoCloseable and have the Java worker call close() when the invocation ends

  2. Share one client across all invocations — Use a static singleton so only one gRPC channel is ever created per worker process</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits January 27, 2026 16:36
Co-authored-by: torosent <17064840+torosent@users.noreply.github.com>
Co-authored-by: torosent <17064840+torosent@users.noreply.github.com>
Co-authored-by: torosent <17064840+torosent@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix gRPC channels not closing after function invocation Fix gRPC channel leak in DurableClientContext Jan 27, 2026
Copilot AI requested a review from torosent January 27, 2026 16:46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add tests for this change?

@torosent torosent closed this Jan 28, 2026
@torosent torosent deleted the copilot/fix-grpc-channel-leak branch January 28, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DurableClientContext gRPC channels are never closed after function invocation

3 participants