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:
DurableClientContext does not implement AutoCloseable
DurableClientContext has no close() method
- The Java Functions worker has no mechanism to close the client after invocation
Suggested fixes
-
Close the client after each invocation — Make DurableClientContext implement AutoCloseable and have the Java worker call close() when the invocation ends
-
Share one client across all invocations — Use a static singleton so only one gRPC channel is ever created per worker process
Summary
Each function invocation creates a new
DurableClientContextwith 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:
Repro
Any Java Durable Functions app using
DurableClientContext.getClient()will eventually see these warnings.Root Cause
DurableClientContextdoes not implementAutoCloseableand never callsclient.close().PR #249 added proper shutdown logic to
DurableTaskGrpcClient.close(), but that method is never called because:DurableClientContextdoes not implementAutoCloseableDurableClientContexthas noclose()methodSuggested fixes
Close the client after each invocation — Make
DurableClientContextimplementAutoCloseableand have the Java worker callclose()when the invocation endsShare one client across all invocations — Use a static singleton so only one gRPC channel is ever created per worker process