Skip to content

Commit 97a0a9c

Browse files
authored
Merge pull request #1 from nicholsl/feat/add-async-channel-ping
feat/add-async-channel-ping add an async channel pinging method to be used in channel health checking
2 parents 1693818 + b4cf91a commit 97a0a9c

2 files changed

Lines changed: 83 additions & 34 deletions

File tree

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableChannelPrimer.java

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,43 +96,12 @@ public void primeChannel(ManagedChannel managedChannel) {
9696
}
9797

9898
private void primeChannelUnsafe(ManagedChannel managedChannel) throws IOException {
99-
sendPrimeRequests(managedChannel);
99+
sendPrimeRequestsBlocking(managedChannel);
100100
}
101101

102-
private void sendPrimeRequests(ManagedChannel managedChannel) {
102+
private void sendPrimeRequestsBlocking(ManagedChannel managedChannel) {
103103
try {
104-
ClientCall<PingAndWarmRequest, PingAndWarmResponse> clientCall =
105-
managedChannel.newCall(
106-
BigtableGrpc.getPingAndWarmMethod(),
107-
CallOptions.DEFAULT
108-
.withCallCredentials(callCredentials)
109-
.withDeadline(Deadline.after(1, TimeUnit.MINUTES)));
110-
111-
SettableApiFuture<PingAndWarmResponse> future = SettableApiFuture.create();
112-
clientCall.start(
113-
new ClientCall.Listener<PingAndWarmResponse>() {
114-
PingAndWarmResponse response;
115-
116-
@Override
117-
public void onMessage(PingAndWarmResponse message) {
118-
response = message;
119-
}
120-
121-
@Override
122-
public void onClose(Status status, Metadata trailers) {
123-
if (status.isOk()) {
124-
future.set(response);
125-
} else {
126-
future.setException(status.asException());
127-
}
128-
}
129-
},
130-
createMetadata(headers, request));
131-
clientCall.sendMessage(request);
132-
clientCall.halfClose();
133-
clientCall.request(Integer.MAX_VALUE);
134-
135-
future.get(1, TimeUnit.MINUTES);
104+
sendPrimeRequestsAsync(managedChannel).get(1, TimeUnit.MINUTES);
136105
} catch (Throwable e) {
137106
// TODO: Not sure if we should swallow the error here. We are pre-emptively swapping
138107
// channels if the new
@@ -141,6 +110,53 @@ public void onClose(Status status, Metadata trailers) {
141110
}
142111
}
143112

113+
public SettableApiFuture<PingAndWarmResponse> sendPrimeRequestsAsync(
114+
ManagedChannel managedChannel) {
115+
ClientCall<PingAndWarmRequest, PingAndWarmResponse> clientCall =
116+
managedChannel.newCall(
117+
BigtableGrpc.getPingAndWarmMethod(),
118+
CallOptions.DEFAULT
119+
.withCallCredentials(callCredentials)
120+
.withDeadline(Deadline.after(1, TimeUnit.MINUTES)));
121+
122+
SettableApiFuture<PingAndWarmResponse> future = SettableApiFuture.create();
123+
clientCall.start(
124+
new ClientCall.Listener<PingAndWarmResponse>() {
125+
private PingAndWarmResponse response;
126+
127+
@Override
128+
public void onMessage(PingAndWarmResponse message) {
129+
response = message;
130+
}
131+
132+
@Override
133+
public void onClose(Status status, Metadata trailers) {
134+
if (status.isOk()) {
135+
future.set(response);
136+
} else {
137+
// Propagate the gRPC error to the future.
138+
future.setException(status.asException(trailers));
139+
}
140+
}
141+
},
142+
createMetadata(headers, request));
143+
144+
try {
145+
// Send the request message.
146+
clientCall.sendMessage(request);
147+
// Signal that no more messages will be sent.
148+
clientCall.halfClose();
149+
// Request the response from the server.
150+
clientCall.request(Integer.MAX_VALUE);
151+
} catch (Throwable t) {
152+
// If sending fails, cancel the call and notify the future.
153+
clientCall.cancel("Failed to send priming request", t);
154+
future.setException(t);
155+
}
156+
157+
return future;
158+
}
159+
144160
private static Metadata createMetadata(Map<String, String> headers, PingAndWarmRequest request) {
145161
Metadata metadata = new Metadata();
146162

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/BigtableChannelPrimerTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
package com.google.cloud.bigtable.data.v2.stub;
1717

1818
import static com.google.common.truth.Truth.assertThat;
19+
import static org.junit.Assert.assertThrows;
1920

2021
import com.google.api.core.ApiFunction;
22+
import com.google.api.core.SettableApiFuture;
2123
import com.google.auth.oauth2.AccessToken;
2224
import com.google.auth.oauth2.OAuth2Credentials;
2325
import com.google.bigtable.v2.BigtableGrpc.BigtableImplBase;
@@ -39,6 +41,8 @@
3941
import java.io.IOException;
4042
import java.util.Queue;
4143
import java.util.concurrent.ConcurrentLinkedQueue;
44+
import java.util.concurrent.ExecutionException;
45+
import java.util.concurrent.TimeUnit;
4246
import java.util.logging.Handler;
4347
import java.util.logging.LogRecord;
4448
import java.util.logging.Logger;
@@ -166,6 +170,35 @@ public void testHeadersAreSent() {
166170
}
167171
}
168172

173+
// New test for the async success path
174+
@Test
175+
public void testAsyncSuccess() throws Exception {
176+
SettableApiFuture<PingAndWarmResponse> future = primer.sendPrimeRequestsAsync(channel);
177+
178+
PingAndWarmResponse response = future.get(1, TimeUnit.SECONDS);
179+
assertThat(response).isNotNull();
180+
assertThat(future.isDone()).isTrue();
181+
}
182+
183+
// New test for the async failure path
184+
@Test
185+
public void testAsyncFailure() {
186+
// Configure the server to return a gRPC error
187+
fakeService.pingAndWarmCallback =
188+
new ApiFunction<PingAndWarmRequest, PingAndWarmResponse>() {
189+
@Override
190+
public PingAndWarmResponse apply(PingAndWarmRequest pingAndWarmRequest) {
191+
throw new StatusRuntimeException(Status.UNAVAILABLE);
192+
}
193+
};
194+
195+
SettableApiFuture<PingAndWarmResponse> future = primer.sendPrimeRequestsAsync(channel);
196+
197+
ExecutionException e =
198+
assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS));
199+
assertThat(e).hasCauseThat().hasMessageThat().contains("UNAVAILABLE");
200+
}
201+
169202
private static class MetadataInterceptor implements ServerInterceptor {
170203
ConcurrentLinkedQueue<Metadata> metadataList = new ConcurrentLinkedQueue<>();
171204

0 commit comments

Comments
 (0)