11/*
2- * Copyright 2019 Google LLC
2+ * Copyright 2020 Google LLC
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1414 * limitations under the License.
1515 */
1616
17- package com .google .cloud .spanner . it ;
17+ package com .google .cloud .spanner ;
1818
1919import static com .google .common .truth .Truth .assertThat ;
2020
21- import com .google .cloud .spanner .Database ;
22- import com .google .cloud .spanner .DatabaseAdminClient ;
23- import com .google .cloud .spanner .DatabaseClient ;
24- import com .google .cloud .spanner .InstanceAdminClient ;
25- import com .google .cloud .spanner .IntegrationTestEnv ;
26- import com .google .cloud .spanner .ParallelIntegrationTest ;
27- import com .google .cloud .spanner .ResultSet ;
28- import com .google .cloud .spanner .Spanner ;
29- import com .google .cloud .spanner .SpannerOptions ;
30- import com .google .cloud .spanner .Statement ;
21+ import com .google .api .core .ApiFunction ;
22+ import com .google .cloud .NoCredentials ;
23+ import com .google .cloud .spanner .connection .AbstractMockServerTest ;
3124import com .google .common .base .Stopwatch ;
25+ import com .google .spanner .admin .database .v1 .ListDatabasesResponse ;
26+ import com .google .spanner .admin .instance .v1 .ListInstancesResponse ;
27+ import io .grpc .ManagedChannelBuilder ;
3228import java .util .ArrayList ;
3329import java .util .List ;
30+ import java .util .Set ;
3431import java .util .concurrent .TimeUnit ;
3532import java .util .regex .Pattern ;
36- import org .junit .AfterClass ;
37- import org .junit .BeforeClass ;
38- import org .junit .ClassRule ;
3933import org .junit .Test ;
40- import org .junit .experimental .categories .Category ;
4134import org .junit .runner .RunWith ;
4235import org .junit .runners .JUnit4 ;
4336
44- @ Category (ParallelIntegrationTest .class )
4537@ RunWith (JUnit4 .class )
46- public class ITSpannerOptionsTest {
47- @ ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv ();
48- private static Database db ;
49-
50- @ BeforeClass
51- public static void setUp () throws Exception {
52- db = env .getTestHelper ().createTestDatabase ();
53- }
54-
55- @ AfterClass
56- public static void tearDown () throws Exception {
57- db .drop ();
58- }
59-
38+ public class SpannerOptionsThreadTest extends AbstractMockServerTest {
6039 private static final int NUMBER_OF_TEST_RUNS = 2 ;
61- private static final int DEFAULT_NUM_CHANNELS = 4 ;
62- private static final int NUM_THREADS_PER_CHANNEL = 4 ;
40+ private static final int DEFAULT_NUM_CHANNELS_PER_GAPIC_CLIENT = 4 ;
41+ private static final int NUM_GAPIC_CLIENTS = 4 ;
42+ private static final int NUM_THREADS =
43+ Math .max (
44+ DEFAULT_NUM_CHANNELS_PER_GAPIC_CLIENT * NUM_GAPIC_CLIENTS ,
45+ Runtime .getRuntime ().availableProcessors ());
6346 private static final String SPANNER_THREAD_NAME = "Cloud-Spanner-TransportChannel" ;
6447 private static final String THREAD_PATTERN = "%s-[0-9]+" ;
6548
49+ private final DatabaseId dbId = DatabaseId .of ("p" , "i" , "d" );
50+
51+ @ SuppressWarnings ("rawtypes" )
52+ private SpannerOptions createOptions () {
53+ return SpannerOptions .newBuilder ()
54+ .setProjectId ("p" )
55+ // Set a custom channel configurator to allow http instead of https.
56+ .setChannelConfigurator (
57+ new ApiFunction <ManagedChannelBuilder , ManagedChannelBuilder >() {
58+ @ Override
59+ public ManagedChannelBuilder apply (ManagedChannelBuilder input ) {
60+ input .usePlaintext ();
61+ return input ;
62+ }
63+ })
64+ .setHost ("http://localhost:" + getPort ())
65+ .setCredentials (NoCredentials .getInstance ())
66+ .build ();
67+ }
68+
6669 @ Test
6770 public void testCloseAllThreadsWhenClosingSpanner () throws InterruptedException {
6871 int baseThreadCount = getNumberOfThreadsWithName (SPANNER_THREAD_NAME );
@@ -72,27 +75,23 @@ public void testCloseAllThreadsWhenClosingSpanner() throws InterruptedException
7275 // Create Spanner instance.
7376 // We make a copy of the options instance, as SpannerOptions caches any service object
7477 // that has been handed out.
75- SpannerOptions options = env . getTestHelper (). getOptions (). toBuilder (). build ();
78+ SpannerOptions options = createOptions ();
7679 Spanner spanner = options .getService ();
7780 // Get a database client and do a query. This should initiate threads for the Spanner service.
78- DatabaseClient client = spanner .getDatabaseClient (db . getId () );
81+ DatabaseClient client = spanner .getDatabaseClient (dbId );
7982 List <ResultSet > resultSets = new ArrayList <>();
8083 // SpannerStub affiliates a channel with a session, so we need to use multiple sessions
8184 // to ensure we also hit multiple channels.
8285 for (int i2 = 0 ; i2 < options .getSessionPoolOptions ().getMaxSessions (); i2 ++) {
83- ResultSet rs =
84- client
85- .singleUse ()
86- .executeQuery (Statement .of ("SELECT 1 AS COL1 UNION ALL SELECT 2 AS COL2" ));
86+ ResultSet rs = client .singleUse ().executeQuery (SELECT_COUNT_STATEMENT );
8787 // Execute ResultSet#next() to send the query to Spanner.
8888 rs .next ();
8989 // Delay closing the result set in order to force the use of multiple sessions.
9090 // As each session is linked to one transport channel, using multiple different
9191 // sessions should initialize multiple transport channels.
9292 resultSets .add (rs );
9393 // Check whether the number of expected threads has been reached.
94- if (getNumberOfThreadsWithName (SPANNER_THREAD_NAME )
95- == DEFAULT_NUM_CHANNELS * NUM_THREADS_PER_CHANNEL + baseThreadCount ) {
94+ if (getNumberOfThreadsWithName (SPANNER_THREAD_NAME ) == NUM_THREADS + baseThreadCount ) {
9695 break ;
9796 }
9897 }
@@ -102,25 +101,27 @@ public void testCloseAllThreadsWhenClosingSpanner() throws InterruptedException
102101 // Check the number of threads after the query. Doing a request should initialize a thread
103102 // pool for the underlying SpannerClient.
104103 assertThat (getNumberOfThreadsWithName (SPANNER_THREAD_NAME ))
105- .isEqualTo (DEFAULT_NUM_CHANNELS * NUM_THREADS_PER_CHANNEL + baseThreadCount );
104+ .isEqualTo (NUM_THREADS + baseThreadCount );
106105
107106 // Then do a request to the InstanceAdmin service and check the number of threads.
108107 // Doing a request should initialize a thread pool for the underlying InstanceAdminClient.
109- for (int i2 = 0 ; i2 < DEFAULT_NUM_CHANNELS * 2 ; i2 ++) {
108+ for (int i2 = 0 ; i2 < DEFAULT_NUM_CHANNELS_PER_GAPIC_CLIENT * 2 ; i2 ++) {
110109 InstanceAdminClient instanceAdminClient = spanner .getInstanceAdminClient ();
110+ mockInstanceAdmin .addResponse (ListInstancesResponse .getDefaultInstance ());
111111 instanceAdminClient .listInstances ();
112112 }
113113 assertThat (getNumberOfThreadsWithName (SPANNER_THREAD_NAME ))
114- .isEqualTo (2 * DEFAULT_NUM_CHANNELS * NUM_THREADS_PER_CHANNEL + baseThreadCount );
114+ .isEqualTo (NUM_THREADS + baseThreadCount );
115115
116116 // Then do a request to the DatabaseAdmin service and check the number of threads.
117117 // Doing a request should initialize a thread pool for the underlying DatabaseAdminClient.
118- for (int i2 = 0 ; i2 < DEFAULT_NUM_CHANNELS * 2 ; i2 ++) {
118+ for (int i2 = 0 ; i2 < DEFAULT_NUM_CHANNELS_PER_GAPIC_CLIENT * 2 ; i2 ++) {
119119 DatabaseAdminClient databaseAdminClient = spanner .getDatabaseAdminClient ();
120- databaseAdminClient .listDatabases (db .getId ().getInstanceId ().getInstance ());
120+ mockDatabaseAdmin .addResponse (ListDatabasesResponse .getDefaultInstance ());
121+ databaseAdminClient .listDatabases (dbId .getInstanceId ().getInstance ());
121122 }
122123 assertThat (getNumberOfThreadsWithName (SPANNER_THREAD_NAME ))
123- .isEqualTo (3 * DEFAULT_NUM_CHANNELS * NUM_THREADS_PER_CHANNEL + baseThreadCount );
124+ .isEqualTo (NUM_THREADS + baseThreadCount );
124125
125126 // Now close the Spanner instance and check whether the threads are shutdown or not.
126127 spanner .close ();
@@ -138,23 +139,17 @@ public void testCloseAllThreadsWhenClosingSpanner() throws InterruptedException
138139 public void testMultipleSpannersFromSameSpannerOptions () throws InterruptedException {
139140 waitForStartup ();
140141 int baseThreadCount = getNumberOfThreadsWithName (SPANNER_THREAD_NAME );
141- SpannerOptions options = env . getTestHelper (). getOptions (). toBuilder (). build ();
142+ SpannerOptions options = createOptions ();
142143 try (Spanner spanner1 = options .getService ()) {
143144 // Having both in the try-with-resources block is not possible, as it is the same instance.
144145 // One will be closed before the other, and the closing of the second instance would fail.
145146 Spanner spanner2 = options .getService ();
146147 assertThat (spanner1 ).isSameInstanceAs (spanner2 );
147- DatabaseClient client1 = spanner1 .getDatabaseClient (db . getId () );
148- DatabaseClient client2 = spanner2 .getDatabaseClient (db . getId () );
148+ DatabaseClient client1 = spanner1 .getDatabaseClient (dbId );
149+ DatabaseClient client2 = spanner2 .getDatabaseClient (dbId );
149150 assertThat (client1 ).isSameInstanceAs (client2 );
150- try (ResultSet rs1 =
151- client1
152- .singleUse ()
153- .executeQuery (Statement .of ("SELECT 1 AS COL1 UNION ALL SELECT 2 AS COL2" ));
154- ResultSet rs2 =
155- client2
156- .singleUse ()
157- .executeQuery (Statement .of ("SELECT 1 AS COL1 UNION ALL SELECT 2 AS COL2" )); ) {
151+ try (ResultSet rs1 = client1 .singleUse ().executeQuery (SELECT_COUNT_STATEMENT );
152+ ResultSet rs2 = client2 .singleUse ().executeQuery (SELECT_COUNT_STATEMENT )) {
158153 while (rs1 .next () && rs2 .next ()) {
159154 // Do nothing, just consume the result sets.
160155 }
@@ -181,15 +176,10 @@ private void waitForStartup() throws InterruptedException {
181176
182177 private int getNumberOfThreadsWithName (String serviceName ) {
183178 Pattern pattern = Pattern .compile (String .format (THREAD_PATTERN , serviceName ));
184- ThreadGroup group = Thread .currentThread ().getThreadGroup ();
185- while (group .getParent () != null ) {
186- group = group .getParent ();
187- }
188- Thread [] threads = new Thread [100 * NUMBER_OF_TEST_RUNS ];
189- int numberOfThreads = group .enumerate (threads );
179+ Set <Thread > threadSet = Thread .getAllStackTraces ().keySet ();
190180 int res = 0 ;
191- for (int i = 0 ; i < numberOfThreads ; i ++ ) {
192- if (pattern .matcher (threads [ i ] .getName ()).matches ()) {
181+ for (Thread thread : threadSet ) {
182+ if (pattern .matcher (thread .getName ()).matches ()) {
193183 res ++;
194184 }
195185 }
0 commit comments