Skip to content

Polish excpetion api + build in code list that is retryable #2453

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
merged 10 commits into from
Jun 26, 2025

Conversation

mzitnik
Copy link
Contributor

@mzitnik mzitnik commented Jun 14, 2025

This pull request refactors the exception handling in the client-v2 module by reorganizing exception classes into a dedicated exception package and introducing a utility method to determine if certain server errors are retryable. Additionally, minor cleanups were made to remove unused imports.

Exception Handling Refactor:

  • Reorganized exception classes into a new exception package:

    • Moved ClientException, ServerException, and ConnectionInitiationException from com.clickhouse.client.api to com.clickhouse.client.api.exception. [1] [2] [3]
  • Enhanced ServerException:

    • Added a new isRetryable field and corresponding logic to determine if an exception is retryable based on error codes and transport protocol codes. [1] [2]
    • Introduced a Utils class in the exception package with a static method isRetryable to centralize retryability logic.

Codebase Cleanup:

  • Updated imports across the codebase:

  • Removed unused imports:

    • Cleaned up unused imports in several files, such as BinaryStreamReader and MapBackedRecord. [1] [2]## Summary
      A few enhancements to the exception mechanism.

Checklist

Delete items not relevant to your PR:

  • Closes #
  • Unit and integration tests covering the common scenarios were added
  • A human-readable description of the changes was provided to include in CHANGELOG
  • For significant changes, documentation in https://github.com/ClickHouse/clickhouse-docs was updated with further explanations or tutorials

@mzitnik mzitnik requested a review from chernser June 14, 2025 18:57
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
73.3% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@chernser
Copy link
Contributor

@mzitnik
over all looks good. Unfortunately Java is quite ugly and we may not have traits for exceptions so have to use two catch blocks.
as for moving exceptions to a separate package - I do not do that because exceptions should be close to the classes that throw them:

  • exceptions a not too special to have separate package
  • sometimes we may need exception to read package private members

@mzitnik mzitnik marked this pull request as ready for review June 26, 2025 11:39
@mzitnik mzitnik merged commit 7656f84 into main Jun 26, 2025
@mshustov
Copy link
Member

/windsurf-review

@mshustov mshustov deleted the polish-excpetion-mechanism branch June 26, 2025 11:41
Copy link

@windsurf-bot windsurf-bot bot left a comment

Choose a reason for hiding this comment

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

Other comments (4)

💡 To request another review, post a new comment with "/windsurf-review".

@@ -1464,7 +1464,8 @@ public CompletableFuture<InsertResponse> insert(String tableName,
}
}
}
throw new ClientException("Insert request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime), lastException);
LOG.warn("Insert request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime));
Copy link

Choose a reason for hiding this comment

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

The warning log is missing the exception object parameter. Consider adding the lastException as the last parameter to the LOG.warn() call to include the stack trace in logs:

Suggested change
LOG.warn("Insert request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime));
LOG.warn("Insert request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime), lastException);

@@ -1586,8 +1587,8 @@ public CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Objec
}
}
}

throw new ClientException("Query request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime), lastException);
LOG.warn("Query request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime));
Copy link

Choose a reason for hiding this comment

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

The warning log is missing the exception object parameter. Consider adding the lastException as the last parameter to the LOG.warn() call to include the stack trace in logs:

Suggested change
LOG.warn("Query request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime));
LOG.warn("Query request failed after attempts: " + (retries + 1) + " - Duration: " + (System.nanoTime() - startTime), lastException);

super(message);
this.code = code;
this.transportProtocolCode = transportProtocolCode;
this.isRetryable = discoverIsRetryable(code, message, transportProtocolCode);
Copy link

Choose a reason for hiding this comment

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

The code references this.isRetryable but there's no field declaration visible in this class. If this is inherited from ClickHouseException, consider adding a comment to clarify this, or use a more explicit reference to make the code more maintainable.

Copy link

@windsurf-bot windsurf-bot bot left a comment

Choose a reason for hiding this comment

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

Other comments (8)

💡 To request another review, post a new comment with "/windsurf-review".

@@ -379,7 +372,7 @@ public Exception readError(ClassicHttpResponse httpResponse) {
private AtomicLong timeToPoolVent = new AtomicLong(0);

public ClassicHttpResponse executeRequest(Endpoint server, Map<String, Object> requestConfig, LZ4Factory lz4Factory,
IOCallback<OutputStream> writeCallback) throws IOException {
IOCallback<OutputStream> writeCallback) throws Exception {
Copy link

Choose a reason for hiding this comment

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

The method signature has been changed from throwing IOException to throwing Exception, which is a more general exception type. This makes the method contract less specific and could lead to unexpected exception handling behavior in calling code. Consider keeping the original exception type or using a more specific exception type.

@@ -432,14 +425,10 @@

} catch (UnknownHostException e) {
LOG.warn("Host '{}' unknown", server.getBaseURL());
throw new ClientException("Unknown host", e);
throw e;
Copy link

Choose a reason for hiding this comment

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

This change removes the wrapping of UnknownHostException into ClientException. Callers might be expecting a ClientException rather than the original exception. Consider maintaining consistent exception wrapping behavior or updating all callers to handle the new exception type.

} catch (ConnectException | NoRouteToHostException e) {
LOG.warn("Failed to connect to '{}': {}", server.getBaseURL(), e.getMessage());
throw new ClientException("Failed to connect", e);
} catch (ConnectionRequestTimeoutException | ServerException | NoHttpResponseException | ClientException | SocketTimeoutException e) {
throw e;
Copy link

Choose a reason for hiding this comment

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

This change removes the wrapping of ConnectException and NoRouteToHostException into ClientException. This could break existing error handling code that expects these exceptions to be wrapped. Consider maintaining consistent exception wrapping behavior.

Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants