-
Notifications
You must be signed in to change notification settings - Fork 102
add configurable default routing group, add indicative errors, use ex… #687
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
base: main
Are you sure you want to change the base?
add configurable default routing group, add indicative errors, use ex… #687
Conversation
Thank you for your pull request and welcome to the Trino community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. Continue to work with us on the review and improvements in this PR, and submit the signed CLA to [email protected]. Photos, scans, or digitally-signed PDF files are all suitable. Processing may take a few days. The CLA needs to be on file before we merge your changes. For more information, see https://github.com/trinodb/cla |
Please resolve conflicts first :) |
@@ -100,6 +104,10 @@ public String provideBackendForRoutingGroup(String routingGroup, String user) | |||
{ | |||
List<ProxyBackendConfiguration> backends = | |||
gatewayBackendManager.getActiveBackends(routingGroup); | |||
// Check if any backends exist for the routing group (even before filtering unhealthy ones) | |||
if (backends.isEmpty()) { | |||
throw new NotFoundException("Routing group does not exist: " + routingGroup); |
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.
the error message here does not reflect what this is doing. I think something like "Cannot find any backends for routing group: " + routingGroup
is better
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.
Modified
ce3c49b
to
3def10a
Compare
Thank you for your pull request and welcome to the Trino community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. Continue to work with us on the review and improvements in this PR, and submit the signed CLA to [email protected]. Photos, scans, or digitally-signed PDF files are all suitable. Processing may take a few days. The CLA needs to be on file before we merge your changes. For more information, see https://github.com/trinodb/cla |
gateway-ha/src/main/java/io/trino/gateway/ha/config/RulesExternalConfiguration.java
Show resolved
Hide resolved
.entity(e.getMessage()) | ||
.build()); | ||
} | ||
catch (WebApplicationException e) { |
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.
why is this 'catch' needed? you just re-throw the exact error.
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.
Removed
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.
resloved
@@ -67,6 +71,7 @@ public RoutingTargetHandler( | |||
{ | |||
this.routingManager = requireNonNull(routingManager); | |||
this.routingGroupSelector = requireNonNull(routingGroupSelector); | |||
this.defaultRoutingGroup = haGatewayConfiguration.getRouting().getDefaultRoutingGroup(); |
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.
NIT: rename getRouting() to getRoutingConfiguration()
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.
For some reason this seems to be the convention of getters & setters for configuration objects within RoutingTargetHandler
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.
@vishalya would love your response
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.
I think it's ok stay with getRouting
} | ||
else { | ||
routingGroup = defaultRoutingGroup; | ||
clusterHost = routingManager.provideDefaultCluster(user); |
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.
why not use provideClusterForRoutingGroup?
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.
Modified to use provideClusterForRoutingGroup
, fixed fallback to default cluster within provideClusterForRoutingGroup
.
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.
resloved
@@ -119,6 +129,10 @@ else if (response.errors() != null && !response.errors().isEmpty()) { | |||
} | |||
return new RoutingSelectorResponse(response.routingGroup(), filteredHeaders); | |||
} | |||
catch (WebApplicationException e) { |
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.
similar to above comment, is this catch really needed?
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.
This catch throws the required errors from the external rest service as is (no response model building), any other errors are just logged
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.
resloved
CLA has been signed and sent by mail, awaits approval |
Thank you for your pull request and welcome to the Trino community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. Continue to work with us on the review and improvements in this PR, and submit the signed CLA to [email protected]. Photos, scans, or digitally-signed PDF files are all suitable. Processing may take a few days. The CLA needs to be on file before we merge your changes. For more information, see https://github.com/trinodb/cla |
1 similar comment
Thank you for your pull request and welcome to the Trino community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. Continue to work with us on the review and improvements in this PR, and submit the signed CLA to [email protected]. Photos, scans, or digitally-signed PDF files are all suitable. Processing may take a few days. The CLA needs to be on file before we merge your changes. For more information, see https://github.com/trinodb/cla |
Hey, |
gateway-ha/src/main/java/io/trino/gateway/ha/config/RulesExternalConfiguration.java
Outdated
Show resolved
Hide resolved
Thank you for your pull request and welcome to the Trino community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. Continue to work with us on the review and improvements in this PR, and submit the signed CLA to [email protected]. Photos, scans, or digitally-signed PDF files are all suitable. Processing may take a few days. The CLA needs to be on file before we merge your changes. For more information, see https://github.com/trinodb/cla |
@shanigeva Have you sent CLA doc? |
@Chaho12 , yeah CLA has been signed and sent by mail, awaits approval, maybe @mosabua can help with this |
ef4ecaa
to
7d175a6
Compare
Rebased & squashed commits |
import io.trino.gateway.ha.router.QueryCountBasedRouter; | ||
import io.trino.gateway.ha.router.RoutingManager; | ||
|
||
public class QueryCountBasedRouterProvider | ||
extends RouterBaseModule | ||
{ | ||
private final HaGatewayConfiguration configuration; |
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.
Don't put mutable objects in fields.
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.
Removed
import io.trino.gateway.ha.router.RoutingManager; | ||
import io.trino.gateway.ha.router.StochasticRoutingManager; | ||
|
||
public class StochasticRoutingManagerProvider | ||
extends RouterBaseModule | ||
{ | ||
private final HaGatewayConfiguration configuration; |
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.
Don't put mutable objects in fields.
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.
Removed
@@ -67,6 +70,7 @@ public class ExternalRoutingGroupSelector | |||
.add("Content-Length") | |||
.addAll(rulesExternalConfiguration.getExcludeHeaders()) | |||
.build(); | |||
this.propagateErrors = rulesExternalConfiguration.isPropagateErrors(); |
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.
this.
is redundant.
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.
Done
{ | ||
dao = requireNonNull(jdbi, "jdbi is null").onDemand(GatewayBackendDao.class); | ||
this.defaultRoutingGroup = requireNonNull(routingConfiguration, "routingConfiguration is null").getDefaultRoutingGroup(); |
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.
We don't use requireNonNull
for config classes.
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.
removed
public StochasticRoutingManager( | ||
GatewayBackendManager gatewayBackendManager, QueryHistoryManager queryHistoryManager) | ||
{ | ||
super(gatewayBackendManager, queryHistoryManager); | ||
this(gatewayBackendManager, queryHistoryManager, null); |
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.
I don't think we should pass null
for RoutingConfiguration
.
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.
Removed this constructor, unused
ExternalRouterResponse mockResponse = new ExternalRouterResponse( | ||
"test-group", null, ImmutableMap.of()); |
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.
Unwrap. Same for other test methods.
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.
wym?
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.
Meaning - you can have the code on a single line, the line is not long enough.
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.
done
import static io.trino.gateway.ha.TestingJdbcConnectionManager.createTestingJdbcConnectionManager; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@TestInstance(Lifecycle.PER_CLASS) |
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.
Do we really need this annotation?
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.
It is required for using @BeforeAll annotation, instead of initializing properties before each test.
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.
No need to use @BeforeAll
. Please replace it with a constructor. See https://trino.io/docs/current/develop/tests.html#use-simple-resource-initialization
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.
done
@mosabua LGTM please :) |
f6e18b3
to
34d2ca0
Compare
@vishalya Build passes locally |
34d2ca0
to
c11934c
Compare
@vishalya |
@vishalya LGTM please |
docs/routing-rules.md
Outdated
route requests. If this header is not specified, requests are sent to the default routing group. | ||
|
||
You can configure a default routing group by setting the `defaultRoutingGroup` under the `routing` section. | ||
This group will be used whenever routing information is unavailable or the external routing service fails. | ||
If not configured, the fallback remains the built-in group `adhoc`. |
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.
Please wrap at 80 characters.
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.
Minimized.
logRewrite(routingTargetResponse.routingDestination().clusterHost(), request); | ||
return routingTargetResponse; | ||
} | ||
catch (NotFoundException e) { |
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.
Which code throws NotFoundException
in this try block?
/** | ||
* @deprecated Use {@link #findActiveBackendByRoutingGroup(String)} with the configured default routing group | ||
*/ | ||
@Deprecated |
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.
Remove an unused method instead of marking as @Deprecated
.
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.
done
RoutingGroupSelector.byRoutingExternal(httpClient, config.getRoutingRules().getRulesExternalConfiguration(), | ||
config.getRequestAnalyzerConfig()), |
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.
Unwrap.
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.
done
RoutingGroupSelector.byRoutingExternal(httpClient, config.getRoutingRules().getRulesExternalConfiguration(), | ||
config.getRequestAnalyzerConfig()), |
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.
Unwrap.
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.
done
assertThat(routingSelectorResponse.routingGroup()).isNull(); | ||
assertThat(routingSelectorResponse.externalHeaders().get(headerKey)).isNull(); | ||
assertThat(routingSelectorResponse.externalHeaders().get(ROUTING_GROUP_HEADER)).isNull(); | ||
assertThat(routingSelectorResponse.externalHeaders().get(headerKey)).isEqualTo("should-be-null"); |
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.
assertThat(routingSelectorResponse.externalHeaders())
.doesNotContainKey(ROUTING_GROUP_HEADER)
.containsEntry(headerKey, "should-be-null");
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.
done
Please rebase on main to resolve conflicts. |
…ternal routing url to pass errors to client Fix configurable default routing group, add indicative errors, use external routing url to pass errors to client Fix CR comments Updated docs with PR changes Added PR changes testing Added PR testing Apply suggestions from code review Co-authored-by: Jaeho Yoo <[email protected]> Fixed review comments Fixed configuration binding
c11934c
to
99d5d71
Compare
99d5d71
to
e072b5c
Compare
Improve external routing with error propagation and configurable defaults
Description
This PR is to solve request of Git Issue #658
This PR enhances Trino Gateway's external routing capabilities with three key improvements:
Error Propagation: Ensures errors from the external routing API are properly passed to clients rather than being silently ignored, addressing issue Errors from External Routing Service Are Not Propagated to Clients #658.
Configurable Default Routing: Adds the ability to configure a default routing group when the external router fails or is unavailable, improving system resilience.
Meaningful Error Messages: Enhances error reporting with detailed, structured messages that provide clear information about routing failures.
These changes improve both the reliability and usability of Trino Gateway when using external routing services.
Additional context and related issues
The main changes include:
ExternalRoutingGroupProvider
to capture and propagate errors from the external APIRelated issues:
These enhancements make external routing more robust while giving administrators better visibility into routing problems, supporting more advanced multi-cluster deployment scenarios.
Release notes
( ) This is not user-visible or is docs only, and no release notes are required.
( ) Release notes are required. Please propose a release note for me.
(x) Release notes are required, with the following suggested text: