Copilot commented on code in PR #13769:
URL: https://github.com/apache/gravitino/pull/13769#discussion_r4245123301
##########
catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/config/JdbcConfig.java:
##########
@@ -114,6 +114,24 @@ public class JdbcConfig extends Config {
.checkValue(value -> value > 0,
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
.createWithDefault(30000L);
+ /**
+ * The canonical connect/socket timeout for a single JDBC connection, in
milliseconds. This bounds
+ * how long a connection attempt (and a subsequent socket read) may block
against an unreachable
+ * or unresponsive backend, so that create-time verification fails fast
instead of hanging. It is
+ * translated to each driver family's own connection parameters (and unit)
by {@link
+ * org.apache.gravitino.catalog.jdbc.utils.JdbcConnectionTimeouts}; an
operator value set directly
+ * on the connection or in the {@code jdbc-url} is never overridden.
+ */
+ public static final ConfigEntry<Long> CONNECT_TIMEOUT_MS =
+ new ConfigBuilder("jdbc.pool.connect-timeout-ms")
+ .doc(
+ "The connect/socket timeout for a single JDBC connection, in
milliseconds, "
+ + "translated per driver so an unreachable backend fails
fast")
+ .version(ConfigConstants.VERSION_2_0_0)
+ .longConf()
+ .checkValue(value -> value > 0,
ConfigConstants.POSITIVE_NUMBER_ERROR_MSG)
+ .createWithDefault(30000L);
Review Comment:
`JdbcCatalogOperations.initialize` filters the catalog configuration through
`JdbcCatalogPropertiesMetadata.transformProperties`, but the new key is absent
from both that metadata and its `JDBC_PROPERTIES` allowlist. Consequently, a
user-supplied `jdbc.pool.connect-timeout-ms` is discarded and every real
catalog uses 30000 ms despite the documented override. Register and retain this
entry in `JdbcCatalogPropertiesMetadata`, and cover the catalog initialization
path rather than only constructing `JdbcConfig` directly.
##########
catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java:
##########
@@ -2364,6 +2364,10 @@ void testAlterCatalogProperties() throws SQLException {
String password = MYSQL_CONTAINER.getPassword();
String wrongPassword = password + "wrong";
catalogProperties.put(JdbcConfig.PASSWORD.getKey(), wrongPassword);
+ // This test deliberately creates the catalog with a wrong password and
then fixes it via
+ // alterCatalog, so it must opt out of create-time backend verification;
otherwise the create
+ // itself would be rejected for the unreachable backend (the behavior
added in #2154).
+ catalogProperties.put(Catalog.PROPERTY_VERIFY_ON_CREATE, "false");
Review Comment:
The new default also affects
`testExistingCatalogConnectionWithUnreachableEndpoint` in this file: it creates
`127.0.0.1:1` without the opt-out and expects creation to succeed before
testing the existing catalog. Creation will now throw, so apply this same
opt-out to that test (or rewrite it to assert the new create-time failure).
##########
catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/JdbcConnectionTimeouts.java:
##########
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.gravitino.catalog.jdbc.utils;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Translates the canonical {@link JdbcConfig#CONNECT_TIMEOUT_MS
connect-timeout-ms} into the
+ * driver-family connection properties that actually bound a JDBC connection
attempt.
+ *
+ * <p>DBCP has no portable TCP-connect timeout; the real bound lives in the
driver, and both the
+ * parameter name and its unit differ per driver family:
+ *
+ * <ul>
+ * <li><b>MySQL Connector/J</b> ({@code com.mysql.cj.jdbc.Driver}, the
legacy {@code
+ * com.mysql.jdbc.Driver} — also used by Doris and StarRocks — and the
MySQL-compatible
+ * OceanBase Connector/J {@code com.oceanbase.jdbc.Driver}): {@code
connectTimeout} and {@code
+ * socketTimeout} in <b>milliseconds</b>.
+ * <li><b>PostgreSQL</b> ({@code org.postgresql.Driver}, also used by the
PostgreSQL-compatible
+ * Hologres catalog): {@code connectTimeout} and {@code socketTimeout}
in <b>seconds</b>.
+ * <li><b>ClickHouse</b> ({@code com.clickhouse.jdbc.ClickHouseDriver}):
{@code connect_timeout}
+ * and {@code socket_timeout} in <b>milliseconds</b> (note the
underscore names).
+ * </ul>
+ *
+ * <p>An unknown driver family yields no properties (logged at debug), so
adding a new JDBC provider
+ * never breaks; that provider opts in by registering its family here.
+ *
+ * <p><b>Defaults, never overrides.</b> For each property a family would emit,
an operator value is
+ * preserved when it is already present either as a DBCP connection property
key in the catalog
+ * configuration ({@link JdbcConfig#getAllConfig()}) or as a query parameter
in the {@code
+ * jdbc-url}. In that case the property is omitted from the returned map so
the operator's value
+ * wins. The URL-parameter check is case-sensitive and anchored at a parameter
boundary ({@code ?}
+ * or {@code &}), mirroring {@code MaxComputeTimeoutUtils.hasParameter}, so a
differently-cased or
+ * substring variant does not suppress injection of the canonical key.
+ *
+ * <p>The returned properties are applied as DBCP <em>connection</em>
properties (via {@code
+ * BasicDataSource.addConnectionProperty}), not raw pool keys, so they neither
collide with the
+ * unsafe pool properties nor make the pool eager; they take effect on the
first real connection,
+ * i.e. during create-time verification.
+ */
+public final class JdbcConnectionTimeouts {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(JdbcConnectionTimeouts.class);
+
+ /** The MySQL Connector/J driver class (modern). */
+ static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";
+
+ /** The MySQL Connector/J driver class (legacy, pre-8.0). */
+ static final String MYSQL_LEGACY_DRIVER = "com.mysql.jdbc.Driver";
+
+ /** The OceanBase Connector/J driver class (MySQL-compatible, milliseconds).
*/
+ static final String OCEANBASE_DRIVER = "com.oceanbase.jdbc.Driver";
+
+ /** The PostgreSQL driver class (also used by the Hologres catalog). */
+ static final String POSTGRESQL_DRIVER = "org.postgresql.Driver";
+
+ /** The ClickHouse JDBC driver class. */
+ static final String CLICKHOUSE_DRIVER =
"com.clickhouse.jdbc.ClickHouseDriver";
+
+ /** The connect/socket parameter names used by the MySQL and PostgreSQL
families (camelCase). */
+ static final String CONNECT_TIMEOUT_PARAM = "connectTimeout";
+
+ static final String SOCKET_TIMEOUT_PARAM = "socketTimeout";
+
+ /** The connect/socket parameter names used by ClickHouse (snake_case). */
+ static final String CLICKHOUSE_CONNECT_TIMEOUT_PARAM = "connect_timeout";
+
+ static final String CLICKHOUSE_SOCKET_TIMEOUT_PARAM = "socket_timeout";
+
+ private JdbcConnectionTimeouts() {
+ // Utility class, prevent instantiation.
+ }
+
+ /**
+ * Returns the driver-family connection properties that bound a single
connection to the canonical
+ * {@link JdbcConfig#getConnectTimeoutMs()} value, omitting any the operator
has already set.
+ *
+ * @param config the catalog JDBC configuration; must not be null
+ * @param jdbcUrl the JDBC URL whose query parameters are inspected for
operator-set timeouts; may
+ * be null or blank
+ * @return an ordered map of connection property name to value; empty when
the driver family is
+ * unknown or the operator has already set every relevant property
+ */
+ public static Map<String, String> connectionProperties(JdbcConfig config,
String jdbcUrl) {
+ Map<String, String> properties = new LinkedHashMap<>();
+ String driver = config.getJdbcDriver();
+ long millis = config.getConnectTimeoutMs();
+
+ String connectParam;
+ String socketParam;
+ boolean seconds;
+ if (MYSQL_DRIVER.equals(driver)
+ || MYSQL_LEGACY_DRIVER.equals(driver)
+ || OCEANBASE_DRIVER.equals(driver)) {
+ // MySQL Connector/J and the MySQL-compatible OceanBase driver expect
milliseconds.
+ connectParam = CONNECT_TIMEOUT_PARAM;
+ socketParam = SOCKET_TIMEOUT_PARAM;
+ seconds = false;
+ } else if (POSTGRESQL_DRIVER.equals(driver)) {
+ // PostgreSQL (and Hologres via the PostgreSQL driver) expect seconds.
+ connectParam = CONNECT_TIMEOUT_PARAM;
+ socketParam = SOCKET_TIMEOUT_PARAM;
+ seconds = true;
+ } else if (CLICKHOUSE_DRIVER.equals(driver)) {
+ // ClickHouse uses snake_case parameter names and expects milliseconds.
+ connectParam = CLICKHOUSE_CONNECT_TIMEOUT_PARAM;
+ socketParam = CLICKHOUSE_SOCKET_TIMEOUT_PARAM;
+ seconds = false;
+ } else {
+ LOG.debug(
+ "JDBC driver '{}' has no known connect-timeout translation; leaving
connection timeouts "
+ + "unbounded by Gravitino. Set them in the jdbc-url if needed.",
+ driver);
+ return properties;
+ }
+
+ String value = Long.toString(seconds ? millisToSeconds(millis) : millis);
+ addIfAbsent(properties, connectParam, value, config, jdbcUrl);
+ addIfAbsent(properties, socketParam, value, config, jdbcUrl);
+ return properties;
+ }
+
+ /**
+ * Converts milliseconds to seconds, rounded up with a minimum of 1 so a
sub-second bound never
+ * collapses to 0 ("no timeout").
+ *
+ * @param millis the timeout in milliseconds
+ * @return the timeout in whole seconds, at least 1
+ */
+ private static long millisToSeconds(long millis) {
+ return Math.max(1L, (millis + 999L) / 1000L);
+ }
+
+ /**
+ * Adds a connection property unless the operator has already set it (as a
config key or a {@code
+ * jdbc-url} query parameter), preserving the operator's value in that case.
+ *
+ * @param properties the map being built
+ * @param name the connection property name
+ * @param value the Gravitino default value
+ * @param config the catalog configuration
+ * @param jdbcUrl the JDBC URL
+ */
+ private static void addIfAbsent(
+ Map<String, String> properties,
+ String name,
+ String value,
+ JdbcConfig config,
+ String jdbcUrl) {
+ if (config.getAllConfig().containsKey(name) || hasUrlParameter(jdbcUrl,
name)) {
+ return;
+ }
+ properties.put(name, value);
Review Comment:
`containsKey(name)` does not detect a DBCP connection property. Actual
operator-supplied driver properties arrive inside the `connectionProperties`
value (as confirmed by `TestDataSourceUrlValidation`), so this misses them and
the later `addConnectionProperty` can overwrite their timeout. Conversely, a
raw `gravitino.bypass.connectTimeout` key makes this return even though
`BasicDataSourceFactory` ignores that unknown bean property, dropping the value
and potentially restoring MySQL's unbounded default. Inspect/merge the
datasource's real connection properties, or explicitly parse and apply both
forms before defaulting.
##########
core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java:
##########
@@ -1826,11 +1826,12 @@ private CatalogWrapper initCatalogWrapper(
catalog.properties();
catalog.capability();
- // Eagerly initialize opted-in catalogs so a misconfiguration
fails at create rather
- // than on first use. The backend translates failures into the
caller-error vs
+ // Validate opted-in catalogs so a misconfiguration fails at
create rather than on
+ // first use. The default validation eagerly initializes ops();
JDBC overrides it to
+ // run a real backend probe. The backend translates failures into
the caller-error vs
// dependency-unavailable taxonomy; both types are forwarded by
the passthrough below.
if (propsToValidate != null && catalog.shouldValidateOnCreate()) {
- catalog.ops();
+ catalog.validateOnCreate();
Review Comment:
The successful standalone `CatalogManager.testConnection` path now probes
the backend twice: `createCatalogWrapper(dummyEntity, mergedConfig)` reaches
this new JDBC validation, then line 1003 invokes `c.testConnection(...)` again.
This adds a second `listDatabases` round trip and can make a transient second
failure fail an otherwise successful check. Distinguish create-time
initialization from the standalone test path so only its explicit probe runs
there.
##########
catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalogOperations.java:
##########
@@ -248,12 +248,54 @@ public void testConnection(NameIdentifier catalogIdent) {
} catch (GravitinoRuntimeException e) {
if (e.getClass() == GravitinoRuntimeException.class && e.getCause()
instanceof SQLException) {
throw new ConnectionFailedException(
- e.getCause(), "Failed to connect to JDBC catalog: %s",
e.getMessage());
+ e.getCause(),
+ "Failed to connect to JDBC catalog at %s: %s",
+ hostPortFromJdbcUrl(jdbcUrl),
+ e.getMessage());
Review Comment:
This host-aware formatting is bypassed for failures already classified as
`ConnectionFailedException`: MySQL access-denied errors and PostgreSQL SQLSTATE
08/password failures are rethrown by the preceding branch unchanged. Re-wrap
that branch with the same host context so common create failures satisfy the
advertised error contract.
##########
catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/integration/test/CatalogClickHouseIT.java:
##########
@@ -3274,6 +3274,10 @@ void testAlterCatalogProperties() throws SQLException {
String password = CLICKHOUSE_CONTAINER.getPassword();
String wrongPassword = password + "wrong";
catalogProperties.put(JdbcConfig.PASSWORD.getKey(), wrongPassword);
+ // This test deliberately creates the catalog with a wrong password and
then fixes it via
+ // alterCatalog, so it must opt out of create-time backend verification;
otherwise the create
+ // itself would be rejected for the unreachable backend (the behavior
added in #2154).
Review Comment:
The referenced issue is unrelated to JDBC validation; this behavior is being
added for #13767, while apache/gravitino#2154 concerns `Column.equals`. Correct
the reference so future readers can find the relevant rationale.
##########
catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/JdbcConnectionTimeouts.java:
##########
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.gravitino.catalog.jdbc.utils;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Translates the canonical {@link JdbcConfig#CONNECT_TIMEOUT_MS
connect-timeout-ms} into the
+ * driver-family connection properties that actually bound a JDBC connection
attempt.
+ *
+ * <p>DBCP has no portable TCP-connect timeout; the real bound lives in the
driver, and both the
+ * parameter name and its unit differ per driver family:
+ *
+ * <ul>
+ * <li><b>MySQL Connector/J</b> ({@code com.mysql.cj.jdbc.Driver}, the
legacy {@code
+ * com.mysql.jdbc.Driver} — also used by Doris and StarRocks — and the
MySQL-compatible
+ * OceanBase Connector/J {@code com.oceanbase.jdbc.Driver}): {@code
connectTimeout} and {@code
+ * socketTimeout} in <b>milliseconds</b>.
+ * <li><b>PostgreSQL</b> ({@code org.postgresql.Driver}, also used by the
PostgreSQL-compatible
+ * Hologres catalog): {@code connectTimeout} and {@code socketTimeout}
in <b>seconds</b>.
+ * <li><b>ClickHouse</b> ({@code com.clickhouse.jdbc.ClickHouseDriver}):
{@code connect_timeout}
+ * and {@code socket_timeout} in <b>milliseconds</b> (note the
underscore names).
+ * </ul>
+ *
+ * <p>An unknown driver family yields no properties (logged at debug), so
adding a new JDBC provider
+ * never breaks; that provider opts in by registering its family here.
+ *
+ * <p><b>Defaults, never overrides.</b> For each property a family would emit,
an operator value is
+ * preserved when it is already present either as a DBCP connection property
key in the catalog
+ * configuration ({@link JdbcConfig#getAllConfig()}) or as a query parameter
in the {@code
+ * jdbc-url}. In that case the property is omitted from the returned map so
the operator's value
+ * wins. The URL-parameter check is case-sensitive and anchored at a parameter
boundary ({@code ?}
+ * or {@code &}), mirroring {@code MaxComputeTimeoutUtils.hasParameter}, so a
differently-cased or
+ * substring variant does not suppress injection of the canonical key.
+ *
+ * <p>The returned properties are applied as DBCP <em>connection</em>
properties (via {@code
+ * BasicDataSource.addConnectionProperty}), not raw pool keys, so they neither
collide with the
+ * unsafe pool properties nor make the pool eager; they take effect on the
first real connection,
+ * i.e. during create-time verification.
+ */
+public final class JdbcConnectionTimeouts {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(JdbcConnectionTimeouts.class);
+
+ /** The MySQL Connector/J driver class (modern). */
+ static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";
+
+ /** The MySQL Connector/J driver class (legacy, pre-8.0). */
+ static final String MYSQL_LEGACY_DRIVER = "com.mysql.jdbc.Driver";
+
+ /** The OceanBase Connector/J driver class (MySQL-compatible, milliseconds).
*/
+ static final String OCEANBASE_DRIVER = "com.oceanbase.jdbc.Driver";
+
+ /** The PostgreSQL driver class (also used by the Hologres catalog). */
+ static final String POSTGRESQL_DRIVER = "org.postgresql.Driver";
+
+ /** The ClickHouse JDBC driver class. */
+ static final String CLICKHOUSE_DRIVER =
"com.clickhouse.jdbc.ClickHouseDriver";
+
+ /** The connect/socket parameter names used by the MySQL and PostgreSQL
families (camelCase). */
+ static final String CONNECT_TIMEOUT_PARAM = "connectTimeout";
+
+ static final String SOCKET_TIMEOUT_PARAM = "socketTimeout";
+
+ /** The connect/socket parameter names used by ClickHouse (snake_case). */
+ static final String CLICKHOUSE_CONNECT_TIMEOUT_PARAM = "connect_timeout";
+
+ static final String CLICKHOUSE_SOCKET_TIMEOUT_PARAM = "socket_timeout";
+
+ private JdbcConnectionTimeouts() {
+ // Utility class, prevent instantiation.
+ }
+
+ /**
+ * Returns the driver-family connection properties that bound a single
connection to the canonical
+ * {@link JdbcConfig#getConnectTimeoutMs()} value, omitting any the operator
has already set.
+ *
+ * @param config the catalog JDBC configuration; must not be null
+ * @param jdbcUrl the JDBC URL whose query parameters are inspected for
operator-set timeouts; may
+ * be null or blank
+ * @return an ordered map of connection property name to value; empty when
the driver family is
+ * unknown or the operator has already set every relevant property
+ */
+ public static Map<String, String> connectionProperties(JdbcConfig config,
String jdbcUrl) {
+ Map<String, String> properties = new LinkedHashMap<>();
+ String driver = config.getJdbcDriver();
+ long millis = config.getConnectTimeoutMs();
+
+ String connectParam;
+ String socketParam;
+ boolean seconds;
+ if (MYSQL_DRIVER.equals(driver)
+ || MYSQL_LEGACY_DRIVER.equals(driver)
+ || OCEANBASE_DRIVER.equals(driver)) {
+ // MySQL Connector/J and the MySQL-compatible OceanBase driver expect
milliseconds.
+ connectParam = CONNECT_TIMEOUT_PARAM;
+ socketParam = SOCKET_TIMEOUT_PARAM;
+ seconds = false;
+ } else if (POSTGRESQL_DRIVER.equals(driver)) {
+ // PostgreSQL (and Hologres via the PostgreSQL driver) expect seconds.
+ connectParam = CONNECT_TIMEOUT_PARAM;
+ socketParam = SOCKET_TIMEOUT_PARAM;
+ seconds = true;
+ } else if (CLICKHOUSE_DRIVER.equals(driver)) {
+ // ClickHouse uses snake_case parameter names and expects milliseconds.
+ connectParam = CLICKHOUSE_CONNECT_TIMEOUT_PARAM;
+ socketParam = CLICKHOUSE_SOCKET_TIMEOUT_PARAM;
+ seconds = false;
+ } else {
+ LOG.debug(
+ "JDBC driver '{}' has no known connect-timeout translation; leaving
connection timeouts "
+ + "unbounded by Gravitino. Set them in the jdbc-url if needed.",
+ driver);
+ return properties;
+ }
+
+ String value = Long.toString(seconds ? millisToSeconds(millis) : millis);
+ addIfAbsent(properties, connectParam, value, config, jdbcUrl);
+ addIfAbsent(properties, socketParam, value, config, jdbcUrl);
+ return properties;
+ }
+
+ /**
+ * Converts milliseconds to seconds, rounded up with a minimum of 1 so a
sub-second bound never
+ * collapses to 0 ("no timeout").
+ *
+ * @param millis the timeout in milliseconds
+ * @return the timeout in whole seconds, at least 1
+ */
+ private static long millisToSeconds(long millis) {
+ return Math.max(1L, (millis + 999L) / 1000L);
Review Comment:
This conversion overflows for valid positive configuration values near
`Long.MAX_VALUE`: `millis + 999L` becomes negative, and the result silently
collapses to a one-second timeout. Use an overflow-safe ceiling division for
the validated-positive input.
##########
catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java:
##########
@@ -2364,6 +2364,10 @@ void testAlterCatalogProperties() throws SQLException {
String password = MYSQL_CONTAINER.getPassword();
String wrongPassword = password + "wrong";
catalogProperties.put(JdbcConfig.PASSWORD.getKey(), wrongPassword);
+ // This test deliberately creates the catalog with a wrong password and
then fixes it via
+ // alterCatalog, so it must opt out of create-time backend verification;
otherwise the create
+ // itself would be rejected for the unreachable backend (the behavior
added in #2154).
Review Comment:
The referenced issue is unrelated to JDBC validation; this behavior is being
added for #13767, while apache/gravitino#2154 concerns `Column.equals`. Correct
the reference so future readers can find the relevant rationale.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]