-
Notifications
You must be signed in to change notification settings - Fork 39
[ES-1884446] Classify transient/mis-categorized server errors with retryable SQL states #1430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
samikshya-db
merged 5 commits into
databricks:main
from
samikshya-db:fix/transient-error-sql-state-classification
May 12, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
d4a9367
Classify transient/mis-categorized server errors with retryable SQL s…
samikshya-db b7eeeca
Address review feedback: harden classifier and apply at all Thrift er…
samikshya-db e4e34fc
Apply SQL state classifier to SEA failed-execution path
samikshya-db 0a0b880
Drop square brackets from Spark error class matchers
samikshya-db d99144c
Merge branch 'main' into fix/transient-error-sql-state-classification
samikshya-db File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
src/main/java/com/databricks/jdbc/common/util/SqlStateClassifier.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package com.databricks.jdbc.common.util; | ||
|
|
||
| import static com.databricks.jdbc.common.DatabricksJdbcConstants.COMMUNICATION_LINK_FAILURE_SQLSTATE; | ||
| import static com.databricks.jdbc.common.DatabricksJdbcConstants.SERIALIZATION_FAILURE_SQLSTATE; | ||
|
|
||
| /** | ||
| * Reclassifies SQL states for known transient or mis-categorized server errors so callers can | ||
| * programmatically identify retryable failures. | ||
| * | ||
| * <p>Patterns handled: | ||
| * | ||
| * <ul> | ||
| * <li>Unity Catalog unavailability ({@code UC_CLIENT_EXCEPTION}) → {@code 08S01} (communication | ||
| * link failure, retryable). | ||
| * <li>Connection-acquisition / parquet read deadlines ({@code PARQUET_FAILED_READ_FOOTER}, {@code | ||
| * DEADLINE_EXCEEDED: acquiring connection}) → {@code 08S01}. | ||
| * <li>Server-side {@code java.util.ConcurrentModificationException} mis-mapped to {@code 42000} | ||
| * (syntax/access violation) → {@code 40001} (serialization failure, retryable). Only applied | ||
| * when the original SQL state is {@code 42000} so unrelated {@code 42xxx} states are | ||
| * preserved. | ||
| * </ul> | ||
| * | ||
| * <p>Patterns are anchored on stable server-emitted tokens (Spark error classes, fully-qualified | ||
| * Java exception names) rather than English prose, so server message rewording does not silently | ||
| * regress the classifier. | ||
| */ | ||
| public final class SqlStateClassifier { | ||
|
|
||
| private static final String SYNTAX_OR_ACCESS_VIOLATION_SQLSTATE = "42000"; | ||
|
|
||
| private SqlStateClassifier() {} | ||
|
|
||
| /** | ||
| * Returns a remapped SQL state if {@code errorMessage} matches a known transient pattern, or | ||
| * {@code originalSqlState} otherwise. Pure function — callers should log the remap separately | ||
| * with their own context (statement ID, response). | ||
| */ | ||
| public static String classifyTransientSqlState(String errorMessage, String originalSqlState) { | ||
| if (errorMessage == null) { | ||
| return originalSqlState; | ||
| } | ||
| // Spark error classes are the outer cause; check before nested-Java-exception patterns like | ||
| // ConcurrentModificationException, which may appear inside a UC/Parquet wrapping. | ||
| if (errorMessage.contains("UC_CLIENT_EXCEPTION") | ||
| || errorMessage.contains("PARQUET_FAILED_READ_FOOTER") | ||
| || errorMessage.contains("DEADLINE_EXCEEDED: acquiring connection")) { | ||
| return COMMUNICATION_LINK_FAILURE_SQLSTATE; | ||
| } | ||
| if (SYNTAX_OR_ACCESS_VIOLATION_SQLSTATE.equals(originalSqlState) | ||
| && errorMessage.contains("java.util.ConcurrentModificationException")) { | ||
| return SERIALIZATION_FAILURE_SQLSTATE; | ||
| } | ||
| return originalSqlState; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
src/test/java/com/databricks/jdbc/common/util/SqlStateClassifierTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| package com.databricks.jdbc.common.util; | ||
|
|
||
| import static com.databricks.jdbc.common.util.SqlStateClassifier.classifyTransientSqlState; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class SqlStateClassifierTest { | ||
|
|
||
| @Test | ||
| void unityCatalogTokenRemapsTo08S01() { | ||
| String ucMessage = | ||
| "Error running query: [UC_CLIENT_EXCEPTION] " | ||
| + "com.databricks.sql.managedcatalog.UnityCatalogClientException: " | ||
| + "[UC_CLIENT_EXCEPTION] Failed to contact the Unity Catalog server. " | ||
| + "HTTP/1.1 504 Gateway Timeout, DEADLINE_EXCEEDED"; | ||
| assertEquals("08S01", classifyTransientSqlState(ucMessage, "XXUCC")); | ||
| } | ||
|
|
||
| @Test | ||
| void parquetReadFooterTokenRemapsTo08S01() { | ||
| String message = | ||
| "Error running query: [PARQUET_FAILED_READ_FOOTER] " | ||
| + "com.databricks.sql.io.parquet.ParquetFailedReadFooterException: " | ||
| + "DEADLINE_EXCEEDED: acquiring connection"; | ||
| assertEquals("08S01", classifyTransientSqlState(message, null)); | ||
| } | ||
|
|
||
| @Test | ||
| void deadlineExceededAcquiringConnectionRemapsTo08S01() { | ||
| assertEquals( | ||
| "08S01", | ||
| classifyTransientSqlState("DEADLINE_EXCEEDED: acquiring connection from pool", null)); | ||
| } | ||
|
|
||
| @Test | ||
| void concurrentModificationGatedOn42000() { | ||
| String message = | ||
| "Error running query: java.util.ConcurrentModificationException: " | ||
| + "mutation occurred during iteration"; | ||
| assertEquals("40001", classifyTransientSqlState(message, "42000")); | ||
| } | ||
|
|
||
| @Test | ||
| void concurrentModificationDoesNotRemapWhenOriginalStateIsNot42000() { | ||
| String message = | ||
| "Error running query: java.util.ConcurrentModificationException: " | ||
| + "mutation occurred during iteration"; | ||
| assertEquals("42501", classifyTransientSqlState(message, "42501")); | ||
| assertEquals("XXUCC", classifyTransientSqlState(message, "XXUCC")); | ||
| assertNull(classifyTransientSqlState(message, null)); | ||
| } | ||
|
|
||
| @Test | ||
| void bareConcurrentModificationExceptionWithoutFqnDoesNotRemap() { | ||
| assertEquals( | ||
| "42000", | ||
| classifyTransientSqlState( | ||
| "SELECT 'ConcurrentModificationException' FROM nonexistent_tbl", "42000")); | ||
| } | ||
|
|
||
| @Test | ||
| void ucProseWithoutTokenDoesNotTriggerRemap() { | ||
| assertEquals( | ||
| "42S02", | ||
| classifyTransientSqlState( | ||
| "User-supplied literal: Failed to contact the Unity Catalog server", "42S02")); | ||
| } | ||
|
|
||
| @Test | ||
| void unrelatedErrorPreservesOriginalState() { | ||
| assertEquals( | ||
| "42S02", classifyTransientSqlState("Table or view not found: foo.bar.baz", "42S02")); | ||
| } | ||
|
|
||
| @Test | ||
| void nullMessagePreservesOriginalState() { | ||
| assertNull(classifyTransientSqlState(null, null)); | ||
| assertEquals("42S02", classifyTransientSqlState(null, "42S02")); | ||
| } | ||
|
|
||
| @Test | ||
| void emptyOriginalStateIsPreservedWhenUnrelated() { | ||
| assertEquals("", classifyTransientSqlState("Some unrelated error", "")); | ||
| } | ||
|
|
||
| @Test | ||
| void caseSensitivityIsExplicit() { | ||
| assertEquals( | ||
| "42S02", | ||
| classifyTransientSqlState("Lowercase [uc_client_exception] should not match", "42S02")); | ||
| } | ||
|
|
||
| @Test | ||
| void ucCheckRunsBeforeCmeWhenOriginalStateIs42000AndBothSubstringsPresent() { | ||
| String message = | ||
| "[UC_CLIENT_EXCEPTION] catalog server: caused by " | ||
| + "java.util.ConcurrentModificationException: nested"; | ||
| assertEquals("08S01", classifyTransientSqlState(message, "42000")); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we verify this mapping