ListenableFuture<Void> closeAsync() { return pool.closeAsync(); } }
@Test public void closeQuicklyDoesNotBlockIndefinitely() throws Exception { pool.closeAsync().get(); }
@Test public void closeWhenSessionsActiveFinishes() throws Exception { Session session = pool.getReadSession(); pool.closeAsync().get(); } }
@Test public void poolClosure() throws Exception { setupMockSessionCreation(); pool = createPool(); pool.closeAsync().get(); }
@Test public void closeAfterInitialCreateDoesNotBlockIndefinitely() throws Exception { pool.getReadSession().close(); pool.closeAsync().get(); }
@Test public void poolClosesWhenMaintenanceLoopIsRunning() throws Exception { setupMockSessionCreation(); final FakeClock clock = new FakeClock(); pool = createPool(clock); final AtomicBoolean stop = new AtomicBoolean(false); new Thread( new Runnable() { @Override public void run() { // Run in a tight loop. while (!stop.get()) { runMaintainanceLoop(clock, pool, 1); } } }) .start(); pool.closeAsync().get(); stop.set(true); }
@Test public void poolClosesEvenIfPreparationFails() throws Exception { Session session = mockSession(); when(client.createSession(db)).thenReturn(session); final CountDownLatch insidePrepare = new CountDownLatch(1); final CountDownLatch releasePrepare = new CountDownLatch(1); doAnswer( new Answer<Session>() { @Override public Session answer(InvocationOnMock invocation) throws Throwable { insidePrepare.countDown(); releasePrepare.await(); throw SpannerExceptionFactory.newSpannerException(new RuntimeException()); } }) .when(session) .prepareReadWriteTransaction(); pool = createPool(); AtomicBoolean failed = new AtomicBoolean(false); CountDownLatch latch = new CountDownLatch(1); getReadWriteSessionAsync(latch, failed); insidePrepare.await(); ListenableFuture<Void> f = pool.closeAsync(); releasePrepare.countDown(); f.get(); assertThat(f.isDone()).isTrue(); }
@Test public void poolClosesEvenIfCreationFails() throws Exception { final CountDownLatch insideCreation = new CountDownLatch(1); final CountDownLatch releaseCreation = new CountDownLatch(1); when(client.createSession(db)) .thenAnswer( new Answer<Session>() { @Override public Session answer(InvocationOnMock invocation) throws Throwable { insideCreation.countDown(); releaseCreation.await(); throw SpannerExceptionFactory.newSpannerException(new RuntimeException()); } }); pool = createPool(); AtomicBoolean failed = new AtomicBoolean(false); CountDownLatch latch = new CountDownLatch(1); getSessionAsync(latch, failed); insideCreation.await(); ListenableFuture<Void> f = pool.closeAsync(); releaseCreation.countDown(); f.get(); assertThat(f.isDone()).isTrue(); }
@Test public void poolClosureFailsPendingReadWaiters() throws Exception { final CountDownLatch insideCreation = new CountDownLatch(1); final CountDownLatch releaseCreation = new CountDownLatch(1); Session session1 = mockSession(); final Session session2 = mockSession(); when(client.createSession(db)) .thenReturn(session1) .thenAnswer( new Answer<Session>() { @Override public Session answer(InvocationOnMock invocation) throws Throwable { insideCreation.countDown(); releaseCreation.await(); return session2; } }); pool = createPool(); pool.getReadSession(); AtomicBoolean failed = new AtomicBoolean(false); CountDownLatch latch = new CountDownLatch(1); getSessionAsync(latch, failed); insideCreation.await(); pool.closeAsync(); releaseCreation.countDown(); latch.await(); assertThat(failed.get()).isTrue(); }
@Test public void poolClosureFailsPendingWriteWaiters() throws Exception { final CountDownLatch insideCreation = new CountDownLatch(1); final CountDownLatch releaseCreation = new CountDownLatch(1); Session session1 = mockSession(); final Session session2 = mockSession(); when(client.createSession(db)) .thenReturn(session1) .thenAnswer( new Answer<Session>() { @Override public Session answer(InvocationOnMock invocation) throws Throwable { insideCreation.countDown(); releaseCreation.await(); return session2; } }); pool = createPool(); pool.getReadSession(); AtomicBoolean failed = new AtomicBoolean(false); CountDownLatch latch = new CountDownLatch(1); getReadWriteSessionAsync(latch, failed); insideCreation.await(); pool.closeAsync(); releaseCreation.countDown(); latch.await(); assertThat(failed.get()).isTrue(); }
@Test public void poolClosureClosesLeakedSessions() throws Exception { Session mockSession1 = mockSession(); Session mockSession2 = mockSession(); when(client.createSession(db)).thenReturn(mockSession1).thenReturn(mockSession2); pool = createPool(); Session session1 = pool.getReadSession(); // Leaked sessions pool.getReadSession(); session1.close(); pool.closeAsync().get(); verify(mockSession1).close(); verify(mockSession2).close(); }
@Test public void poolClosureFailsNewRequests() throws Exception { Session session = mockSession(); when(client.createSession(db)).thenReturn(session); pool = createPool(); pool.getReadSession(); pool.closeAsync(); expectedException.expect(IllegalStateException.class); pool.getReadSession(); }
@Test public void keepAlive() throws Exception { options = SessionPoolOptions.newBuilder().setMinSessions(2).setMaxSessions(3).build(); Session session = mockSession(); mockKeepAlive(session); // This is cheating as we are returning the same session each but it makes the verification // easier. when(client.createSession(db)).thenReturn(session); FakeClock clock = new FakeClock(); clock.currentTimeMillis = System.currentTimeMillis(); pool = createPool(clock); Session session1 = pool.getReadSession(); Session session2 = pool.getReadSession(); session1.close(); session2.close(); runMaintainanceLoop(clock, pool, pool.poolMaintainer.numKeepAliveCycles); verify(session, never()).singleUse(any(TimestampBound.class)); runMaintainanceLoop(clock, pool, pool.poolMaintainer.numKeepAliveCycles); verify(session, times(2)).singleUse(any(TimestampBound.class)); clock.currentTimeMillis += clock.currentTimeMillis + 35 * 60 * 1000; session1 = pool.getReadSession(); session1.writeAtLeastOnce(new ArrayList<Mutation>()); session1.close(); runMaintainanceLoop(clock, pool, pool.poolMaintainer.numKeepAliveCycles); verify(session, times(3)).singleUse(any(TimestampBound.class)); pool.closeAsync().get(); }
assertThat(maxAliveSessions).isAtMost(maxSessions); pool.closeAsync().get(); Exception e = getFailedError(); if (e != null) {
pool.closeAsync().get();
ListenableFuture<Void> closeAsync() { return pool.closeAsync(); } }