2323import static org .junit .Assert .assertTrue ;
2424import static org .junit .Assume .assumeFalse ;
2525
26- import com .google .api .gax .grpc .GrpcInterceptorProvider ;
2726import com .google .cloud .NoCredentials ;
28- import com .google .cloud .grpc .GcpManagedChannel ;
2927import com .google .cloud .spanner .MockSpannerServiceImpl .SimulatedExecutionTime ;
3028import com .google .cloud .spanner .connection .AbstractMockServerTest ;
31- import com .google .common .collect .ImmutableList ;
3229import com .google .spanner .v1 .BatchCreateSessionsRequest ;
3330import com .google .spanner .v1 .BeginTransactionRequest ;
3431import com .google .spanner .v1 .ExecuteSqlRequest ;
35- import com .google .spanner .v1 .SpannerGrpc ;
36- import io .grpc .CallOptions ;
37- import io .grpc .Channel ;
38- import io .grpc .ClientCall ;
39- import io .grpc .ClientInterceptor ;
4032import io .grpc .Context ;
4133import io .grpc .Deadline ;
4234import io .grpc .ManagedChannelBuilder ;
43- import io .grpc .MethodDescriptor ;
4435import io .grpc .Status ;
4536import java .time .Duration ;
46- import java .util .HashMap ;
47- import java .util .HashSet ;
4837import java .util .List ;
49- import java .util .Map ;
5038import java .util .Set ;
5139import java .util .concurrent .Executors ;
5240import java .util .concurrent .ScheduledExecutorService ;
6250
6351@ RunWith (JUnit4 .class )
6452public class RetryOnDifferentGrpcChannelMockServerTest extends AbstractMockServerTest {
65- /** Tracks the logical affinity keys before grpc-gcp routes the request. */
66- private static final Map <String , Set <String >> LOGICAL_AFFINITY_KEYS = new HashMap <>();
67-
68- /** Tracks how many calls explicitly request grpc-gcp affinity unbind. */
69- private static final Map <String , Integer > UNBIND_AFFINITY_CALL_COUNTS = new HashMap <>();
7053
7154 @ BeforeClass
7255 public static void setupAndStartServer () throws Exception {
@@ -82,52 +65,16 @@ public static void removeSystemProperty() {
8265
8366 @ After
8467 public void clearRequests () {
85- LOGICAL_AFFINITY_KEYS .clear ();
86- UNBIND_AFFINITY_CALL_COUNTS .clear ();
8768 mockSpanner .clearRequests ();
8869 mockSpanner .removeAllExecutionTimes ();
8970 }
9071
91- /**
92- * Creates a client interceptor that captures the logical affinity key before grpc-gcp routes the
93- * request. This allows us to verify that retry logic uses distinct logical channel hints, even
94- * when DCP maps them to fewer physical channels.
95- */
96- static GrpcInterceptorProvider createAffinityKeyInterceptorProvider () {
97- return () ->
98- ImmutableList .of (
99- new ClientInterceptor () {
100- @ Override
101- public <ReqT , RespT > ClientCall <ReqT , RespT > interceptCall (
102- MethodDescriptor <ReqT , RespT > method , CallOptions callOptions , Channel next ) {
103- String methodName = method .getFullMethodName ();
104- // Capture the AFFINITY_KEY before grpc-gcp processes it.
105- String affinityKey = callOptions .getOption (GcpManagedChannel .AFFINITY_KEY );
106- if (affinityKey != null ) {
107- synchronized (LOGICAL_AFFINITY_KEYS ) {
108- Set <String > keys =
109- LOGICAL_AFFINITY_KEYS .computeIfAbsent (methodName , k -> new HashSet <>());
110- keys .add (affinityKey );
111- }
112- }
113- if (Boolean .TRUE .equals (
114- callOptions .getOption (GcpManagedChannel .UNBIND_AFFINITY_KEY ))) {
115- synchronized (UNBIND_AFFINITY_CALL_COUNTS ) {
116- UNBIND_AFFINITY_CALL_COUNTS .merge (methodName , 1 , Integer ::sum );
117- }
118- }
119- return next .newCall (method , callOptions );
120- }
121- });
122- }
123-
12472 SpannerOptions .Builder createSpannerOptionsBuilder () {
12573 return SpannerOptions .newBuilder ()
12674 .setProjectId ("my-project" )
12775 .setHost (String .format ("http://localhost:%d" , getPort ()))
12876 .setChannelConfigurator (ManagedChannelBuilder ::usePlaintext )
129- .setCredentials (NoCredentials .getInstance ())
130- .setInterceptorProvider (createAffinityKeyInterceptorProvider ());
77+ .setCredentials (NoCredentials .getInstance ());
13178 }
13279
13380 @ Test
@@ -163,12 +110,6 @@ public void testReadWriteTransaction_retriesOnNewChannel() {
163110 List <BeginTransactionRequest > requests =
164111 mockSpanner .getRequestsOfType (BeginTransactionRequest .class );
165112 assertNotEquals (requests .get (0 ).getSession (), requests .get (1 ).getSession ());
166- // Verify that the retry used 2 distinct logical affinity keys (before grpc-gcp routing).
167- assertEquals (
168- 2 ,
169- LOGICAL_AFFINITY_KEYS
170- .getOrDefault ("google.spanner.v1.Spanner/BeginTransaction" , new HashSet <>())
171- .size ());
172113 }
173114
174115 @ Test
@@ -208,13 +149,6 @@ public void testReadWriteTransaction_stopsRetrying() {
208149 Set <String > sessions =
209150 requests .stream ().map (BeginTransactionRequest ::getSession ).collect (Collectors .toSet ());
210151 assertEquals (numChannels , sessions .size ());
211- // Verify that the retry logic used distinct logical affinity keys (before grpc-gcp routing).
212- // This confirms each retry attempt targeted a different logical channel.
213- assertEquals (
214- numChannels ,
215- LOGICAL_AFFINITY_KEYS
216- .getOrDefault ("google.spanner.v1.Spanner/BeginTransaction" , new HashSet <>())
217- .size ());
218152 }
219153 }
220154
@@ -284,13 +218,6 @@ public void testDenyListedChannelIsCleared() {
284218 // of the first transaction. That fails, the session is deny-listed, the transaction is
285219 // retried on yet another session and succeeds.
286220 assertEquals (numChannels + 1 , sessions .size ());
287- // Verify that the retry logic used distinct logical affinity keys (before grpc-gcp routing).
288- // This confirms each retry attempt targeted a different logical channel.
289- assertEquals (
290- numChannels ,
291- LOGICAL_AFFINITY_KEYS
292- .getOrDefault ("google.spanner.v1.Spanner/BeginTransaction" , new HashSet <>())
293- .size ());
294221 assertEquals (numChannels , mockSpanner .countRequestsOfType (BatchCreateSessionsRequest .class ));
295222 }
296223 }
@@ -316,17 +243,6 @@ public void testSingleUseQuery_retriesOnNewChannel() {
316243 List <ExecuteSqlRequest > requests = mockSpanner .getRequestsOfType (ExecuteSqlRequest .class );
317244 // The requests use the same multiplexed session.
318245 assertEquals (requests .get (0 ).getSession (), requests .get (1 ).getSession ());
319- // Verify that the retry used 2 distinct logical affinity keys (before grpc-gcp routing).
320- assertEquals (
321- 2 ,
322- LOGICAL_AFFINITY_KEYS
323- .getOrDefault ("google.spanner.v1.Spanner/ExecuteStreamingSql" , new HashSet <>())
324- .size ());
325- assertEquals (
326- 2 ,
327- UNBIND_AFFINITY_CALL_COUNTS
328- .getOrDefault (SpannerGrpc .getExecuteStreamingSqlMethod ().getFullMethodName (), 0 )
329- .intValue ());
330246 }
331247
332248 @ Test
@@ -354,17 +270,6 @@ public void testSingleUseQuery_stopsRetrying() {
354270 // Verify that the retry mechanism is working (made numChannels requests).
355271 int totalRequests = mockSpanner .countRequestsOfType (ExecuteSqlRequest .class );
356272 assertEquals (numChannels , totalRequests );
357- // Verify each attempt used a distinct logical affinity key (before grpc-gcp routing).
358- int distinctLogicalKeys =
359- LOGICAL_AFFINITY_KEYS
360- .getOrDefault ("google.spanner.v1.Spanner/ExecuteStreamingSql" , new HashSet <>())
361- .size ();
362- assertEquals (totalRequests , distinctLogicalKeys );
363- assertEquals (
364- totalRequests ,
365- UNBIND_AFFINITY_CALL_COUNTS
366- .getOrDefault (SpannerGrpc .getExecuteStreamingSqlMethod ().getFullMethodName (), 0 )
367- .intValue ());
368273 }
369274 }
370275
0 commit comments