diff --git a/docs/quickstart-config.yaml b/docs/quickstart-config.yaml index 43182e5ff..e8254df76 100644 --- a/docs/quickstart-config.yaml +++ b/docs/quickstart-config.yaml @@ -22,11 +22,7 @@ dataStore: driver: org.postgresql.Driver clusterStatsConfiguration: - useApi: true - -backendState: - username: gateway - ssl: false + clusterStatsMonitorType: COORDINATOR_INFO modules: - io.trino.gateway.ha.module.HaGatewayProviderModule diff --git a/gateway-ha/gateway-ha-config-docker.yml b/gateway-ha/gateway-ha-config-docker.yml index 4c7e8f587..2198cbd34 100644 --- a/gateway-ha/gateway-ha-config-docker.yml +++ b/gateway-ha/gateway-ha-config-docker.yml @@ -15,12 +15,8 @@ dataStore: driver: org.postgresql.Driver queryHistoryHoursRetention: 24 -backendState: - username: lb_query - ssl: false - clusterStatsConfiguration: - useApi: true + clusterStatsMonitorType: COORDINATOR_INFO server: applicationConnectors: diff --git a/gateway-ha/gateway-ha-config.yml b/gateway-ha/gateway-ha-config.yml index d60d4ec4d..2d470a7b7 100644 --- a/gateway-ha/gateway-ha-config.yml +++ b/gateway-ha/gateway-ha-config.yml @@ -15,12 +15,8 @@ dataStore: driver: org.postgresql.Driver queryHistoryHoursRetention: 24 -backendState: - username: lb_query - ssl: false - clusterStatsConfiguration: - useApi: true + clusterStatsMonitorType: COORDINATOR_INFO server: applicationConnectors: diff --git a/gateway-ha/pom.xml b/gateway-ha/pom.xml index 18f11084e..b9a008f48 100644 --- a/gateway-ha/pom.xml +++ b/gateway-ha/pom.xml @@ -97,6 +97,22 @@ + + io.airlift + http-client + + + + io.airlift + json + + + + io.airlift + units + 1.10 + + io.dropwizard dropwizard-assets @@ -239,6 +255,13 @@ jakarta.ws.rs-api + + + joda-time + joda-time + + org.apache.commons commons-pool2 @@ -506,9 +529,22 @@ - org.apache.maven.plugins + maven-dependency-plugin + + + + + joda-time:joda-time + + + + + + + + maven-shade-plugin false diff --git a/gateway-ha/src/main/java/io/trino/client/NodeVersion.java b/gateway-ha/src/main/java/io/trino/client/NodeVersion.java new file mode 100644 index 000000000..dd3aee159 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/client/NodeVersion.java @@ -0,0 +1,67 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.client; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +// copy of https://github.com/trinodb/trino/blob/f83854687229df1ae3325249e7bcd29f03d0943c/client/trino-client/src/main/java/io/trino/client/NodeVersion.java +public class NodeVersion +{ + public static final NodeVersion UNKNOWN = new NodeVersion(""); + + private final String version; + + @JsonCreator + public NodeVersion(@JsonProperty("version") String version) + { + this.version = requireNonNull(version, "version is null"); + } + + @JsonProperty + public String getVersion() + { + return version; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + NodeVersion that = (NodeVersion) o; + return Objects.equals(version, that.version); + } + + @Override + public int hashCode() + { + return Objects.hash(version); + } + + @Override + public String toString() + { + return version; + } +} diff --git a/gateway-ha/src/main/java/io/trino/client/ServerInfo.java b/gateway-ha/src/main/java/io/trino/client/ServerInfo.java new file mode 100644 index 000000000..c6007dd7b --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/client/ServerInfo.java @@ -0,0 +1,114 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.client; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.airlift.units.Duration; + +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +//copy of https://github.com/trinodb/trino/blob/f83854687229df1ae3325249e7bcd29f03d0943c/client/trino-client/src/main/java/io/trino/client/ServerInfo.java#L28 +public class ServerInfo +{ + private final NodeVersion nodeVersion; + private final String environment; + private final boolean coordinator; + private final boolean starting; + + // optional to maintain compatibility with older servers + private final Optional uptime; + + @JsonCreator + public ServerInfo( + @JsonProperty("nodeVersion") NodeVersion nodeVersion, + @JsonProperty("environment") String environment, + @JsonProperty("coordinator") boolean coordinator, + @JsonProperty("starting") boolean starting, + @JsonProperty("uptime") Optional uptime) + { + this.nodeVersion = requireNonNull(nodeVersion, "nodeVersion is null"); + this.environment = requireNonNull(environment, "environment is null"); + this.coordinator = coordinator; + this.starting = starting; + this.uptime = requireNonNull(uptime, "uptime is null"); + } + + @JsonProperty + public NodeVersion getNodeVersion() + { + return nodeVersion; + } + + @JsonProperty + public String getEnvironment() + { + return environment; + } + + @JsonProperty + public boolean isCoordinator() + { + return coordinator; + } + + @JsonProperty + public boolean isStarting() + { + return starting; + } + + @JsonProperty + public Optional getUptime() + { + return uptime; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ServerInfo that = (ServerInfo) o; + return Objects.equals(nodeVersion, that.nodeVersion) && + Objects.equals(environment, that.environment); + } + + @Override + public int hashCode() + { + return Objects.hash(nodeVersion, environment); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("nodeVersion", nodeVersion) + .add("environment", environment) + .add("coordinator", coordinator) + .add("uptime", uptime.orElse(null)) + .omitNullValues() + .toString(); + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/clustermonitor/ClusterStatsInfoApiMonitor.java b/gateway-ha/src/main/java/io/trino/gateway/ha/clustermonitor/ClusterStatsInfoApiMonitor.java new file mode 100644 index 000000000..37fb9d6ef --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/clustermonitor/ClusterStatsInfoApiMonitor.java @@ -0,0 +1,63 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.gateway.ha.clustermonitor; + +import io.airlift.http.client.HttpClient; +import io.airlift.http.client.HttpClientConfig; +import io.airlift.http.client.Request; +import io.airlift.http.client.jetty.JettyHttpClient; +import io.airlift.json.JsonCodec; +import io.trino.client.ServerInfo; +import io.trino.gateway.ha.config.ProxyBackendConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; + +import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom; +import static io.airlift.http.client.JsonResponseHandler.createJsonResponseHandler; +import static io.airlift.http.client.Request.Builder.prepareGet; + +public class ClusterStatsInfoApiMonitor + implements ClusterStatsMonitor +{ + private static HttpClient client = new JettyHttpClient(new HttpClientConfig()); + private static final Logger log = LoggerFactory.getLogger(ClusterStatsInfoApiMonitor.class); + + @Override + public ClusterStats monitor(ProxyBackendConfiguration backend) + { + return ClusterStats.builder(backend.getName()).healthy(isReadyStatus(backend.getProxyTo())) + .proxyTo(backend.getProxyTo()) + .externalUrl(backend.getExternalUrl()) + .routingGroup(backend.getRoutingGroup()).build(); + } + + private boolean isReadyStatus(String baseUrl) + { + Request request = prepareGet() + .setUri(uriBuilderFrom(URI.create(baseUrl)).appendPath("/v1/status").build()) + .build(); + + try { + ServerInfo serverInfo = client.execute(request, createJsonResponseHandler(JsonCodec.jsonCodec(ServerInfo.class))); + return !serverInfo.isStarting(); + } + catch (Exception e) { + e.printStackTrace(); + log.error("Exception checking {}/v1/info for health: {} ", baseUrl, e.getMessage()); + } + return false; + } +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsConfiguration.java b/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsConfiguration.java index 3b84e6e5e..f3454b813 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsConfiguration.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsConfiguration.java @@ -15,17 +15,17 @@ public class ClusterStatsConfiguration { - private boolean useApi; + private ClusterStatsMonitorType clusterStatsMonitorType; public ClusterStatsConfiguration() {} - public boolean isUseApi() + public ClusterStatsMonitorType getClusterStatsMonitorType() { - return this.useApi; + return clusterStatsMonitorType; } - public void setUseApi(boolean useApi) + public void setClusterStatsMonitorType(ClusterStatsMonitorType clusterStatsMonitorType) { - this.useApi = useApi; + this.clusterStatsMonitorType = clusterStatsMonitorType; } } diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsMonitorType.java b/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsMonitorType.java new file mode 100644 index 000000000..ad7835bd2 --- /dev/null +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/config/ClusterStatsMonitorType.java @@ -0,0 +1,22 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.gateway.ha.config; + +public enum ClusterStatsMonitorType +{ + NOOP, + INFO_API, + UI_API, + JDBC +} diff --git a/gateway-ha/src/main/java/io/trino/gateway/ha/module/ClusterStatsMonitorModule.java b/gateway-ha/src/main/java/io/trino/gateway/ha/module/ClusterStatsMonitorModule.java index dd11be260..99ea41604 100644 --- a/gateway-ha/src/main/java/io/trino/gateway/ha/module/ClusterStatsMonitorModule.java +++ b/gateway-ha/src/main/java/io/trino/gateway/ha/module/ClusterStatsMonitorModule.java @@ -18,6 +18,7 @@ import io.dropwizard.core.setup.Environment; import io.trino.gateway.baseapp.AppModule; import io.trino.gateway.ha.clustermonitor.ClusterStatsHttpMonitor; +import io.trino.gateway.ha.clustermonitor.ClusterStatsInfoApiMonitor; import io.trino.gateway.ha.clustermonitor.ClusterStatsJdbcMonitor; import io.trino.gateway.ha.clustermonitor.ClusterStatsMonitor; import io.trino.gateway.ha.clustermonitor.NoopClusterStatsMonitor; @@ -41,13 +42,13 @@ public ClusterStatsMonitor getClusterStatsMonitor() { ClusterStatsConfiguration clusterStatsConfig = config.getClusterStatsConfiguration(); if (config.getBackendState() == null) { - return new NoopClusterStatsMonitor(); - } - if (clusterStatsConfig.isUseApi()) { - return new ClusterStatsHttpMonitor(config.getBackendState()); - } - else { - return new ClusterStatsJdbcMonitor(config.getBackendState()); + return new ClusterStatsInfoApiMonitor(); } + return switch (clusterStatsConfig.getClusterStatsMonitorType()) { + case INFO_API -> new ClusterStatsInfoApiMonitor(); + case UI_API -> new ClusterStatsHttpMonitor(config.getBackendState()); + case JDBC -> new ClusterStatsJdbcMonitor(config.getBackendState()); + case NOOP -> new NoopClusterStatsMonitor(); + }; } } diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/clustermonitor/TestClusterStatsMonitor.java b/gateway-ha/src/test/java/io/trino/gateway/ha/clustermonitor/TestClusterStatsMonitor.java index bfbde8f47..3c95f0bb9 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/clustermonitor/TestClusterStatsMonitor.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/clustermonitor/TestClusterStatsMonitor.java @@ -56,12 +56,22 @@ public void testJdbcMonitor() testClusterStatsMonitor(ClusterStatsJdbcMonitor::new); } + @Test + public void testInfoApiMonitor() + { + testClusterStatsMonitor(new ClusterStatsInfoApiMonitor()); + } + private void testClusterStatsMonitor(Function monitorFactory) { BackendStateConfiguration backendStateConfiguration = new BackendStateConfiguration(); backendStateConfiguration.setUsername("test_user"); ClusterStatsMonitor monitor = monitorFactory.apply(backendStateConfiguration); + testClusterStatsMonitor(monitor); + } + private void testClusterStatsMonitor(ClusterStatsMonitor monitor) + { ProxyBackendConfiguration proxyBackend = new ProxyBackendConfiguration(); proxyBackend.setProxyTo("http://localhost:" + trino.getMappedPort(8080)); proxyBackend.setName("test_cluster");