Skip to content

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:

@mzitnik mzitnik requested a review from chernser June 14, 2025 18:57
@sonarqubecloud
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
Contributor

@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".

}
}
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
Contributor

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);

}

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
Contributor

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
Contributor

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
Contributor

@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".


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

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.

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

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.

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
Contributor

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.

@sonarqubecloud
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.

4 participants