Skip to content

HIVE-28265: Fix JDBC timeout message for hive.query.timeout.seconds#6412

Open
ashniku wants to merge 34 commits into
apache:masterfrom
ashniku:HIVE-28265
Open

HIVE-28265: Fix JDBC timeout message for hive.query.timeout.seconds#6412
ashniku wants to merge 34 commits into
apache:masterfrom
ashniku:HIVE-28265

Conversation

@ashniku

@ashniku ashniku commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

hive.query.timeout.seconds is enforced correctly, but Beeline/JDBC reported Query timed out after 0 seconds when Statement.setQueryTimeout was not used.

This PR:

  1. SQLOperation (HiveServer2)
    Before cancel(OperationState.TIMEDOUT), set a HiveSQLException whose message is Query timed out after seconds, using the effective operation timeout ( is the same value used to schedule the cancel). GetOperationStatus then exposes the right text via operationException.
    For async execution, do not call setOperationException from the background thread if the operation is already TIMEDOUT, so the timeout message is not overwritten.

  2. HiveStatement (JDBC)
    On TIMEDOUT_STATE, prefer the server errorMessage. If it is missing or clearly wrong (contains after 0 seconds), build the client message from Statement query timeout or from the last SET hive.query.timeout.seconds=... value tracked on HiveConnection. SET is detected with a regex find() so assignments can appear inside a longer script (last match wins).

  3. HiveConnection
    Stores the last parsed hive.query.timeout.seconds from a successful SET for use in the timeout message when needed.

  4. Tests

==> testQueryTimeoutMessageUsesHiveConf: session SET hive.query.timeout.seconds=1s, no setQueryTimeout, slow query via existing SleepMsUDF — expects SQLTimeoutException and message not claiming after 0 seconds, and containing 1.
==> testQueryTimeout: same checks for the existing setQueryTimeout(1) path.

-->

Why are the changes needed?

Does this PR introduce any user-facing change?

How was this patch tested?

