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 {

Reply via email to