From 6f9f594629424ab0d91249e3820dfbb60cda2e42 Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 15 Feb 2017 03:02:34 +0530 Subject: [PATCH 1/5] Added a random number to the database id --- .../com/google/cloud/spanner/testing/RemoteSpannerHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java index 29aef79b5192..a58154b2cd2c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; @@ -41,6 +42,7 @@ public class RemoteSpannerHelper { private final Spanner client; private final InstanceId instanceId; private static int dbSeq; + private static int dbPrefix = new Random().nextInt(); private final List dbs = new ArrayList<>(); private RemoteSpannerHelper(SpannerOptions options, InstanceId instanceId, Spanner client) { @@ -78,7 +80,7 @@ public Database createTestDatabase(String... statements) throws SpannerException * Returns a database id which is guaranteed to be unique within the context of this environment. */ public String getUniqueDatabaseId() { - return String.format("testdb_%04d", dbSeq++); + return String.format("testdb_%d_%04d", dbPrefix, dbSeq++); } /** From 0ea211fb9ff320bd0cb5d503fecace50237ffa3c Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 15 Feb 2017 03:13:05 +0530 Subject: [PATCH 2/5] Pick a positive random number --- .../com/google/cloud/spanner/testing/RemoteSpannerHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java index a58154b2cd2c..674c2f171e79 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java @@ -42,7 +42,7 @@ public class RemoteSpannerHelper { private final Spanner client; private final InstanceId instanceId; private static int dbSeq; - private static int dbPrefix = new Random().nextInt(); + private static int dbPrefix = new Random().nextInt(Integer.MAX_VALUE); private final List dbs = new ArrayList<>(); private RemoteSpannerHelper(SpannerOptions options, InstanceId instanceId, Spanner client) { From 1d23f0ab88d40d6d97e88456b8e9c74d2ad4d26d Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 15 Feb 2017 04:25:45 +0530 Subject: [PATCH 3/5] Use getUniqueDatabaseId in createTestDatabase --- .../com/google/cloud/spanner/testing/RemoteSpannerHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java index 674c2f171e79..12778b4e46cf 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java @@ -89,7 +89,7 @@ public String getUniqueDatabaseId() { * statement generated accordingly. */ public Database createTestDatabase(Iterable statements) throws SpannerException { - String dbId = String.format("testdb_%04d", dbSeq++); + String dbId = getUniqueDatabaseId(); Operation op = client.getDatabaseAdminClient().createDatabase(instanceId.getInstance(), dbId, statements); op = op.waitFor(); From 6ffe9e7a25565e7cda026ebd4d33c2ec4bc34bf5 Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 8 Mar 2017 16:11:57 +0530 Subject: [PATCH 4/5] Adding session pool tests --- .../google/cloud/spanner/SessionPoolTest.java | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java index 460e6c48a94e..6bdbe31f8179 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java @@ -32,6 +32,7 @@ import com.google.cloud.GrpcServiceOptions.ExecutorFactory; import com.google.cloud.spanner.SessionPool.Clock; import com.google.cloud.spanner.SessionPool.PooledSession; +import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.Uninterruptibles; import java.util.ArrayList; import java.util.Arrays; @@ -113,7 +114,118 @@ public void poolClosure() throws Exception { pool = createPool(); pool.closeAsync().get(); } + + @Test + public void poolClosureFailsPendingReadWaiters() throws Exception { + final CountDownLatch insideCreation = new CountDownLatch(1); + final CountDownLatch releaseCreation = new CountDownLatch(1); + when(client.createSession(db)) + .thenReturn(mock(Session.class)) + .thenAnswer(new Answer() { + @Override + public Session answer(InvocationOnMock invocation) throws Throwable { + insideCreation.countDown(); + releaseCreation.await(); + return mock(Session.class); + } + }); + 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); + when(client.createSession(db)) + .thenReturn(mock(Session.class)) + .thenAnswer(new Answer() { + @Override + public Session answer(InvocationOnMock invocation) throws Throwable { + insideCreation.countDown(); + releaseCreation.await(); + return mock(Session.class); + } + }); + 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 poolClosesEvenIfCreationFails() throws Exception { + final CountDownLatch insideCreation = new CountDownLatch(1); + final CountDownLatch releaseCreation = new CountDownLatch(1); + when(client.createSession(db)) + .thenAnswer(new Answer() { + @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 f = pool.closeAsync(); + releaseCreation.countDown(); + f.get(); + assertThat(f.isDone()).isTrue(); + } + + @Test + public void poolClosesEvenIfPreparationFails() throws Exception { + Session session = mock(Session.class); + when(client.createSession(db)).thenReturn(session); + final CountDownLatch insidePrepare = new CountDownLatch(1); + final CountDownLatch releasePrepare = new CountDownLatch(1); + doAnswer(new Answer() { + @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 f = pool.closeAsync(); + releasePrepare.countDown(); + f.get(); + assertThat(f.isDone()).isTrue(); + } + + @Test + public void poolClosureFailsNewRequests() throws Exception { + when(client.createSession(db)).thenReturn(mock(Session.class)); + pool = createPool(); + pool.getReadSession(); + pool.closeAsync(); + expectedException.expect(IllegalStateException.class); + pool.getReadSession(); + } + @Test public void atMostMaxSessionsCreated() { AtomicBoolean failed = new AtomicBoolean(false); @@ -242,6 +354,35 @@ public Void answer(InvocationOnMock arg0) throws Throwable { readSession.close(); writeSession.close(); } + + @Test + public void getReadSessionFallsBackToWritePreparedSession() throws Exception { + Session mockSession1 = mock(Session.class); + final CountDownLatch prepareLatch = new CountDownLatch(2); + doAnswer( + new Answer() { + @Override + public Void answer(InvocationOnMock arg0) throws Throwable { + prepareLatch.countDown(); + return null; + } + }) + .when(mockSession1) + .prepareReadWriteTransaction(); + when(client.createSession(db)).thenReturn(mockSession1); + options = + SessionPoolOptions.newBuilder() + .setMinSessions(minSessions) + .setMaxSessions(1) + .setWriteSessionsFraction(1.0f) + .build(); + pool = createPool(); + pool.getReadWriteSession().close(); + prepareLatch.await(); + // This session should also be write prepared. + PooledSession readSession = (PooledSession) pool.getReadSession(); + verify(readSession.delegate, times(2)).prepareReadWriteTransaction(); + } @Test public void failOnPoolExhaustion() { @@ -262,6 +403,18 @@ public void failOnPoolExhaustion() { session1.close(); } + @Test + public void poolWorksWhenSessionNotFound() { + Session mockSession1 = mock(Session.class); + Session mockSession2 = mock(Session.class); + doThrow( + SpannerExceptionFactory.newSpannerException(ErrorCode.NOT_FOUND, "Session not found")). + when(mockSession1).prepareReadWriteTransaction(); + when(client.createSession(db)).thenReturn(mockSession1).thenReturn(mockSession2); + pool = createPool(); + assertThat(((PooledSession) pool.getReadWriteSession()).delegate).isEqualTo(mockSession2); + } + @Test public void idleSessionCleanup() throws Exception { options = @@ -366,6 +519,9 @@ public void run() { try (Session session = pool.getReadSession()) { failed.compareAndSet(false, session == null); Uninterruptibles.sleepUninterruptibly(10, TimeUnit.MILLISECONDS); + } catch (SpannerException e) { + failed.compareAndSet(false, true); + } finally { latch.countDown(); } } @@ -381,6 +537,9 @@ public void run() { try (Session session = pool.getReadWriteSession()) { failed.compareAndSet(false, session == null); Uninterruptibles.sleepUninterruptibly(2, TimeUnit.MILLISECONDS); + } catch (SpannerException e) { + failed.compareAndSet(false, true); + } finally { latch.countDown(); } } From f9b5a014420f83ba792f714cff0ccf0d1902ee55 Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 5 Apr 2017 09:32:15 -0700 Subject: [PATCH 5/5] Fixed indentation --- .../google/cloud/spanner/SessionPoolTest.java | 268 +++++++++--------- 1 file changed, 137 insertions(+), 131 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java index b9e1d42e0156..c4d910cba6ca 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java @@ -114,118 +114,124 @@ public void poolClosure() throws Exception { pool = createPool(); pool.closeAsync().get(); } - + @Test public void poolClosureFailsPendingReadWaiters() throws Exception { - final CountDownLatch insideCreation = new CountDownLatch(1); - final CountDownLatch releaseCreation = new CountDownLatch(1); - when(client.createSession(db)) - .thenReturn(mock(Session.class)) - .thenAnswer(new Answer() { - @Override - public Session answer(InvocationOnMock invocation) throws Throwable { - insideCreation.countDown(); - releaseCreation.await(); - return mock(Session.class); - } - }); - 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(); + final CountDownLatch insideCreation = new CountDownLatch(1); + final CountDownLatch releaseCreation = new CountDownLatch(1); + when(client.createSession(db)) + .thenReturn(mock(Session.class)) + .thenAnswer( + new Answer() { + @Override + public Session answer(InvocationOnMock invocation) throws Throwable { + insideCreation.countDown(); + releaseCreation.await(); + return mock(Session.class); + } + }); + 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); - when(client.createSession(db)) - .thenReturn(mock(Session.class)) - .thenAnswer(new Answer() { - @Override - public Session answer(InvocationOnMock invocation) throws Throwable { - insideCreation.countDown(); - releaseCreation.await(); - return mock(Session.class); - } - }); - 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(); + final CountDownLatch insideCreation = new CountDownLatch(1); + final CountDownLatch releaseCreation = new CountDownLatch(1); + when(client.createSession(db)) + .thenReturn(mock(Session.class)) + .thenAnswer( + new Answer() { + @Override + public Session answer(InvocationOnMock invocation) throws Throwable { + insideCreation.countDown(); + releaseCreation.await(); + return mock(Session.class); + } + }); + 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 poolClosesEvenIfCreationFails() throws Exception { - final CountDownLatch insideCreation = new CountDownLatch(1); - final CountDownLatch releaseCreation = new CountDownLatch(1); - when(client.createSession(db)) - .thenAnswer(new Answer() { - @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 f = pool.closeAsync(); - releaseCreation.countDown(); - f.get(); - assertThat(f.isDone()).isTrue(); + final CountDownLatch insideCreation = new CountDownLatch(1); + final CountDownLatch releaseCreation = new CountDownLatch(1); + when(client.createSession(db)) + .thenAnswer( + new Answer() { + @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 f = pool.closeAsync(); + releaseCreation.countDown(); + f.get(); + assertThat(f.isDone()).isTrue(); } - + @Test public void poolClosesEvenIfPreparationFails() throws Exception { - Session session = mock(Session.class); - when(client.createSession(db)).thenReturn(session); - final CountDownLatch insidePrepare = new CountDownLatch(1); - final CountDownLatch releasePrepare = new CountDownLatch(1); - doAnswer(new Answer() { - @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 f = pool.closeAsync(); - releasePrepare.countDown(); - f.get(); - assertThat(f.isDone()).isTrue(); + Session session = mock(Session.class); + when(client.createSession(db)).thenReturn(session); + final CountDownLatch insidePrepare = new CountDownLatch(1); + final CountDownLatch releasePrepare = new CountDownLatch(1); + doAnswer( + new Answer() { + @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 f = pool.closeAsync(); + releasePrepare.countDown(); + f.get(); + assertThat(f.isDone()).isTrue(); } - + @Test public void poolClosureFailsNewRequests() throws Exception { - when(client.createSession(db)).thenReturn(mock(Session.class)); - pool = createPool(); - pool.getReadSession(); - pool.closeAsync(); - expectedException.expect(IllegalStateException.class); - pool.getReadSession(); + when(client.createSession(db)).thenReturn(mock(Session.class)); + pool = createPool(); + pool.getReadSession(); + pool.closeAsync(); + expectedException.expect(IllegalStateException.class); + pool.getReadSession(); } - + @Test public void atMostMaxSessionsCreated() { AtomicBoolean failed = new AtomicBoolean(false); @@ -354,34 +360,34 @@ public Void answer(InvocationOnMock arg0) throws Throwable { readSession.close(); writeSession.close(); } - + @Test public void getReadSessionFallsBackToWritePreparedSession() throws Exception { - Session mockSession1 = mock(Session.class); - final CountDownLatch prepareLatch = new CountDownLatch(2); - doAnswer( - new Answer() { - @Override - public Void answer(InvocationOnMock arg0) throws Throwable { - prepareLatch.countDown(); - return null; - } - }) - .when(mockSession1) - .prepareReadWriteTransaction(); - when(client.createSession(db)).thenReturn(mockSession1); - options = - SessionPoolOptions.newBuilder() - .setMinSessions(minSessions) - .setMaxSessions(1) - .setWriteSessionsFraction(1.0f) - .build(); - pool = createPool(); - pool.getReadWriteSession().close(); - prepareLatch.await(); - // This session should also be write prepared. - PooledSession readSession = (PooledSession) pool.getReadSession(); - verify(readSession.delegate, times(2)).prepareReadWriteTransaction(); + Session mockSession1 = mock(Session.class); + final CountDownLatch prepareLatch = new CountDownLatch(2); + doAnswer( + new Answer() { + @Override + public Void answer(InvocationOnMock arg0) throws Throwable { + prepareLatch.countDown(); + return null; + } + }) + .when(mockSession1) + .prepareReadWriteTransaction(); + when(client.createSession(db)).thenReturn(mockSession1); + options = + SessionPoolOptions.newBuilder() + .setMinSessions(minSessions) + .setMaxSessions(1) + .setWriteSessionsFraction(1.0f) + .build(); + pool = createPool(); + pool.getReadWriteSession().close(); + prepareLatch.await(); + // This session should also be write prepared. + PooledSession readSession = (PooledSession) pool.getReadSession(); + verify(readSession.delegate, times(2)).prepareReadWriteTransaction(); } @Test @@ -405,16 +411,16 @@ public void failOnPoolExhaustion() { @Test public void poolWorksWhenSessionNotFound() { - Session mockSession1 = mock(Session.class); - Session mockSession2 = mock(Session.class); - doThrow( - SpannerExceptionFactory.newSpannerException(ErrorCode.NOT_FOUND, "Session not found")). - when(mockSession1).prepareReadWriteTransaction(); - when(client.createSession(db)).thenReturn(mockSession1).thenReturn(mockSession2); - pool = createPool(); - assertThat(((PooledSession) pool.getReadWriteSession()).delegate).isEqualTo(mockSession2); + Session mockSession1 = mock(Session.class); + Session mockSession2 = mock(Session.class); + doThrow(SpannerExceptionFactory.newSpannerException(ErrorCode.NOT_FOUND, "Session not found")) + .when(mockSession1) + .prepareReadWriteTransaction(); + when(client.createSession(db)).thenReturn(mockSession1).thenReturn(mockSession2); + pool = createPool(); + assertThat(((PooledSession) pool.getReadWriteSession()).delegate).isEqualTo(mockSession2); } - + @Test public void idleSessionCleanup() throws Exception { options =