Skip to content

Commit 73957f5

Browse files
committed
Handle keyspace setup failures during borrow
1 parent b666e09 commit 73957f5

3 files changed

Lines changed: 118 additions & 4 deletions

File tree

driver-core/src/main/java/com/datastax/driver/core/Connection.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,17 @@ ListenableFuture<Connection> setKeyspaceAsync(final String keyspace)
900900
logger.debug("{} Setting keyspace {}", this, keyspace);
901901
// Note: we quote the keyspace below, because the name is the one coming from Cassandra, so
902902
// it's in the right case already
903-
Future future = write(new Requests.Query("USE \"" + keyspace + '"'));
903+
Future future;
904+
try {
905+
future = write(new Requests.Query("USE \"" + keyspace + '"'));
906+
} catch (ConnectionException | BusyConnectionException e) {
907+
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
908+
ksFuture.setException(e);
909+
return ksFuture;
910+
} catch (RuntimeException e) {
911+
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
912+
throw e;
913+
}
904914
Futures.addCallback(
905915
future,
906916
new FutureCallback<Message.Response>() {

driver-core/src/main/java/com/datastax/driver/core/HostConnectionPool.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static com.datastax.driver.core.Connection.State.TRASHED;
2828

2929
import com.datastax.driver.core.exceptions.AuthenticationException;
30+
import com.datastax.driver.core.exceptions.BusyConnectionException;
3031
import com.datastax.driver.core.exceptions.BusyPoolException;
3132
import com.datastax.driver.core.exceptions.ConnectionException;
3233
import com.datastax.driver.core.exceptions.UnsupportedProtocolVersionException;
@@ -611,7 +612,22 @@ ListenableFuture<Connection> borrowConnection(
611612
if (totalInFlightCount > currentCapacity) maybeSpawnNewConnection(shardId);
612613
}
613614

614-
return leastBusy.setKeyspaceAsync(manager.poolsState.keyspace);
615+
final Connection borrowedConnection = leastBusy;
616+
ListenableFuture<Connection> setKeyspaceFuture =
617+
borrowedConnection.setKeyspaceAsync(manager.poolsState.keyspace);
618+
Futures.addCallback(
619+
setKeyspaceFuture,
620+
new FutureCallback<Connection>() {
621+
@Override
622+
public void onSuccess(Connection connection) {}
623+
624+
@Override
625+
public void onFailure(Throwable t) {
626+
borrowedConnection.release(t instanceof BusyConnectionException);
627+
}
628+
},
629+
MoreExecutors.directExecutor());
630+
return setKeyspaceFuture;
615631
}
616632

617633
private ListenableFuture<Connection> enqueue(
@@ -698,8 +714,14 @@ private void dequeue(final Connection connection) {
698714
pendingBorrowCount.decrementAndGet();
699715
// Ensure that the keyspace set on the connection is the one set on the pool state, in the
700716
// general case it will be.
701-
ListenableFuture<Connection> setKeyspaceFuture =
702-
connection.setKeyspaceAsync(manager.poolsState.keyspace);
717+
ListenableFuture<Connection> setKeyspaceFuture;
718+
try {
719+
setKeyspaceFuture = connection.setKeyspaceAsync(manager.poolsState.keyspace);
720+
} catch (ConnectionException | BusyConnectionException e) {
721+
pendingBorrow.setException(e);
722+
connection.inFlight.decrementAndGet();
723+
continue;
724+
}
703725
// Slight optimization, if the keyspace was already correct the future will be complete, so
704726
// simply complete it here.
705727
if (setKeyspaceFuture.isDone()) {

driver-core/src/test/java/com/datastax/driver/core/HostConnectionPoolTest.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,85 @@ public void should_adjust_connection_keyspace_on_dequeue_if_pool_state_is_differ
393393
}
394394
}
395395

396+
/**
397+
* Ensures that if keyspace setup fails while borrowing a connection, the borrow future fails and
398+
* pool accounting is restored.
399+
*
400+
* @test_category connection:connection_pool
401+
*/
402+
@Test(groups = "short")
403+
public void should_restore_pool_accounting_when_keyspace_setup_fails_on_borrow()
404+
throws Exception {
405+
Cluster cluster = createClusterBuilder().build();
406+
try {
407+
HostConnectionPool pool = createPool(cluster, 1, 1);
408+
Connection connection = spy(pool.connections[0].get(0));
409+
pool.connections[0].set(0, connection);
410+
411+
pool.manager.poolsState.setKeyspace("newkeyspace");
412+
ConnectionException failure =
413+
new ConnectionException(pool.host.getEndPoint(), "Write attempt on defunct connection");
414+
doReturn(Futures.<Connection>immediateFailedFuture(failure))
415+
.when(connection)
416+
.setKeyspaceAsync("newkeyspace");
417+
418+
MockRequest request = MockRequest.send(pool);
419+
420+
try {
421+
Uninterruptibles.getUninterruptibly(request.connectionFuture, 5, TimeUnit.SECONDS);
422+
fail("Should have failed to borrow connection");
423+
} catch (ExecutionException e) {
424+
assertThat(e.getCause()).isSameAs(failure);
425+
}
426+
assertThat(connection.inFlight.get()).isEqualTo(0);
427+
assertThat(pool.totalInFlight.get()).isEqualTo(0);
428+
} finally {
429+
cluster.close();
430+
}
431+
}
432+
433+
/**
434+
* Ensures that a synchronous write failure while setting the keyspace is reported through the
435+
* returned future and does not leave the failed keyspace attempt in-flight.
436+
*
437+
* @test_category connection:connection_pool
438+
*/
439+
@Test(groups = "short")
440+
public void should_fail_keyspace_future_and_clear_attempt_when_connection_is_defunct()
441+
throws Exception {
442+
Cluster cluster = createClusterBuilder().build();
443+
try {
444+
HostConnectionPool pool = createPool(cluster, 1, 1);
445+
Connection connection = pool.connections[0].get(0);
446+
447+
connection.defunct(
448+
new ConnectionException(pool.host.getEndPoint(), "Test defunct connection"));
449+
450+
ListenableFuture<Connection> firstAttempt = connection.setKeyspaceAsync("newkeyspace");
451+
ListenableFuture<Connection> secondAttempt = connection.setKeyspaceAsync("newkeyspace");
452+
453+
assertThat(firstAttempt).isNotSameAs(secondAttempt);
454+
try {
455+
Uninterruptibles.getUninterruptibly(firstAttempt, 5, TimeUnit.SECONDS);
456+
fail("Should have failed keyspace attempt");
457+
} catch (ExecutionException e) {
458+
assertThat(e.getCause())
459+
.isInstanceOf(ConnectionException.class)
460+
.hasMessageContaining("Write attempt on defunct connection");
461+
}
462+
try {
463+
Uninterruptibles.getUninterruptibly(secondAttempt, 5, TimeUnit.SECONDS);
464+
fail("Should have failed keyspace attempt");
465+
} catch (ExecutionException e) {
466+
assertThat(e.getCause())
467+
.isInstanceOf(ConnectionException.class)
468+
.hasMessageContaining("Write attempt on defunct connection");
469+
}
470+
} finally {
471+
cluster.close();
472+
}
473+
}
474+
396475
/**
397476
* Ensures that on borrowConnection if a set keyspace attempt is in progress on that connection
398477
* for a different keyspace than the pool state that the borrowConnection future returned is
@@ -501,6 +580,9 @@ public void should_adjust_connection_keyspace_on_dequeue_if_pool_state_is_differ
501580
.contains(
502581
"Aborting attempt to set keyspace to 'newkeyspace' since there is already an in flight attempt to set keyspace to 'slowks'.");
503582
}
583+
assertThat(connection.inFlight.get()).isEqualTo(0);
584+
assertThat(pool.totalInFlight.get()).isEqualTo(0);
585+
assertThat(pool.pendingBorrowCount.get()).isEqualTo(0);
504586
} finally {
505587
MockRequest.completeAll(requests);
506588
cluster.close();

0 commit comments

Comments
 (0)