Skip to content

Commit b4d1939

Browse files
Fix potential connection leak when using jdbc storage and Scan operation fails because the target table doesn't exist (#2766)
Co-authored-by: Hiroyuki Yamada <[email protected]>
1 parent 9541cd0 commit b4d1939

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ public Scanner scan(Scan scan) throws ExecutionException {
117117
close(connection);
118118
throw new ExecutionException(
119119
CoreError.JDBC_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e);
120+
} catch (Exception e) {
121+
try {
122+
if (connection != null) {
123+
connection.rollback();
124+
}
125+
} catch (SQLException ex) {
126+
e.addSuppressed(ex);
127+
}
128+
129+
close(connection);
130+
throw e;
120131
}
121132
}
122133

@@ -186,6 +197,9 @@ public void mutate(List<? extends Mutation> mutations) throws ExecutionException
186197
close(connection);
187198
throw new ExecutionException(
188199
CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e);
200+
} catch (Exception e) {
201+
close(connection);
202+
throw e;
189203
}
190204

191205
try {

core/src/test/java/com/scalar/db/storage/jdbc/JdbcDatabaseTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import static org.assertj.core.api.Assertions.assertThatThrownBy;
44
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.ArgumentMatchers.anyBoolean;
56
import static org.mockito.Mockito.doThrow;
7+
import static org.mockito.Mockito.never;
68
import static org.mockito.Mockito.verify;
79
import static org.mockito.Mockito.when;
810

@@ -138,6 +140,25 @@ public void whenScanOperationExecutedAndScannerClosed_shouldCallJdbcService() th
138140
verify(connection).close();
139141
}
140142

143+
@Test
144+
public void
145+
whenScanOperationExecutedAndJdbcServiceThrowsIllegalArgumentException_shouldCloseConnectionAndThrowIllegalArgumentException()
146+
throws Exception {
147+
// Arrange
148+
Exception cause = new IllegalArgumentException("Table not found");
149+
// Simulate the table not found scenario.
150+
when(jdbcService.getScanner(any(), any())).thenThrow(cause);
151+
152+
// Act Assert
153+
assertThatThrownBy(
154+
() -> {
155+
Scan scan = new Scan(new Key("p1", "val")).forNamespace(NAMESPACE).forTable(TABLE);
156+
jdbcDatabase.scan(scan);
157+
})
158+
.isInstanceOf(IllegalArgumentException.class);
159+
verify(connection).close();
160+
}
161+
141162
@Test
142163
public void
143164
whenScanOperationExecutedAndScannerClosed_SQLExceptionThrownByConnectionCommit_shouldThrowIOException()
@@ -382,4 +403,30 @@ public void mutate_withConflictError_shouldThrowRetriableExecutionException()
382403
verify(connection).rollback();
383404
verify(connection).close();
384405
}
406+
407+
@Test
408+
public void mutate_WhenSettingAutoCommitFails_ShouldThrowExceptionAndCloseConnection()
409+
throws SQLException, ExecutionException {
410+
// Arrange
411+
Exception exception = new RuntimeException("Failed to set auto-commit");
412+
doThrow(exception).when(connection).setAutoCommit(anyBoolean());
413+
414+
// Act Assert
415+
assertThatThrownBy(
416+
() -> {
417+
Put put =
418+
new Put(new Key("p1", "val1"))
419+
.withValue("v1", "val2")
420+
.forNamespace(NAMESPACE)
421+
.forTable(TABLE);
422+
Delete delete =
423+
new Delete(new Key("p1", "val1")).forNamespace(NAMESPACE).forTable(TABLE);
424+
jdbcDatabase.mutate(Arrays.asList(put, delete));
425+
})
426+
.isEqualTo(exception);
427+
verify(connection).setAutoCommit(false);
428+
verify(jdbcService, never()).mutate(any(), any());
429+
verify(connection, never()).rollback();
430+
verify(connection).close();
431+
}
385432
}

0 commit comments

Comments
 (0)