Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit 2c9da0d

Browse files
committed
feat: add ChannelFinder server interfaces
This commit adds the server abstraction interfaces for location-aware routing: - ChannelFinderServer: Interface representing a Spanner server endpoint with address, health check, and channel access - ChannelFinderServerFactory: Factory interface for creating and caching server connections - GrpcChannelFinderServerFactory: gRPC implementation that creates and manages gRPC channels for different server endpoints These interfaces enable the client to maintain connections to multiple Spanner servers and route requests directly to the appropriate server based on key location information. This is part of the experimental location-aware routing for improved latency.
1 parent e3fa634 commit 2c9da0d

3 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner.spi.v1;
18+
19+
import io.grpc.ManagedChannel;
20+
21+
/** Represents a Spanner server endpoint for location-aware routing. */
22+
public interface ChannelFinderServer {
23+
String getAddress();
24+
25+
boolean isHealthy();
26+
27+
ManagedChannel getChannel(); // Added to get the underlying channel for RPC calls
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner.spi.v1;
18+
19+
/** Factory for creating and caching server connections for location-aware routing. */
20+
public interface ChannelFinderServerFactory {
21+
ChannelFinderServer defaultServer();
22+
23+
ChannelFinderServer create(String address);
24+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner.spi.v1;
18+
19+
import com.google.api.gax.grpc.GrpcTransportChannel;
20+
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
21+
import io.grpc.ManagedChannel;
22+
import java.io.IOException;
23+
import java.util.Map;
24+
import java.util.concurrent.ConcurrentHashMap;
25+
26+
class GrpcChannelFinderServerFactory implements ChannelFinderServerFactory {
27+
private final InstantiatingGrpcChannelProvider.Builder channelBuilder;
28+
private final Map<String, GrpcChannelFinderServer> servers = new ConcurrentHashMap<>();
29+
private final GrpcChannelFinderServer defaultServer;
30+
31+
public GrpcChannelFinderServerFactory(InstantiatingGrpcChannelProvider.Builder channelBuilder)
32+
throws IOException {
33+
this.channelBuilder = channelBuilder;
34+
// The "default" server will use the original endpoint from the builder.
35+
this.defaultServer =
36+
new GrpcChannelFinderServer(this.channelBuilder.getEndpoint(), channelBuilder.build());
37+
this.servers.put(this.defaultServer.getAddress(), this.defaultServer);
38+
}
39+
40+
@Override
41+
public ChannelFinderServer defaultServer() {
42+
return defaultServer;
43+
}
44+
45+
@Override
46+
public ChannelFinderServer create(String address) {
47+
return servers.computeIfAbsent(
48+
address,
49+
addr -> {
50+
try {
51+
// Modify the builder to use the new address
52+
synchronized (channelBuilder) {
53+
InstantiatingGrpcChannelProvider.Builder newBuilder =
54+
channelBuilder.setEndpoint(addr);
55+
return new GrpcChannelFinderServer(addr, newBuilder.build());
56+
}
57+
} catch (IOException e) {
58+
throw new RuntimeException("Failed to create channel for address: " + addr, e);
59+
}
60+
});
61+
}
62+
63+
static class GrpcChannelFinderServer implements ChannelFinderServer {
64+
private final String address;
65+
private final ManagedChannel channel;
66+
67+
public GrpcChannelFinderServer(String address, InstantiatingGrpcChannelProvider provider)
68+
throws IOException {
69+
this.address = address;
70+
// It's assumed that getTransportChannel() returns a ManagedChannel or can be cast to one.
71+
// For this example, GrpcTransportChannel is used as in KeyAwareChannel.
72+
GrpcTransportChannel transportChannel = (GrpcTransportChannel) provider.getTransportChannel();
73+
this.channel = (ManagedChannel) transportChannel.getChannel();
74+
}
75+
76+
// Constructor for the default server that already has a channel
77+
public GrpcChannelFinderServer(String address, ManagedChannel channel) {
78+
this.address = address;
79+
this.channel = channel;
80+
}
81+
82+
@Override
83+
public String getAddress() {
84+
return address;
85+
}
86+
87+
@Override
88+
public boolean isHealthy() {
89+
// A simple health check. In a real scenario, this might involve a ping or other checks.
90+
return !channel.isShutdown() && !channel.isTerminated();
91+
}
92+
93+
@Override
94+
public ManagedChannel getChannel() {
95+
return channel;
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)