|
23 | 23 | import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_DEFAULT_LABEL_VALUES; |
24 | 24 | import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS; |
25 | 25 | import static com.google.cloud.spanner.MetricRegistryConstants.SPANNER_LABEL_KEYS_WITH_TYPE; |
| 26 | +import static com.google.cloud.spanner.SpannerOptionsTest.runWithSystemProperty; |
26 | 27 | import static com.google.common.truth.Truth.assertThat; |
27 | 28 | import static org.junit.Assert.assertEquals; |
| 29 | +import static org.junit.Assert.assertNotEquals; |
28 | 30 | import static org.junit.Assert.assertNotNull; |
29 | 31 | import static org.junit.Assert.assertThrows; |
30 | 32 | import static org.junit.Assert.assertTrue; |
|
60 | 62 | import com.google.cloud.spanner.spi.v1.SpannerRpc; |
61 | 63 | import com.google.cloud.spanner.spi.v1.SpannerRpc.ResultStreamConsumer; |
62 | 64 | import com.google.cloud.spanner.v1.stub.SpannerStubSettings; |
| 65 | +import com.google.common.collect.Lists; |
63 | 66 | import com.google.common.util.concurrent.ListenableFuture; |
64 | 67 | import com.google.common.util.concurrent.Uninterruptibles; |
65 | 68 | import com.google.protobuf.ByteString; |
|
82 | 85 | import java.util.LinkedList; |
83 | 86 | import java.util.List; |
84 | 87 | import java.util.Map; |
| 88 | +import java.util.Objects; |
85 | 89 | import java.util.concurrent.CountDownLatch; |
86 | 90 | import java.util.concurrent.ExecutorService; |
87 | 91 | import java.util.concurrent.Executors; |
88 | 92 | import java.util.concurrent.Future; |
89 | 93 | import java.util.concurrent.TimeUnit; |
90 | 94 | import java.util.concurrent.atomic.AtomicBoolean; |
| 95 | +import java.util.stream.Collectors; |
91 | 96 | import org.junit.Before; |
92 | 97 | import org.junit.Test; |
93 | 98 | import org.junit.runner.RunWith; |
@@ -236,6 +241,134 @@ public void poolLifo() { |
236 | 241 | session4.close(); |
237 | 242 | } |
238 | 243 |
|
| 244 | + @Test |
| 245 | + public void poolFifo() throws Exception { |
| 246 | + setupMockSessionCreation(); |
| 247 | + runWithSystemProperty( |
| 248 | + "com.google.cloud.spanner.session_pool_release_to_position", |
| 249 | + "LAST", |
| 250 | + () -> { |
| 251 | + options = |
| 252 | + options |
| 253 | + .toBuilder() |
| 254 | + .setMinSessions(2) |
| 255 | + .setWaitForMinSessions(Duration.ofSeconds(10L)) |
| 256 | + .build(); |
| 257 | + pool = createPool(); |
| 258 | + pool.maybeWaitOnMinSessions(); |
| 259 | + Session session1 = pool.getSession().get(); |
| 260 | + Session session2 = pool.getSession().get(); |
| 261 | + assertNotEquals(session1, session2); |
| 262 | + |
| 263 | + session2.close(); |
| 264 | + session1.close(); |
| 265 | + |
| 266 | + // Check the session out and back in once more to finalize their positions. |
| 267 | + session1 = pool.getSession().get(); |
| 268 | + session2 = pool.getSession().get(); |
| 269 | + session2.close(); |
| 270 | + session1.close(); |
| 271 | + |
| 272 | + // Verify that we get the sessions in FIFO order, so in this order: |
| 273 | + // 1. session2 |
| 274 | + // 2. session1 |
| 275 | + Session session3 = pool.getSession().get(); |
| 276 | + Session session4 = pool.getSession().get(); |
| 277 | + assertEquals(session2, session3); |
| 278 | + assertEquals(session1, session4); |
| 279 | + session3.close(); |
| 280 | + session4.close(); |
| 281 | + |
| 282 | + return null; |
| 283 | + }); |
| 284 | + } |
| 285 | + |
| 286 | + @Test |
| 287 | + public void poolAllPositions() throws Exception { |
| 288 | + int maxAttempts = 100; |
| 289 | + setupMockSessionCreation(); |
| 290 | + for (Position position : Position.values()) { |
| 291 | + runWithSystemProperty( |
| 292 | + "com.google.cloud.spanner.session_pool_release_to_position", |
| 293 | + position.name(), |
| 294 | + () -> { |
| 295 | + int attempt = 0; |
| 296 | + while (attempt < maxAttempts) { |
| 297 | + int numSessions = 5; |
| 298 | + options = |
| 299 | + options |
| 300 | + .toBuilder() |
| 301 | + .setMinSessions(numSessions) |
| 302 | + .setMaxSessions(numSessions) |
| 303 | + .setWaitForMinSessions(Duration.ofSeconds(10L)) |
| 304 | + .build(); |
| 305 | + pool = createPool(); |
| 306 | + pool.maybeWaitOnMinSessions(); |
| 307 | + // First check out and release the sessions twice to the pool, so we know that we have |
| 308 | + // finalized the position of them. |
| 309 | + for (int n = 0; n < 2; n++) { |
| 310 | + checkoutAndReleaseAllSessions(); |
| 311 | + } |
| 312 | + |
| 313 | + // Now verify that if we get all sessions twice, they will be in random order. |
| 314 | + List<List<PooledSessionFuture>> allSessions = new ArrayList<>(2); |
| 315 | + for (int n = 0; n < 2; n++) { |
| 316 | + allSessions.add(checkoutAndReleaseAllSessions()); |
| 317 | + } |
| 318 | + List<Session> firstTime = |
| 319 | + allSessions.get(0).stream() |
| 320 | + .map(PooledSessionFuture::get) |
| 321 | + .collect(Collectors.toList()); |
| 322 | + List<Session> secondTime = |
| 323 | + allSessions.get(1).stream() |
| 324 | + .map(PooledSessionFuture::get) |
| 325 | + .collect(Collectors.toList()); |
| 326 | + switch (position) { |
| 327 | + case FIRST: |
| 328 | + // LIFO: |
| 329 | + // First check out all sessions, so we have 1, 2, 3, 4, ..., N |
| 330 | + // Then release them all back into the pool in the same order (1, 2, 3, 4, ..., N) |
| 331 | + // That will give us the list N, ..., 4, 3, 2, 1 because each session is added at |
| 332 | + // the front of the pool. |
| 333 | + assertEquals(firstTime, Lists.reverse(secondTime)); |
| 334 | + break; |
| 335 | + case LAST: |
| 336 | + // FIFO: |
| 337 | + // First check out all sessions, so we have 1, 2, 3, 4, ..., N |
| 338 | + // Then release them all back into the pool in the same order (1, 2, 3, 4, ..., N) |
| 339 | + // That will give us the list 1, 2, 3, 4, ..., N because each session is added at |
| 340 | + // the end of the pool. |
| 341 | + assertEquals(firstTime, secondTime); |
| 342 | + break; |
| 343 | + case RANDOM: |
| 344 | + // Random means that we should not get the same order twice (unless the randomizer |
| 345 | + // got lucky, and then we retry). |
| 346 | + if (attempt < (maxAttempts - 1)) { |
| 347 | + if (Objects.equals(firstTime, secondTime)) { |
| 348 | + attempt++; |
| 349 | + continue; |
| 350 | + } |
| 351 | + } |
| 352 | + assertNotEquals(firstTime, secondTime); |
| 353 | + } |
| 354 | + break; |
| 355 | + } |
| 356 | + return null; |
| 357 | + }); |
| 358 | + } |
| 359 | + } |
| 360 | + |
| 361 | + private List<PooledSessionFuture> checkoutAndReleaseAllSessions() { |
| 362 | + List<PooledSessionFuture> sessions = new ArrayList<>(pool.totalSessions()); |
| 363 | + for (int i = 0; i < pool.totalSessions(); i++) { |
| 364 | + sessions.add(pool.getSession()); |
| 365 | + } |
| 366 | + for (Session session : sessions) { |
| 367 | + session.close(); |
| 368 | + } |
| 369 | + return sessions; |
| 370 | + } |
| 371 | + |
239 | 372 | @Test |
240 | 373 | public void poolClosure() throws Exception { |
241 | 374 | setupMockSessionCreation(); |
|
0 commit comments