fail("Expecting SQLTimeoutException");
} catch (SQLTimeoutException e) {
assertNotNull(e);
assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show us an example about the whole output that demonstrates the change?

I wonder if it is possible to get any number other than the timeout in the message. Like a timestamp or maybe a query id, host name, etc. Asserting to a single number in a string looks a little bit fragile to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduced constant QUERY_TIMED_OUT_AFTER_1_SECONDS = "Query timed out after 1 seconds" with Javadoc that this is the full message from HS2 / client (no query id, host, timestamp in that string for these paths).
testQueryTimeout now uses assertEquals, expected value = that constant, with a failure message that repeats the example text.

* {@code N == 1} with flexible whitespace so we do not treat {@code 10} or unrelated digits as {@code 1}.
*/
private static boolean isQueryTimedOutAfterOneSecondMessage(String msg) {
return msg != null && msg.matches("(?is).*timed out after\\s+1\\s+seconds.*");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know it is 1 sec. And we don't accept any other output in that case.
In my opinion, regex here can be a little bit overkill.

What about something like:

final String expectedMessage = "Query timed out after 1 seconds";
assertEquals("Message should reflect JDBC query timeout", expectedMesage, message);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed isQueryTimedOutAfterOneSecondMessage (regex helper).
Positive assertions use assertEquals("…", QUERY_TIMED_OUT_AFTER_1_SECONDS, e.getMessage()) in both timeout-related tests.

assertNotNull(e);
assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(),
isQueryTimedOutAfterOneSecondMessage(e.getMessage()));
assertFalse("Message should not claim 0 seconds: " + e.getMessage(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the previous assertion, is that assertion possible at all?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(assertFalse(… "after 0 seconds") after the positive check in testQueryTimeout — “is that even possible?”)

Changes

Removed assertFalse(..., e.getMessage().contains("after 0 seconds")) from testQueryTimeout.

+ " t2 on t1.under_col = t2.under_col");
fail("Expecting SQLTimeoutException");
} catch (SQLTimeoutException e) {
assertNotNull(e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible having an exception with null value in a catch block?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(assertNotNull(e) in catch (SQLTimeoutException e) — can e be null?)

Changes

Removed assertNotNull(e) from both SQLTimeoutException catch blocks in these tests.

assertNotNull(e);
assertTrue("Message should include session timeout (1s): " + e.getMessage(),
isQueryTimedOutAfterOneSecondMessage(e.getMessage()));
assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefits of putting the ticket number into assertions or comments?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped ticket id from assertion messages.
Kept HIVE-28265 and expected message behavior in testQueryTimeoutMessageUsesHiveConf Javadoc (and the constant / assertEquals text describes behavior without the ticket).

assertNotNull(e);
assertTrue("Message should include session timeout (1s): " + e.getMessage(),
isQueryTimedOutAfterOneSecondMessage(e.getMessage()));
assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the previous assertion, is that assertion possible at all?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed assertFalse(..., "after 0 seconds") from testQueryTimeoutMessageUsesHiveConf.

* Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet.
*/
static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L;
private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking out loud: I wonder if a connection can have concurrency issue: I mean, you can have multiple individual connections to Hive, but inside a connection itself, can we have multiple hive statements in parallel?
I have no such use case in my mind, but let me ping Ayush about this question.

@ayushtkn , what do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a single JDBC Connection can be shared across multiple threads, and it is entirely possible to have multiple HiveStatement objects executing concurrently on the same connection (which maps to a single session on the HS2 side).

via Beeline or so maybe not but In Hive Server 2 (HS2), a single JDBC Connection corresponds to a single HS2 Session. You can absolutely execute multiple queries concurrently within the same session by spawning multiple threads on the client side, each using a different HiveStatement created from that single HiveConnection.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx

* Records the effective {@code hive.query.timeout.seconds} (in seconds) after a successful
* {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages.
*/
void recordSessionQueryTimeoutFromSet(long seconds) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep the getter..setter naming pattern?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recordSessionQueryTimeoutFromSet(long) → setSessionQueryTimeoutSeconds(long)
getSessionQueryTimeoutSecondsTracked() → getSessionQueryTimeoutSeconds()
HiveStatement updated to call the new names.

@ashniku ashniku left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review my change

Comment thread jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java Outdated
AtomicLong is unnecessary because we only perform simple get/set on this
field; volatile provides the required cross-thread visibility.

@ashniku ashniku left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deniskuzZ could you please review and let me know,if I have missed any

Comment thread jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java Outdated
Comment thread Jenkinsfile Outdated
Comment thread jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java Outdated
Remove redundant sessionQueryTimeoutSeconds field and parse
hive.query.timeout.seconds from connParams.getHiveConfs() on demand.
Revert out-of-scope sqlExceptionForCanceledState refactor and Jenkinsfile
change per review.
Comment thread jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java

@deniskuzZ deniskuzZ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the PR is overengineered on the client side. Once the server sends the authoritative HYT00 message, the whole HiveConnection.sessionQueryTimeoutSeconds machinery — the field/setter, applySessionQueryTimeoutFromJdbcUrl(), getSessionQueryTimeoutSeconds(), and resolveEffectiveTimeoutSecondsForMessage() — is redundant

I removed all of it and let the fallback be just the per-statement queryTimeout (which is all a pre-HYT00 server can meaningfully report). All four of your timeout tests still pass

Subject: [PATCH] patch
---
Index: itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java
--- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java	(revision 93c40e17851b3c12beee9ef711705621b85ccf66)
+++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java	(date 1783328104714)
@@ -2785,9 +2785,9 @@
   /**
    * Variant of {@link #testQueryTimeoutFromSetStatement}: the {@code SET} is issued on a separate,
    * already-closed statement; the timed-out query runs on a brand-new statement with no
-   * {@link Statement#setQueryTimeout(int)} call. The tracked session timeout lives on the
-   * {@link HiveConnection}, so it persists across statement instances and must still drive the
-   * {@link SQLTimeoutException} message correctly.
+   * {@link Statement#setQueryTimeout(int)} call. The timeout lives on the HS2 session, so it
+   * persists across client statement instances and the server-supplied {@link SQLTimeoutException}
+   * message must still reflect it correctly.
    */
   @Test
   public void testQueryTimeoutMessagePersistedAcrossStatements() throws Exception {
@@ -2801,7 +2801,7 @@
     stmt2.execute("set hive.query.timeout.seconds=1s");
     stmt2.close();
 
-    // Brand-new statement, no setQueryTimeout() call – relies solely on the tracked session value
+    // Brand-new statement, no setQueryTimeout() call – relies solely on the HS2 session timeout
     Statement stmt = con.createStatement();
     System.err.println("Executing query (expecting timeout): ");
     try {
Index: jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java
--- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java	(revision 93c40e17851b3c12beee9ef711705621b85ccf66)
+++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java	(date 1783328248965)
@@ -69,7 +69,6 @@
 import java.util.Optional;
 import java.util.Properties;
 import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Stream;
 
@@ -83,7 +82,6 @@
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.hive.common.auth.HiveAuthUtils;
-import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
@@ -165,13 +163,6 @@
  */
 public class HiveConnection implements java.sql.Connection {
   private static final Logger LOG = LoggerFactory.getLogger(HiveConnection.class);
-
-  /**
-   * Last effective {@code hive.query.timeout.seconds} in seconds, or {@code -1} if not yet set.
-   * Seeded from the JDBC URL at connect time; a JDBC {@link java.sql.Connection} may be shared
-   * across threads with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session.
-   */
-  private volatile long sessionQueryTimeoutSeconds = -1L;
   private String jdbcUriString;
   private String host;
   private int port;
@@ -199,46 +190,6 @@
 
   public TCLIService.Iface getClient() { return client; }
 
-  /**
-   * Updates the tracked {@code hive.query.timeout.seconds} value (in seconds) on this connection.
-   * Called at connect time from the JDBC URL hive-conf map, and may be called again later if needed.
-   */
-  void setSessionQueryTimeoutSeconds(long seconds) {
-    sessionQueryTimeoutSeconds = seconds;
-  }
-
-  /**
-   * If the JDBC URL supplied {@code hive.query.timeout.seconds} (via the {@code ?hive_conf_list}
-   * segment), parses and stores the value so that {@link #getSessionQueryTimeoutSeconds()} can
-   * return it for timeout error messages. This runs once at connect time and does not affect the
-   * server-side configuration, which is applied separately in {@link #openSession()}.
-   */
-  private void applySessionQueryTimeoutFromJdbcUrl() {
-    Map<String, String> hiveConfs = connParams.getHiveConfs();
-    if (hiveConfs == null || hiveConfs.isEmpty()) {
-      return;
-    }
-    String raw = hiveConfs.get(ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname);
-    if (StringUtils.isBlank(raw)) {
-      return;
-    }
-    try {
-      long sec = HiveConf.toTime(raw.trim(), TimeUnit.SECONDS, TimeUnit.SECONDS);
-      if (sec > 0) {
-        setSessionQueryTimeoutSeconds(sec);
-      }
-    } catch (Exception e) {
-      LOG.debug("Could not parse {} from JDBC URL: {}", ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, raw, e);
-    }
-  }
-
-  /**
-   * @return the tracked {@code hive.query.timeout.seconds} in seconds, or {@code -1} if not set
-   */
-  long getSessionQueryTimeoutSeconds() {
-    return sessionQueryTimeoutSeconds;
-  }
-
   /**
    * Get all direct HiveServer2 URLs from a ZooKeeper based HiveServer2 URL
    * @param zookeeperBasedHS2Url
@@ -381,7 +332,6 @@
     // hive_conf_list -> hiveConfMap
     // hive_var_list -> hiveVarMap
     sessConfMap = connParams.getSessionVars();
-    applySessionQueryTimeoutFromJdbcUrl();
     setupLoginTimeout();
     if (isKerberosAuthMode()) {
       // Ensure UserGroupInformation includes any authorized Kerberos principals.
Index: jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java
--- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java	(revision 93c40e17851b3c12beee9ef711705621b85ccf66)
+++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java	(date 1783328875508)
@@ -399,43 +399,19 @@
   }
 
   /**
-   * Returns the timeout message for a {@code TIMEDOUT_STATE} response.
-   * Uses the server error message when the SQL state is {@code HYT00} ("timeout expired"),
-   * which indicates that the server set a precise message. Otherwise falls back to a
-   * locally derived message from {@link #setQueryTimeout(int)} or the URL-seeded
-   * {@code hive.query.timeout.seconds} value on the connection.
+   * Returns the timeout message for a {@code TIMEDOUT_STATE} response. The server is authoritative:
+   * when the SQL state is {@code HYT00} ("timeout expired") it has set a precise message that
+   * already reflects the effective {@code hive.query.timeout.seconds}, so it is used verbatim.
+   * Otherwise (e.g. an older server) falls back to the per-statement {@link #setQueryTimeout(int)}.
    */
   private String sqlTimeoutMessageForTimedOutState(String serverMessage, String sqlState) {
     if ("HYT00".equals(sqlState) && StringUtils.isNotBlank(serverMessage)) {
       return serverMessage;
     }
-    long effectiveSec = resolveEffectiveTimeoutSecondsForMessage();
-    if (effectiveSec > 0) {
-      return "Query timed out after " + effectiveSec + " seconds";
-    }
-    return "Query timed out";
-  }
-
-  private long resolveEffectiveTimeoutSecondsForMessage() {
     if (queryTimeout > 0) {
-      return queryTimeout;
-    }
-    long tracked = connection.getSessionQueryTimeoutSeconds();
-    if (tracked > 0) {
-      return tracked;
+      return "Query timed out after " + queryTimeout + " seconds";
     }
-    return 0L;
-  }
-
-  private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp statusResp) {
-    final String errMsg = statusResp.getErrorMessage();
-    final String fullErrMsg;
-    if (errMsg == null || errMsg.isEmpty()) {
-      fullErrMsg = QUERY_CANCELLED_MESSAGE;
-    } else {
-      fullErrMsg = QUERY_CANCELLED_MESSAGE + " " + errMsg;
-    }
-    return new SQLException(fullErrMsg, "01000"); // SQLSTATE 01000 = warning
+    return "Query timed out";
   }
 
   /**
@@ -457,7 +433,11 @@
       isLogBeingGenerated = false;
       break;
     case CANCELED_STATE:
-      throw sqlExceptionForCanceledState(statusResp);
+      // 01000 -> warning
+      final String errMsg = statusResp.getErrorMessage();
+      final String fullErrMsg =
+          (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg;
+      throw new SQLException(fullErrMsg, "01000");
     case TIMEDOUT_STATE:
       throw new SQLTimeoutException(
           sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage(), statusResp.getSqlState()));
Index: service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java
--- a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java	(revision 93c40e17851b3c12beee9ef711705621b85ccf66)
+++ b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java	(date 1783328402146)
@@ -177,6 +177,12 @@
         timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
         timeoutExecutor.schedule(() -> {
           try {
+            // The query may have already finished, failed, or been cancelled before this task
+            // fires (the executor is only shut down when the operation is closed). Skip in that
+            // case so we don't overwrite a real result/exception with a spurious timeout message.
+            if (getState().isTerminal()) {
+              return null;
+            }
             final String queryId = queryState.getQueryId();
             log.info("Query timed out after: {} seconds. Cancelling the execution now: {}", queryTimeout, queryId);
             setOperationException(new HiveSQLException(

Remove client-side session timeout tracking; rely on server HYT00 message.
Client fallback is per-statement setQueryTimeout() only. Skip timeout cancel
when the operation is already in a terminal state.
Comment thread jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java Outdated
Follow existing getXXX naming convention in HiveConnection.

@ashniku ashniku left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deniskuzZ

Addressed the latest review feedback in the most recent push: removed the redundant sessionQueryTimeoutSeconds field from HiveConnection and now parse hive.query.timeout.seconds on demand from connParams.getHiveConfs() when building a client-side fallback message (the primary path remains the server HYT00 message from SQLOperation). Also reverted the out-of-scope sqlExceptionForCanceledState extraction in HiveStatement back to the original inline ternary for CANCELED_STATE, dropped the unrelated Jenkinsfile skipPublishingChecks change, and updated test Javadoc/comments to reflect that SET-based timeouts are covered by the HS2 timeout message rather than connection-level tracking.

CI passed. Final PR: server sets HYT00 with the correct timeout message in SQLOperation; JDBC client uses it verbatim with setQueryTimeout() as the only fallback. All client-side session timeout tracking removed per your feedback. Four timeout tests pass. Only incidental HiveConnection change is getRetryIntervalMillis() extraction for Sonar.

@ashniku ashniku left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deniskuzZ could you please check

* already reflects the effective {@code hive.query.timeout.seconds}, so it is used verbatim.
* Otherwise (e.g. an older server) falls back to the per-statement {@link #setQueryTimeout(int)}.
*/
private String sqlTimeoutMessageForTimedOutState(String serverMessage, String sqlState) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe simplify sqlTimeoutMessage() ?

Comment thread service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java Outdated
ashniku added 2 commits July 9, 2026 19:09
Centralize the guard in Operation.setOperationException() per review.
The timeout executor sets the HYT00 message before cancel(TIMEDOUT); the
background query thread must not overwrite it from its catch blocks.
Do not let a late timeout overwrite FINISHED/CANCELED outcomes. Keep ERROR
excluded so the background thread can still record the SQLException.
Synchronize the timeout handler check, setOperationException, and cancel.
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

// case so we don't overwrite a real result/exception with a spurious timeout message.
if (getState().isTerminal()) {
return null;
synchronized (SQLOperation.this) {

@deniskuzZ deniskuzZ Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you use synchronized here? timeoutExecutor is single-threaded and i don't see we synchronize anywhere else

}
// Do not overwrite a completed outcome (success, cancel, etc.). ERROR is excluded so the
// background thread can still record the SQLException after runQuery() sets ERROR.
if (state.isTerminal() && state != OperationState.ERROR) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if that doesn't make above state == OperationState.TIMEDOUT check redundant

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the synchronized block — timeoutExecutor is single-threaded and we don’t synchronize elsewhere on this path; the isTerminal() check plus setOperationException() guard is enough.

Dropped the explicit TIMEDOUT check — it was redundant since TIMEDOUT is terminal and already covered by state.isTerminal() && state != OperationState.ERROR.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment on lines +401 to +406
/**
* Returns the timeout message for a {@code TIMEDOUT_STATE} response. The server is authoritative:
* when the SQL state is {@code HYT00} ("timeout expired") it has set a precise message that
* already reflects the effective {@code hive.query.timeout.seconds}, so it is used verbatim.
* Otherwise (e.g. an older server) falls back to the per-statement {@link #setQueryTimeout(int)}.
*/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated Javadoc — the server HYT00 message reflects the effective operation timeout (min of session hive.query.timeout.seconds and setQueryTimeout()), not the session value alone

Comment on lines +407 to +415
private String sqlTimeoutMessageForTimedOutState(String serverMessage, String sqlState) {
if ("HYT00".equals(sqlState) && StringUtils.isNotBlank(serverMessage)) {
return serverMessage;
}
if (queryTimeout > 0) {
return "Query timed out after " + queryTimeout + " seconds";
}
return "Query timed out";
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sqlTimeoutMessageForTimedOutState() now accepts a server HYT00 message only when it does not contain the known-bad after 0 seconds placeholder; otherwise it falls back to the per-statement setQueryTimeout() message. Added TestHiveStatement.testIsUsableServerTimeoutMessage.

ashniku added 2 commits July 10, 2026 14:08
Ignore HYT00 server text containing "after 0 seconds" and fall back to
client timeout message. Add TestHiveStatement coverage. Remove synchronized
from timeout handler and redundant TIMEDOUT check per review.
Document that the server HYT00 message reflects the effective operation
timeout (min of session hive.query.timeout.seconds and setQueryTimeout),
not the session setting alone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants