Skip to content

Commit 7341810

Browse files
sophiatevSophia TevosyanCopilotCopilot
authored
Add DurableTaskGrpcClientFactory (#256)
* starting new release * removed preview flag * first commit * fixing the build error * fixing the build error * Update azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientContext.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * copilot comments * fixing the build errors * Add unit tests for DurableTaskGrpcClientFactory (#257) * Initial plan * Add unit tests for DurableTaskGrpcClientFactory and fix compilation error Co-authored-by: sophiatev <38052607+sophiatev@users.noreply.github.com> * Remove changes to DurableTaskGrpcClient.java and proto file, keep only test file Co-authored-by: sophiatev <38052607+sophiatev@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sophiatev <38052607+sophiatev@users.noreply.github.com> * fixing the test build errors --------- Co-authored-by: Sophia Tevosyan <stevosyan@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent ecc0124 commit 7341810

File tree

4 files changed

+114
-5
lines changed

4 files changed

+114
-5
lines changed

azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientContext.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import com.microsoft.azure.functions.HttpStatus;
1010
import com.microsoft.durabletask.DurableTaskClient;
11-
import com.microsoft.durabletask.DurableTaskGrpcClientBuilder;
11+
import com.microsoft.durabletask.DurableTaskGrpcClientFactory;
1212
import com.microsoft.durabletask.OrchestrationMetadata;
1313
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
1414

@@ -45,6 +45,10 @@ public String getTaskHubName() {
4545
* @return the Durable Task client object associated with the current function invocation.
4646
*/
4747
public DurableTaskClient getClient() {
48+
if (this.client != null) {
49+
return this.client;
50+
}
51+
4852
if (this.rpcBaseUrl == null || this.rpcBaseUrl.length() == 0) {
4953
throw new IllegalStateException("The client context wasn't populated with an RPC base URL!");
5054
}
@@ -56,7 +60,7 @@ public DurableTaskClient getClient() {
5660
throw new IllegalStateException("The client context RPC base URL was invalid!", ex);
5761
}
5862

59-
this.client = new DurableTaskGrpcClientBuilder().port(rpcURL.getPort()).build();
63+
this.client = DurableTaskGrpcClientFactory.getClient(rpcURL.getPort(), null);
6064
return this.client;
6165
}
6266

@@ -78,9 +82,7 @@ public HttpResponseMessage waitForCompletionOrCreateCheckStatusResponse(
7882
HttpRequestMessage<?> request,
7983
String instanceId,
8084
Duration timeout) {
81-
if (this.client == null) {
82-
this.client = getClient();
83-
}
85+
this.client = getClient();
8486
OrchestrationMetadata orchestration;
8587
try {
8688
orchestration = this.client.waitForInstanceCompletion(instanceId, timeout, true);

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ public final class DurableTaskGrpcClient extends DurableTaskClient {
6161
this.sidecarClient = TaskHubSidecarServiceGrpc.newBlockingStub(sidecarGrpcChannel);
6262
}
6363

64+
DurableTaskGrpcClient(int port, String defaultVersion) {
65+
this.dataConverter = new JacksonDataConverter();
66+
this.defaultVersion = defaultVersion;
67+
68+
// Need to keep track of this channel so we can dispose it on close()
69+
this.managedSidecarChannel = ManagedChannelBuilder
70+
.forAddress("localhost", port)
71+
.usePlaintext()
72+
.build();
73+
this.sidecarClient = TaskHubSidecarServiceGrpc.newBlockingStub(this.managedSidecarChannel);
74+
}
75+
6476
/**
6577
* Closes the internally managed gRPC channel, if one exists.
6678
* <p>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.durabletask;
5+
6+
import java.util.concurrent.ConcurrentHashMap;
7+
import java.util.concurrent.ConcurrentMap;
8+
9+
public final class DurableTaskGrpcClientFactory {
10+
private static final ConcurrentMap<Integer, DurableTaskGrpcClient> portToClientMap = new ConcurrentHashMap<>();
11+
12+
// Private to prevent instantiation and enforce a singleton pattern
13+
private DurableTaskGrpcClientFactory() {
14+
}
15+
16+
public static DurableTaskClient getClient(int port, String defaultVersion) {
17+
return portToClientMap.computeIfAbsent(port, p -> new DurableTaskGrpcClient(p, defaultVersion));
18+
}
19+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.durabletask;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
/**
11+
* Unit tests for DurableTaskGrpcClientFactory.
12+
*/
13+
public class DurableTaskGrpcClientFactoryTest {
14+
15+
private static final String DEFAULT_VERSION = null;
16+
17+
@Test
18+
void getClient_samePort_returnsSameInstance() {
19+
// Arrange
20+
int port = 5001;
21+
22+
// Act
23+
DurableTaskClient client1 = DurableTaskGrpcClientFactory.getClient(port, DEFAULT_VERSION);
24+
DurableTaskClient client2 = DurableTaskGrpcClientFactory.getClient(port, DEFAULT_VERSION);
25+
26+
// Assert
27+
assertNotNull(client1, "First client should not be null");
28+
assertNotNull(client2, "Second client should not be null");
29+
assertSame(client1, client2, "getClient should return the same instance for the same port");
30+
}
31+
32+
@Test
33+
void getClient_differentPorts_returnsDifferentInstances() {
34+
// Arrange
35+
int port1 = 5002;
36+
int port2 = 5003;
37+
38+
// Act
39+
DurableTaskClient client1 = DurableTaskGrpcClientFactory.getClient(port1, DEFAULT_VERSION);
40+
DurableTaskClient client2 = DurableTaskGrpcClientFactory.getClient(port2, DEFAULT_VERSION);
41+
42+
// Assert
43+
assertNotNull(client1, "Client for port1 should not be null");
44+
assertNotNull(client2, "Client for port2 should not be null");
45+
assertNotSame(client1, client2, "getClient should return different instances for different ports");
46+
}
47+
48+
@Test
49+
void getClient_multiplePorts_maintainsCorrectMapping() {
50+
// Arrange
51+
int port1 = 5004;
52+
int port2 = 5005;
53+
int port3 = 5006;
54+
55+
// Act
56+
DurableTaskClient client1 = DurableTaskGrpcClientFactory.getClient(port1, DEFAULT_VERSION);
57+
DurableTaskClient client2 = DurableTaskGrpcClientFactory.getClient(port2, DEFAULT_VERSION);
58+
DurableTaskClient client3 = DurableTaskGrpcClientFactory.getClient(port3, DEFAULT_VERSION);
59+
60+
// Request the same ports again
61+
DurableTaskClient client1Again = DurableTaskGrpcClientFactory.getClient(port1, DEFAULT_VERSION);
62+
DurableTaskClient client2Again = DurableTaskGrpcClientFactory.getClient(port2, DEFAULT_VERSION);
63+
DurableTaskClient client3Again = DurableTaskGrpcClientFactory.getClient(port3, DEFAULT_VERSION);
64+
65+
// Assert
66+
// Verify each port returns the same instance
67+
assertSame(client1, client1Again, "Port " + port1 + " should return the same instance");
68+
assertSame(client2, client2Again, "Port " + port2 + " should return the same instance");
69+
assertSame(client3, client3Again, "Port " + port3 + " should return the same instance");
70+
71+
// Verify all instances are different from each other
72+
assertNotSame(client1, client2, "Client for port1 and port2 should be different");
73+
assertNotSame(client1, client3, "Client for port1 and port3 should be different");
74+
assertNotSame(client2, client3, "Client for port2 and port3 should be different");
75+
}
76+
}

0 commit comments

Comments
 (0)