tsreaper commented on code in PR #1620: URL: https://github.com/apache/incubator-paimon/pull/1620#discussion_r1270146119
########## paimon-core/src/main/java/org/apache/paimon/metrics/AbstractMetricGroup.java: ########## @@ -0,0 +1,187 @@ +/* + * 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.paimon.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Contains key functionality for adding metrics and carries metrics. + * + * <p>A MetricGroup can be {@link #close() closed}. Upon closing, the group de-register all metrics. + * + * <p>The {@link #close()} method and {@link #addMetric(String, Metric)} method won't be invoked by + * multiple threads at the same time, {@link #addMetric(String, Metric)} and {@link #getMetrics()} + * have multi-threads problems, so they should be synchronized by the object lock. Review Comment: `won't be invoked by multiple threads` -> `should never be invoked by multiple threads`. `{@link #addMetric(String, Metric)} and {@link #getMetrics()} have multi-threads problems` Why? Under what circumstances? Make it clearer. ########## paimon-core/src/main/java/org/apache/paimon/metrics/AbstractMetricGroup.java: ########## @@ -0,0 +1,187 @@ +/* + * 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.paimon.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Contains key functionality for adding metrics and carries metrics. + * + * <p>A MetricGroup can be {@link #close() closed}. Upon closing, the group de-register all metrics. + * + * <p>The {@link #close()} method and {@link #addMetric(String, Metric)} method won't be invoked by + * multiple threads at the same time, {@link #addMetric(String, Metric)} and {@link #getMetrics()} + * have multi-threads problems, so they should be synchronized by the object lock. + */ +public abstract class AbstractMetricGroup implements MetricGroup { + protected static final Logger LOG = LoggerFactory.getLogger(MetricGroup.class); + + // ------------------------------------------------------------------------ + + /** The map containing all tags and their associated values, lazily computed. */ + protected Map<String, String> tags; + + /** Flag indicating whether this group has been closed. */ + private boolean closed = false; + + private final Map<String, Metric> metrics = new HashMap<>(); + + private final String table; + + // ------------------------------------------------------------------------ + + public AbstractMetricGroup(String table, @Nullable Map<String, String> tags) { + this.table = table; + this.tags = tags; Review Comment: Why is `table` so special? From my perspective it should be a tag, just like partition and bucket. ########## paimon-core/src/test/java/org/apache/paimon/metrics/MetricGroupTest.java: ########## @@ -0,0 +1,59 @@ +/* + * 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.paimon.metrics; + +import org.apache.paimon.metrics.groups.GenericMetricGroup; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** Tests for the {@link MetricGroup}. */ +public class MetricGroupTest { + @Test + public void closedGroupDoesNotRegisterMetrics() { + GenericMetricGroup group = new GenericMetricGroup("testgroup"); + assertFalse(group.isClosed()); + + group.close(); + assertTrue(group.isClosed()); + + // these will fail is the registration is propagated + group.counter("testcounter"); + group.gauge( + "testgauge", + new Gauge<Object>() { + @Override + public Object getValue() { + return null; + } + }); Review Comment: What's the purpose of this test? You just create a `counter` and a `gauge` but never modify or query them? ########## paimon-core/src/main/java/org/apache/paimon/metrics/AbstractMetricGroup.java: ########## @@ -0,0 +1,187 @@ +/* + * 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.paimon.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Contains key functionality for adding metrics and carries metrics. + * + * <p>A MetricGroup can be {@link #close() closed}. Upon closing, the group de-register all metrics. + * + * <p>The {@link #close()} method and {@link #addMetric(String, Metric)} method won't be invoked by + * multiple threads at the same time, {@link #addMetric(String, Metric)} and {@link #getMetrics()} + * have multi-threads problems, so they should be synchronized by the object lock. + */ +public abstract class AbstractMetricGroup implements MetricGroup { + protected static final Logger LOG = LoggerFactory.getLogger(MetricGroup.class); + + // ------------------------------------------------------------------------ + + /** The map containing all tags and their associated values, lazily computed. */ + protected Map<String, String> tags; + + /** Flag indicating whether this group has been closed. */ + private boolean closed = false; + + private final Map<String, Metric> metrics = new HashMap<>(); + + private final String table; + + // ------------------------------------------------------------------------ + + public AbstractMetricGroup(String table, @Nullable Map<String, String> tags) { + this.table = table; + this.tags = tags; + Metrics.getInstance().addGroup(this); + } + + @Override + public Map<String, String> getAllTags() { + return tags; + } + + /** + * Returns the fully qualified metric name using the configured delimiter for the reporter with + * the given index, for example {@code "myTable.bucket-1.metricName"}. + * + * @param metricName metric name + * @param delimiter delimiter to use + * @return fully qualified metric name + */ + public String getMetricIdentifier(String metricName, String delimiter) { + return String.join(delimiter, table, metricName); + } + + /** + * Creates and registers a new {@link org.apache.paimon.metrics.Counter}. + * + * @param name name of the counter + * @return the created counter + */ + public Counter counter(String name) { + return counter(name, new SimpleCounter()); Review Comment: From a user's perspective, what I'm expecting is that, if I call `counter(name)` with the same name twice, I should get the same `Counter` object. Your implementation will throw away the counter created earlier. ########## paimon-core/src/test/java/org/apache/paimon/metrics/groups/BucketMetricGroupTest.java: ########## @@ -0,0 +1,40 @@ +/* + * 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.paimon.metrics.groups; + +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** Tests for the {@link BucketMetricGroup}. */ +public class BucketMetricGroupTest { + // ------------------------------------------------------------------------ + // scope name tests + // ------------------------------------------------------------------------ + + @Test + public void testGenerateScopeDefault() { + BucketMetricGroup group = BucketMetricGroup.createBucketMetricGroup("myTable", 1, "dt=1"); + + assertEquals(2, group.getAllTags().size()); Review Comment: Use `assertThat(group.getAllTags().size()).isEqualTo(2)` in junit5. ########## paimon-core/src/test/java/org/apache/paimon/metrics/MetricGroupTest.java: ########## @@ -0,0 +1,59 @@ +/* + * 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.paimon.metrics; + +import org.apache.paimon.metrics.groups.GenericMetricGroup; + +import org.junit.Test; Review Comment: Use junit5 (`org.junit.jupiter.api.Test`). ########## paimon-core/src/main/java/org/apache/paimon/metrics/groups/GenericMetricGroup.java: ########## @@ -0,0 +1,36 @@ +/* + * 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.paimon.metrics.groups; + +import org.apache.paimon.metrics.AbstractMetricGroup; + +/** A simple named {@link org.apache.paimon.metrics.MetricGroup} that is untagged. */ +public class GenericMetricGroup extends AbstractMetricGroup { + + public static final String GROUP_NAME = "table"; + + public GenericMetricGroup(String table) { + super(table, null); + } + + @Override + public String getGroupName() { + return GROUP_NAME; Review Comment: Same as above. ########## paimon-core/src/main/java/org/apache/paimon/metrics/groups/BucketMetricGroup.java: ########## @@ -0,0 +1,48 @@ +/* + * 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.paimon.metrics.groups; + +import org.apache.paimon.metrics.AbstractMetricGroup; + +import java.util.HashMap; +import java.util.Map; + +/** Special {@link org.apache.paimon.metrics.MetricGroup} representing a bucket. */ +public class BucketMetricGroup extends AbstractMetricGroup { + public static final String GROUP_NAME = "bucket"; + + // ------------------------------------------------------------------------ + + BucketMetricGroup(String table, Map<String, String> tags) { + super(table, tags); + } + + public static BucketMetricGroup createBucketMetricGroup( + final String table, final int bucket, final String partition) { + Map<String, String> tags = new HashMap<>(); + tags.put("bucket", String.valueOf(bucket)); + tags.put("partition", String.valueOf(partition)); + return new BucketMetricGroup(table, tags); + } + + @Override + public String getGroupName() { + return GROUP_NAME; Review Comment: So all `BucketMetricGroup`s have the same name? This seems meaningless. ########## paimon-core/src/main/java/org/apache/paimon/metrics/Metrics.java: ########## @@ -0,0 +1,52 @@ +/* + * 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.paimon.metrics; + +import java.util.ArrayList; +import java.util.List; + +/** Core of Paimon metrics system. */ +public class Metrics { + private static volatile Metrics instance = new Metrics(); + + /** + * The metrics groups. All the commit & compaction & scan metric groups are collected in this + * group container, there is no need to distinguish the groups by group name for reporters. + */ + private final List<MetricGroup> metricGroups = new ArrayList<>(); + + private Metrics() {} + + public static Metrics getInstance() { + return instance; + } + + /** + * Add a metric group. Which is called by metrics instances, like commit / compaction metrics + * instances. + */ + public synchronized void addGroup(AbstractMetricGroup group) { + metricGroups.add(group); + } + + /** Get metric groups. */ + public synchronized List<MetricGroup> getMetricGroups() { + return metricGroups; + } Review Comment: `synchronized` on a method only guarantees that this method is not called by two threads at the same time. However, both `addGroup` and `getMetricGroup` uses `metricGroups` so there might be `ConcurrentModificationException`. Try `ConcurrentLinkedQueue`. ########## paimon-core/src/test/java/org/apache/paimon/metrics/MetricGroupTest.java: ########## @@ -0,0 +1,59 @@ +/* + * 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.paimon.metrics; + +import org.apache.paimon.metrics.groups.GenericMetricGroup; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** Tests for the {@link MetricGroup}. */ +public class MetricGroupTest { + @Test + public void closedGroupDoesNotRegisterMetrics() { + GenericMetricGroup group = new GenericMetricGroup("testgroup"); + assertFalse(group.isClosed()); + + group.close(); + assertTrue(group.isClosed()); + + // these will fail is the registration is propagated + group.counter("testcounter"); + group.gauge( + "testgauge", + new Gauge<Object>() { + @Override + public Object getValue() { + return null; + } + }); + } + + @Test + public void tolerateMetricNameCollisions() { + final String name = "abctestname"; + GenericMetricGroup group = new GenericMetricGroup("testgroup"); + + assertNotNull(group.counter(name)); + assertNotNull(group.counter(name)); + } Review Comment: What's the purpose of this test? Why not check if the two counters are the same object? ########## paimon-core/src/main/java/org/apache/paimon/metrics/MetricGroup.java: ########## @@ -0,0 +1,102 @@ +/* + * 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.paimon.metrics; + +import org.apache.paimon.annotation.Public; + +import java.util.Map; + +/** + * A MetricGroup is a named container for {@link Metric Metrics} and further metric subgroups. + * + * <p>Instances of this class can be used to register new metrics with Paimon. + */ +@Public +public interface MetricGroup { + + /** + * Creates and registers a new {@link org.apache.paimon.metrics.Counter} with Paimon. + * + * @param name name of the counter + * @return the created counter + */ + Counter counter(String name); + + /** + * Registers a {@link org.apache.paimon.metrics.Counter} with Paimon. + * + * @param name name of the counter + * @param counter counter to register + * @param <C> counter type + * @return the given counter + */ + <C extends Counter> C counter(String name, C counter); + + /** + * Registers a new {@link org.apache.paimon.metrics.Gauge} with Paimon. + * + * @param name name of the gauge + * @param gauge gauge to register + * @param <T> return type of the gauge + * @return the given gauge + */ + <T, G extends Gauge<T>> G gauge(String name, G gauge); + + /** + * Registers a new {@link Histogram} with Paimon. + * + * @param name name of the histogram + * @param histogram histogram to register + * @param <H> histogram type + * @return the registered histogram + */ + <H extends Histogram> H histogram(String name, H histogram); + + /** + * Registers a new {@link Histogram} with Paimon. + * + * @param name name of the histogram + * @param histogram histogram to register + * @param <H> histogram type + * @return the registered histogram + */ + default <H extends Histogram> H histogram(int name, H histogram) { + return histogram(String.valueOf(name), histogram); + } Review Comment: Why do we use `int` as a metric name? It seems meaningless. ########## paimon-core/src/main/java/org/apache/paimon/metrics/AbstractMetricGroup.java: ########## @@ -0,0 +1,187 @@ +/* + * 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.paimon.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Contains key functionality for adding metrics and carries metrics. + * + * <p>A MetricGroup can be {@link #close() closed}. Upon closing, the group de-register all metrics. + * + * <p>The {@link #close()} method and {@link #addMetric(String, Metric)} method won't be invoked by + * multiple threads at the same time, {@link #addMetric(String, Metric)} and {@link #getMetrics()} + * have multi-threads problems, so they should be synchronized by the object lock. + */ +public abstract class AbstractMetricGroup implements MetricGroup { + protected static final Logger LOG = LoggerFactory.getLogger(MetricGroup.class); + + // ------------------------------------------------------------------------ + + /** The map containing all tags and their associated values, lazily computed. */ + protected Map<String, String> tags; + + /** Flag indicating whether this group has been closed. */ + private boolean closed = false; + + private final Map<String, Metric> metrics = new HashMap<>(); + + private final String table; + + // ------------------------------------------------------------------------ + + public AbstractMetricGroup(String table, @Nullable Map<String, String> tags) { + this.table = table; + this.tags = tags; + Metrics.getInstance().addGroup(this); + } + + @Override + public Map<String, String> getAllTags() { + return tags; + } + + /** + * Returns the fully qualified metric name using the configured delimiter for the reporter with + * the given index, for example {@code "myTable.bucket-1.metricName"}. + * + * @param metricName metric name + * @param delimiter delimiter to use + * @return fully qualified metric name + */ + public String getMetricIdentifier(String metricName, String delimiter) { + return String.join(delimiter, table, metricName); + } + + /** + * Creates and registers a new {@link org.apache.paimon.metrics.Counter}. + * + * @param name name of the counter + * @return the created counter + */ + public Counter counter(String name) { + return counter(name, new SimpleCounter()); + } + + /** + * Registers a {@link org.apache.paimon.metrics.Counter}. + * + * @param name name of the counter + * @param counter counter to register + * @param <C> counter type + * @return the given counter + */ + public <C extends Counter> C counter(String name, C counter) { + addMetric(name, counter); + return counter; + } + + /** + * Registers a new {@link org.apache.paimon.metrics.Gauge}. + * + * @param name name of the gauge + * @param gauge gauge to register + * @param <T> return type of the gauge + * @return the given gauge + */ + public <T, G extends Gauge<T>> G gauge(String name, G gauge) { + addMetric(name, gauge); + return gauge; + } + + /** + * Registers a new {@link Histogram} with Paimon. + * + * @param name name of the histogram + * @param histogram histogram to register + * @param <H> histogram type + * @return the registered histogram + */ + public <H extends Histogram> H histogram(String name, H histogram) { + addMetric(name, histogram); + return histogram; + } + + /** + * Adds the given metric to the group and registers it at the registry, if the group is not yet + * closed, and if no metric with the same name has been registered before. + * + * @param metricName the name to register the metric under + * @param metric the metric to register + */ + protected void addMetric(String metricName, Metric metric) { + if (metric == null) { + LOG.warn( + "Ignoring attempted registration of a metric due to being null for name {}.", + metricName); + return; + } + // add the metric only if the group is still open + synchronized (this) { + if (!isClosed()) { + switch (metric.getMetricType()) { + case COUNTER: + case GAUGE: + case HISTOGRAM: + metrics.put(metricName, metric); + break; + default: + LOG.warn( + "Cannot add unknown metric type {}. This indicates that the paimon " + + "does not support this metric type.", + metric.getClass().getName()); + } + } + } + } + + @Override + public Map<String, Metric> getMetrics() { + Map<String, Metric> metricsMap = new HashMap<>(); + synchronized (this) { + metrics.forEach((k, v) -> metricsMap.put(k, v)); + } Review Comment: Use `ConcurrentHashMap`. No need for `synchronized`. -- 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]
