This is an automated email from the ASF dual-hosted git repository.
jongyoul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new dfd8cf7d02 [ZEPPELIN-6481] Restore interrupt status after
InterruptedException in JDBC completion loading
dfd8cf7d02 is described below
commit dfd8cf7d02d494c886f9734af9554c4ef1ad6862
Author: HwangRock <[email protected]>
AuthorDate: Sun Jul 12 13:15:29 2026 +0900
[ZEPPELIN-6481] Restore interrupt status after InterruptedException in JDBC
completion loading
### What is this PR for?
The `catch (InterruptedException e)` block around `awaitTermination(...)`
in `createOrUpdateSqlCompleter(...)` in `JDBCInterpreter` logs a warning and
closes the connection, but never restores the interrupt status. Catching
`InterruptedException` clears the thread's interrupt flag, so the interrupt is
swallowed: callers on the completion request path can no longer detect it, and
cooperative cancellation and shutdown signals are silently dropped. This is the
classic swallowed-interrupt [...]
This restores the interrupt with `Thread.currentThread().interrupt()` as
the last statement of the block, after the connection-close attempt. Placing it
last keeps the existing resource-release behavior running first, then leaves
the interrupt status intact when the method returns so the calling thread can
detect it.
### What type of PR is it?
Bug Fix
### Todos
* [x] Restore interrupt status in the `InterruptedException` catch block
* [x] Add a unit test covering the restored interrupt status
### What is the Jira issue?
* https://issues.apache.org/jira/browse/ZEPPELIN-6481
### How should this be tested?
* `./mvnw test -pl jdbc -Dtest=JDBCInterpreterTest`
* New test `testCreateOrUpdateSqlCompleterRestoresInterruptStatusOnTimeout`
pre-interrupts the calling thread so `awaitTermination(...)` throws
`InterruptedException` immediately, drives the completer through the catch
path, and asserts `Thread.currentThread().isInterrupted()` is `true` on return.
Before the fix the flag is cleared and the assertion fails; after the fix it
passes. 32 tests in the class pass.
### Screenshots (if appropriate)
N/A
### Questions:
* Does the license files need to update? No.
* Is there breaking changes for older versions? No.
* Does this needs documentation? No.
Closes #5288 from HwangRock/ZEPPELIN-6481-pr.
Signed-off-by: Jongyoul Lee <[email protected]>
---
.../org/apache/zeppelin/jdbc/JDBCInterpreter.java | 1 +
.../apache/zeppelin/jdbc/JDBCInterpreterTest.java | 31 ++++++++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
index 196747395b..90c34614b5 100644
--- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
+++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
@@ -328,6 +328,7 @@ public class JDBCInterpreter extends KerberosInterpreter {
LOGGER.warn("Error close connection", e1);
}
}
+ Thread.currentThread().interrupt();
}
return completer;
}
diff --git
a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
index 2fe6dc3aab..cc5002b22a 100644
--- a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
+++ b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
@@ -34,6 +34,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
@@ -52,6 +53,7 @@ import net.jodah.concurrentunit.Waiter;
import static java.lang.String.format;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.COMMON_MAX_LINE;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_DRIVER;
+import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_KEY;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_PASSWORD;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_PRECODE;
import static
org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_STATEMENT_PRECODE;
@@ -510,6 +512,35 @@ public class JDBCInterpreterTest extends
BasicJDBCTestCaseAdapter {
assertEquals(true, completionList.contains(correctCompletionKeyword));
}
+ @Test
+ void testCreateOrUpdateSqlCompleterRestoresInterruptStatusOnTimeout() throws
Exception {
+ Properties properties = new Properties();
+ properties.setProperty("common.max_count", "1000");
+ properties.setProperty("common.max_retry", "3");
+ properties.setProperty("default.driver", "org.h2.Driver");
+ properties.setProperty("default.url", getJdbcConnection());
+ properties.setProperty("default.user", "");
+ properties.setProperty("default.password", "");
+ JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties);
+
+ // createOrUpdateSqlCompleter is private, so invoke it via reflection.
+ Method createOrUpdateSqlCompleter =
JDBCInterpreter.class.getDeclaredMethod(
+ "createOrUpdateSqlCompleter", SqlCompleter.class, Connection.class,
String.class,
+ String.class, int.class);
+ createOrUpdateSqlCompleter.setAccessible(true);
+
+ // Entering already interrupted makes awaitTermination throw
InterruptedException
+ // immediately, deterministically hitting the catch block under test.
+ Thread.currentThread().interrupt();
+ try {
+ createOrUpdateSqlCompleter.invoke(jdbcInterpreter, null, null,
DEFAULT_KEY, "sel", 3);
+ assertTrue(Thread.currentThread().isInterrupted());
+ } finally {
+ // Clear the interrupt flag so it doesn't leak into subsequent tests.
+ Thread.interrupted();
+ }
+ }
+
private Properties getDBProperty(String dbPrefix,
String dbUser,
String dbPassowrd) throws IOException {