Skip to content

Commit 47d8fba

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 24e0ec6 commit 47d8fba

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-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
@@ -102,6 +102,17 @@ public Scanner scan(Scan scan) throws ExecutionException {
102102
close(connection);
103103
throw new ExecutionException(
104104
CoreError.JDBC_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e);
105+
} catch (Exception e) {
106+
try {
107+
if (connection != null) {
108+
connection.rollback();
109+
}
110+
} catch (SQLException ex) {
111+
e.addSuppressed(ex);
112+
}
113+
114+
close(connection);
115+
throw e;
105116
}
106117
}
107118

@@ -171,6 +182,9 @@ public void mutate(List<? extends Mutation> mutations) throws ExecutionException
171182
close(connection);
172183
throw new ExecutionException(
173184
CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e);
185+
} catch (Exception e) {
186+
close(connection);
187+
throw e;
174188
}
175189

176190
try {

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +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;
6+
import static org.mockito.Mockito.doThrow;
7+
import static org.mockito.Mockito.never;
58
import static org.mockito.Mockito.verify;
69
import static org.mockito.Mockito.when;
710

@@ -125,6 +128,25 @@ public void whenScanOperationExecutedAndScannerClosed_shouldCallJdbcService() th
125128
verify(connection).close();
126129
}
127130

131+
@Test
132+
public void
133+
whenScanOperationExecutedAndJdbcServiceThrowsIllegalArgumentException_shouldCloseConnectionAndThrowIllegalArgumentException()
134+
throws Exception {
135+
// Arrange
136+
Exception cause = new IllegalArgumentException("Table not found");
137+
// Simulate the table not found scenario.
138+
when(jdbcService.getScanner(any(), any())).thenThrow(cause);
139+
140+
// Act Assert
141+
assertThatThrownBy(
142+
() -> {
143+
Scan scan = new Scan(new Key("p1", "val")).forNamespace(NAMESPACE).forTable(TABLE);
144+
jdbcDatabase.scan(scan);
145+
})
146+
.isInstanceOf(IllegalArgumentException.class);
147+
verify(connection).close();
148+
}
149+
128150
@Test
129151
public void whenPutOperationExecuted_shouldCallJdbcService() throws Exception {
130152
// Arrange
@@ -330,4 +352,30 @@ public void mutate_withConflictError_shouldThrowRetriableExecutionException()
330352
.isInstanceOf(RetriableExecutionException.class);
331353
verify(connection).close();
332354
}
355+
356+
@Test
357+
public void mutate_WhenSettingAutoCommitFails_ShouldThrowExceptionAndCloseConnection()
358+
throws SQLException, ExecutionException {
359+
// Arrange
360+
Exception exception = new RuntimeException("Failed to set auto-commit");
361+
doThrow(exception).when(connection).setAutoCommit(anyBoolean());
362+
363+
// Act Assert
364+
assertThatThrownBy(
365+
() -> {
366+
Put put =
367+
new Put(new Key("p1", "val1"))
368+
.withValue("v1", "val2")
369+
.forNamespace(NAMESPACE)
370+
.forTable(TABLE);
371+
Delete delete =
372+
new Delete(new Key("p1", "val1")).forNamespace(NAMESPACE).forTable(TABLE);
373+
jdbcDatabase.mutate(Arrays.asList(put, delete));
374+
})
375+
.isEqualTo(exception);
376+
verify(connection).setAutoCommit(false);
377+
verify(jdbcService, never()).mutate(any(), any());
378+
verify(connection, never()).rollback();
379+
verify(connection).close();
380+
}
333381
}

0 commit comments

Comments
 (0)