private SpannerOptions(Builder builder) { super(SpannerFactory.class, SpannerRpcFactory.class, builder, new SpannerDefaults()); numChannels = builder.numChannels; Preconditions.checkArgument( numChannels >= 1 && numChannels <= MAX_CHANNELS, "Number of channels must fall in the range [1, %s], found: %s", MAX_CHANNELS, numChannels); channelProvider = builder.channelProvider; interceptorProvider = builder.interceptorProvider; sessionPoolOptions = builder.sessionPoolOptions != null ? builder.sessionPoolOptions : SessionPoolOptions.newBuilder().build(); prefetchChunks = builder.prefetchChunks; sessionLabels = builder.sessionLabels; }
private static Spanner getSpanner(Properties properties, String host, String project) { if (spanner != null) { return spanner; } String numChannels = properties.getProperty(CloudSpannerProperties.NUM_CHANNELS); int numThreads = Integer.parseInt(properties.getProperty(Client.THREAD_COUNT_PROPERTY, "1")); SpannerOptions.Builder optionsBuilder = SpannerOptions.newBuilder() .setSessionPoolOption(SessionPoolOptions.newBuilder() .setMinSessions(numThreads) // Since we have no read-write transactions, we can set the write session fraction to 0. .setWriteSessionsFraction(0) .build()); if (host != null) { optionsBuilder.setHost(host); } if (project != null) { optionsBuilder.setProjectId(project); } if (numChannels != null) { optionsBuilder.setNumChannels(Integer.parseInt(numChannels)); } spanner = optionsBuilder.build().getService(); Runtime.getRuntime().addShutdownHook(new Thread("spannerShutdown") { @Override public void run() { spanner.close(); } }); return spanner; }
@Test public void getDbclientAgainGivesSame() { Map<String, String> labels = new HashMap<>(); labels.put("env", "dev"); Mockito.when(spannerOptions.getSessionLabels()).thenReturn(labels); String dbName = "projects/p1/instances/i1/databases/d1"; DatabaseId db = DatabaseId.of(dbName); Mockito.when(spannerOptions.getTransportOptions()) .thenReturn(GrpcTransportOptions.newBuilder().build()); Mockito.when(spannerOptions.getSessionPoolOptions()) .thenReturn(SessionPoolOptions.newBuilder().build()); DatabaseClient databaseClient = impl.getDatabaseClient(db); // Get db client again DatabaseClient databaseClient1 = impl.getDatabaseClient(db); assertThat(databaseClient1).isSameAs(databaseClient); }
@Before public void setUp() throws Exception { initMocks(this); options = SessionPoolOptions.newBuilder() .setMinSessions(minSessions) .setMaxSessions(2) .setBlockIfPoolExhausted() .build(); }
@Test public void getDbclientAfterCloseThrows() { SpannerImpl imp = new SpannerImpl(rpc, 1, spannerOptions); Map<String, String> labels = new HashMap<>(); labels.put("env", "dev"); Mockito.when(spannerOptions.getSessionLabels()).thenReturn(labels); String dbName = "projects/p1/instances/i1/databases/d1"; DatabaseId db = DatabaseId.of(dbName); Mockito.when(spannerOptions.getTransportOptions()) .thenReturn(GrpcTransportOptions.newBuilder().build()); Mockito.when(spannerOptions.getSessionPoolOptions()) .thenReturn(SessionPoolOptions.newBuilder().build()); imp.close(); try { imp.getDatabaseClient(db); fail("Expected exception"); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains("Cloud Spanner client has been closed"); } }
@Test public void setMinMaxSessions() { if (minSessions > maxSessions) { expectedException.expect(IllegalArgumentException.class); } SessionPoolOptions options = SessionPoolOptions.newBuilder() .setMinSessions(minSessions) .setMaxSessions(maxSessions) .build(); assertEquals(minSessions, options.getMinSessions()); assertEquals(maxSessions, options.getMaxSessions()); } }
@Test public void getReadSessionFallsBackToWritePreparedSession() throws Exception { Session mockSession1 = mockSession(); final CountDownLatch prepareLatch = new CountDownLatch(2); doAnswer( new Answer<Void>() { @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 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(); }
SessionPoolOptions.newBuilder() .setMinSessions(2) .setMaxSessions(2)
float writeSessionsFraction = 0.5f; SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder() .setMinSessions(minSessions) .setMaxSessions(maxSessions)
@Test public void idleSessionCleanup() throws Exception { options = SessionPoolOptions.newBuilder() .setMinSessions(1) .setMaxSessions(3)
@Test public void failOnPoolExhaustion() { options = SessionPoolOptions.newBuilder() .setMinSessions(1) .setMaxSessions(1) .setFailIfPoolExhausted() .build(); Session mockSession = mockSession(); when(client.createSession(db)).thenReturn(mockSession); pool = createPool(); Session session1 = pool.getReadSession(); expectedException.expect(isSpannerException(ErrorCode.RESOURCE_EXHAUSTED)); pool.getReadSession(); session1.close(); session1 = pool.getReadSession(); assertThat(session1).isNotNull(); session1.close(); }
@Before public void setUp() throws Exception { SessionPoolOptions options = SessionPoolOptions.newBuilder().setMinSessions(1).setMaxSessions(2).build(); pool = SessionPool.createPool( options, new ExecutorFactory<ScheduledExecutorService>() { @Override public void release(ScheduledExecutorService executor) { executor.shutdown(); } @Override public ScheduledExecutorService get() { return new ScheduledThreadPoolExecutor(2); } }, db.getId(), (SpannerImpl) env.getTestHelper().getClient()); }
private SpannerOptions(Builder builder) { super(SpannerFactory.class, SpannerRpcFactory.class, builder, new SpannerDefaults()); numChannels = builder.numChannels; Preconditions.checkArgument( numChannels >= 1 && numChannels <= MAX_CHANNELS, "Number of channels must fall in the range [1, %s], found: %s", MAX_CHANNELS, numChannels); channelProvider = builder.channelProvider; interceptorProvider = builder.interceptorProvider; sessionPoolOptions = builder.sessionPoolOptions != null ? builder.sessionPoolOptions : SessionPoolOptions.newBuilder().build(); prefetchChunks = builder.prefetchChunks; sessionLabels = builder.sessionLabels; }
@Bean @ConditionalOnMissingBean public SessionPoolOptions sessionPoolOptions() { SessionPoolOptions.Builder builder = SessionPoolOptions.newBuilder(); if (this.minSessions >= 0) { builder.setMinSessions(this.minSessions); } if (this.maxSessions >= 0) { builder.setMaxSessions(this.maxSessions); } if (this.maxIdleSessions >= 0) { builder.setMaxIdleSessions(this.maxIdleSessions); } if (this.writeSessionsFraction >= 0) { builder.setWriteSessionsFraction(this.writeSessionsFraction); } if (this.keepAliveIntervalMinutes >= 0) { builder.setKeepAliveIntervalMinutes(this.keepAliveIntervalMinutes); } return builder.build(); }