This is an automated email from the ASF dual-hosted git repository. jackie pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push: new 09e88120c4 Test Cases for JMX -> Prom Exporter Regexps (#14155) 09e88120c4 is described below commit 09e88120c42d70346636d534749ef28ccd20d39d Author: Prashant Pandey <84911643+suddend...@users.noreply.github.com> AuthorDate: Wed Oct 23 22:01:06 2024 +0530 Test Cases for JMX -> Prom Exporter Regexps (#14155) --- .../jmx_prometheus_javaagent/configs/server.yml | 14 +- pinot-common/pom.xml | 16 + .../prometheus/BrokerPrometheusMetricsTest.java | 130 ++++++ .../ControllerPrometheusMetricsTest.java | 197 +++++++++ .../prometheus/MinionPrometheusMetricsTest.java | 128 ++++++ .../prometheus/PinotPrometheusMetricsTest.java | 469 +++++++++++++++++++++ .../prometheus/ServerPrometheusMetricsTest.java | 196 +++++++++ .../DropwizardBrokerPrometheusMetricsTest.java | 43 ++ .../DropwizardControllerPrometheusMetricsTest.java | 44 ++ .../DropwizardMinionPrometheusMetricsTest.java | 44 ++ .../DropwizardServerPrometheusMetricsTest.java | 45 ++ .../yammer/YammerBrokerPrometheusMetricsTest.java | 38 ++ .../YammerControllerPrometheusMetricsTest.java | 38 ++ .../yammer/YammerMinionPrometheusMetricsTest.java | 38 ++ .../yammer/YammerServerPrometheusMetricsTest.java | 38 ++ pom.xml | 8 + 16 files changed, 1485 insertions(+), 1 deletion(-) diff --git a/docker/images/pinot/etc/jmx_prometheus_javaagent/configs/server.yml b/docker/images/pinot/etc/jmx_prometheus_javaagent/configs/server.yml index 780c9fffc5..8751bfa517 100644 --- a/docker/images/pinot/etc/jmx_prometheus_javaagent/configs/server.yml +++ b/docker/images/pinot/etc/jmx_prometheus_javaagent/configs/server.yml @@ -94,7 +94,7 @@ rules: - pattern: "\"org\\.apache\\.pinot\\.common\\.metrics\"<type=\"ServerMetrics\", name=\"pinot\\.server\\.realtimeConsumptionExceptions\"><>(\\w+)" name: "pinot_server_realtime_consumptionExceptions_$1" cache: true -- pattern: "\"org\\.apache\\.pinot\\.common\\.metrics\"<type=\"ServerMetrics\", name=\"pinot\\.server\\.(([^.]+)\\.)?([^.]*)_(OFFLINE|REALTIME)\\-(.+)\\-(\\w+)\\.(invalidRealtimeRowsDropped|incompleteRealtimeRowsConsumed|rowsWithErrors|realtimeRowsFiltered|realtimeRowsConsumed|realtimeRowsFetched|streamConsumerCreateExceptions)\"><>(\\w+)" +- pattern: "\"org\\.apache\\.pinot\\.common\\.metrics\"<type=\"ServerMetrics\", name=\"pinot\\.server\\.(([^.]+)\\.)?([^.]*)_(OFFLINE|REALTIME)\\-(.+)\\-(\\w+)\\.(invalidRealtimeRowsDropped|incompleteRealtimeRowsConsumed|rowsWithErrors|realtimeRowsFiltered|realtimeRowsConsumed|realtimeRowsFetched|streamConsumerCreateExceptions|realtimeRowsSanitized)\"><>(\\w+)" name: "pinot_server_$7_$8" cache: true labels: @@ -132,6 +132,18 @@ rules: labels: database: "$2" table: "$1$3" +- pattern: "\"org\\.apache\\.pinot\\.common\\.metrics\"<type=\"ServerMetrics\", name=\"pinot\\.server\\.luceneIndexingDelayMs\\.(([^.]+)\\.)?([^.]*)\"><>(\\w+)" + name: "pinot_server_luceneIndexingDelayMs_$4" + cache: true + labels: + database: "$2" + table: "$1$3" +- pattern: "\"org\\.apache\\.pinot\\.common\\.metrics\"<type=\"ServerMetrics\", name=\"pinot\\.server\\.luceneIndexingDelayDocs\\.(([^.]+)\\.)?([^.]*)\"><>(\\w+)" + name: "pinot_server_luceneIndexingDelayDocs_$4" + cache: true + labels: + database: "$2" + table: "$1$3" - pattern: "\"org\\.apache\\.pinot\\.common\\.metrics\"<type=\"ServerMetrics\", name=\"pinot\\.server\\.numResizes\\.(([^.]+)\\.)?([^.]*)_(OFFLINE|REALTIME)\"><>(\\w+)" name: "pinot_server_numResizes_$5" cache: true diff --git a/pinot-common/pom.xml b/pinot-common/pom.xml index af34dd8173..af2001a9e1 100644 --- a/pinot-common/pom.xml +++ b/pinot-common/pom.xml @@ -52,6 +52,13 @@ <value>false</value> <!-- This will disable all default listeners --> </property> </properties> + <!--Disabling tests as Pinot currently uses Yammer and these tests fail for DropwizardMetricsFactory--> + <excludes> + <exclude>**/DropwizardBrokerPrometheusMetricsTest.java</exclude> + <exclude>**/DropwizardServerPrometheusMetricsTest.java</exclude> + <exclude>**/DropwizardMinionPrometheusMetricsTest.java</exclude> + <exclude>**/DropwizardControllerPrometheusMetricsTest.java</exclude> + </excludes> </configuration> </plugin> @@ -107,6 +114,10 @@ </plugins> </build> <dependencies> + <dependency> + <groupId>io.prometheus.jmx</groupId> + <artifactId>jmx_prometheus_javaagent</artifactId> + </dependency> <dependency> <groupId>org.apache.pinot</groupId> <artifactId>pinot-segment-spi</artifactId> @@ -269,6 +280,11 @@ <artifactId>equalsverifier</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.pinot</groupId> + <artifactId>pinot-dropwizard</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>net.sourceforge.fmpp</groupId> <artifactId>fmpp</artifactId> diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/BrokerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/BrokerPrometheusMetricsTest.java new file mode 100644 index 0000000000..399e5b400b --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/BrokerPrometheusMetricsTest.java @@ -0,0 +1,130 @@ +/** + * 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.pinot.common.metrics.prometheus; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.StringUtils; +import org.apache.pinot.common.metrics.BrokerGauge; +import org.apache.pinot.common.metrics.BrokerMeter; +import org.apache.pinot.common.metrics.BrokerMetrics; +import org.apache.pinot.common.metrics.BrokerTimer; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + + +public abstract class BrokerPrometheusMetricsTest extends PinotPrometheusMetricsTest { + + private static final String EXPORTED_METRIC_PREFIX = "pinot_broker_"; + + private static final String EXPORTED_METRIC_PREFIX_EXCEPTIONS = "exceptions"; + + private static final List<BrokerMeter> GLOBAL_METERS_WITH_EXCEPTIONS_PREFIX = + List.of(BrokerMeter.UNCAUGHT_GET_EXCEPTIONS, BrokerMeter.UNCAUGHT_POST_EXCEPTIONS, + BrokerMeter.QUERY_REJECTED_EXCEPTIONS, BrokerMeter.REQUEST_COMPILATION_EXCEPTIONS, + BrokerMeter.RESOURCE_MISSING_EXCEPTIONS); + + private static final List<BrokerMeter> METERS_ACCEPTING_RAW_TABLENAME = + List.of(BrokerMeter.QUERIES, BrokerMeter.NO_SERVER_FOUND_EXCEPTIONS, BrokerMeter.DOCUMENTS_SCANNED, + BrokerMeter.ENTRIES_SCANNED_IN_FILTER, BrokerMeter.BROKER_RESPONSES_WITH_UNAVAILABLE_SEGMENTS, + BrokerMeter.BROKER_RESPONSES_WITH_PARTIAL_SERVERS_RESPONDED, + BrokerMeter.BROKER_RESPONSES_WITH_PROCESSING_EXCEPTIONS, + BrokerMeter.BROKER_RESPONSES_WITH_NUM_GROUPS_LIMIT_REACHED, BrokerMeter.BROKER_RESPONSES_WITH_TIMEOUTS, + BrokerMeter.ENTRIES_SCANNED_POST_FILTER, BrokerMeter.TOTAL_SERVER_RESPONSE_SIZE, + BrokerMeter.QUERY_QUOTA_EXCEEDED); + + private BrokerMetrics _brokerMetrics; + + @BeforeClass + public void setup() + throws Exception { + _brokerMetrics = new BrokerMetrics(_pinotMetricsFactory.getPinotMetricsRegistry()); + } + + @Test(dataProvider = "brokerTimers") + public void timerTest(BrokerTimer timer) { + if (timer.isGlobal()) { + _brokerMetrics.addTimedValue(timer, 30_000, TimeUnit.MILLISECONDS); + assertTimerExportedCorrectly(timer.getTimerName(), EXPORTED_METRIC_PREFIX); + } else { + _brokerMetrics.addTimedTableValue(PinotPrometheusMetricsTest.ExportedLabelValues.TABLENAME, timer, 30_000L, + TimeUnit.MILLISECONDS); + assertTimerExportedCorrectly(timer.getTimerName(), PinotPrometheusMetricsTest.ExportedLabels.TABLENAME, + EXPORTED_METRIC_PREFIX); + } + } + + @Test(dataProvider = "brokerGauges") + public void gaugeTest(BrokerGauge gauge) { + if (gauge.isGlobal()) { + _brokerMetrics.setOrUpdateGlobalGauge(gauge, () -> 5L); + assertGaugeExportedCorrectly(gauge.getGaugeName(), EXPORTED_METRIC_PREFIX); + } else { + if (gauge == BrokerGauge.REQUEST_SIZE) { + _brokerMetrics.setOrUpdateTableGauge(PinotPrometheusMetricsTest.ExportedLabelValues.TABLENAME, gauge, 5L); + assertGaugeExportedCorrectly(gauge.getGaugeName(), PinotPrometheusMetricsTest.ExportedLabels.TABLENAME, + EXPORTED_METRIC_PREFIX); + } else { + _brokerMetrics.setOrUpdateTableGauge(TABLE_NAME_WITH_TYPE, gauge, 5L); + assertGaugeExportedCorrectly(gauge.getGaugeName(), + PinotPrometheusMetricsTest.ExportedLabels.TABLENAME_TABLETYPE, EXPORTED_METRIC_PREFIX); + } + } + } + + @Test(dataProvider = "brokerMeters") + public void meterTest(BrokerMeter meter) { + if (meter.isGlobal()) { + _brokerMetrics.addMeteredGlobalValue(meter, 5L); + if (GLOBAL_METERS_WITH_EXCEPTIONS_PREFIX.contains(meter)) { + String exportedMeterPrefix = String.format("%s_%s", EXPORTED_METRIC_PREFIX_EXCEPTIONS, + StringUtils.remove(meter.getMeterName(), "Exceptions")); + assertMeterExportedCorrectly(exportedMeterPrefix, EXPORTED_METRIC_PREFIX); + } else { + assertMeterExportedCorrectly(meter.getMeterName(), EXPORTED_METRIC_PREFIX); + } + } else { + if (METERS_ACCEPTING_RAW_TABLENAME.contains(meter)) { + _brokerMetrics.addMeteredTableValue(PinotPrometheusMetricsTest.ExportedLabelValues.TABLENAME, meter, 5L); + assertMeterExportedCorrectly(meter.getMeterName(), PinotPrometheusMetricsTest.ExportedLabels.TABLENAME, + EXPORTED_METRIC_PREFIX); + } else { + _brokerMetrics.addMeteredTableValue(TABLE_NAME_WITH_TYPE, meter, 5L); + assertMeterExportedCorrectly(meter.getMeterName(), + PinotPrometheusMetricsTest.ExportedLabels.TABLENAME_TABLETYPE, EXPORTED_METRIC_PREFIX); + } + } + } + + @DataProvider(name = "brokerTimers") + public Object[] brokerTimers() { + return BrokerTimer.values(); + } + + @DataProvider(name = "brokerMeters") + public Object[] brokerMeters() { + return BrokerMeter.values(); + } + + @DataProvider(name = "brokerGauges") + public Object[] brokerGauges() { + return BrokerGauge.values(); + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java new file mode 100644 index 0000000000..7fcb76eae1 --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ControllerPrometheusMetricsTest.java @@ -0,0 +1,197 @@ +/** + * 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.pinot.common.metrics.prometheus; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.StringUtils; +import org.apache.helix.task.TaskState; +import org.apache.pinot.common.metrics.ControllerGauge; +import org.apache.pinot.common.metrics.ControllerMeter; +import org.apache.pinot.common.metrics.ControllerMetrics; +import org.apache.pinot.common.metrics.ControllerTimer; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + + +public abstract class ControllerPrometheusMetricsTest extends PinotPrometheusMetricsTest { + //all exported controller metrics have this prefix + private static final String EXPORTED_METRIC_PREFIX = "pinot_controller_"; + private static final String LABEL_KEY_TASK_TYPE = "taskType"; + + //that accept global gauge with suffix + private static final List<ControllerGauge> GLOBAL_GAUGES_ACCEPTING_TASKTYPE = + List.of(ControllerGauge.NUM_MINION_TASKS_IN_PROGRESS, ControllerGauge.NUM_MINION_SUBTASKS_RUNNING, + ControllerGauge.NUM_MINION_SUBTASKS_WAITING, ControllerGauge.NUM_MINION_SUBTASKS_ERROR, + ControllerGauge.PERCENT_MINION_SUBTASKS_IN_QUEUE, ControllerGauge.PERCENT_MINION_SUBTASKS_IN_ERROR); + + //local gauges that accept partition + private static final List<ControllerGauge> GAUGES_ACCEPTING_PARTITION = + List.of(ControllerGauge.MAX_RECORDS_LAG, ControllerGauge.MAX_RECORD_AVAILABILITY_LAG_MS); + + //these accept task type + private static final List<ControllerGauge> GAUGES_ACCEPTING_TASKTYPE = + List.of(ControllerGauge.TIME_MS_SINCE_LAST_MINION_TASK_METADATA_UPDATE, + ControllerGauge.TIME_MS_SINCE_LAST_SUCCESSFUL_MINION_TASK_GENERATION, + ControllerGauge.LAST_MINION_TASK_GENERATION_ENCOUNTERS_ERROR); + + private static final List<ControllerGauge> GAUGES_ACCEPTING_RAW_TABLENAME = + List.of(ControllerGauge.OFFLINE_TABLE_ESTIMATED_SIZE); + + private ControllerMetrics _controllerMetrics; + + @BeforeClass + public void setup() + throws Exception { + _controllerMetrics = new ControllerMetrics(_pinotMetricsFactory.getPinotMetricsRegistry()); + } + + @Test(dataProvider = "controllerTimers") + public void timerTest(ControllerTimer controllerTimer) { + if (controllerTimer.isGlobal()) { + _controllerMetrics.addTimedValue(controllerTimer, 30_000, TimeUnit.MILLISECONDS); + assertTimerExportedCorrectly(controllerTimer.getTimerName(), EXPORTED_METRIC_PREFIX); + } else { + _controllerMetrics.addTimedTableValue(TABLE_NAME_WITH_TYPE, controllerTimer, 30_000L, TimeUnit.MILLISECONDS); + _controllerMetrics.addTimedTableValue(ExportedLabelValues.TABLENAME, controllerTimer, 30_000L, + TimeUnit.MILLISECONDS); + + assertTimerExportedCorrectly(controllerTimer.getTimerName(), ExportedLabels.TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + assertTimerExportedCorrectly(controllerTimer.getTimerName(), ExportedLabels.TABLENAME, EXPORTED_METRIC_PREFIX); + } + } + + @Test(dataProvider = "controllerMeters") + public void meterTest(ControllerMeter meter) { + if (meter.isGlobal()) { + _controllerMetrics.addMeteredGlobalValue(meter, 5L); + String meterName = meter.getMeterName(); + //some meters contain a "controller" prefix. For example, controllerInstancePostError. These meters are + // exported as 'pinot_controller_pinot_controller_InstancePostError'. So we strip the 'controller' from + // 'controllerInstancePostError' + String strippedMeterName = StringUtils.remove(meterName, "controller"); + assertMeterExportedCorrectly(strippedMeterName, EXPORTED_METRIC_PREFIX); + } else { + + String meterName = meter.getMeterName(); + String strippedMeterName = StringUtils.remove(meterName, "controller"); + + if (meter == ControllerMeter.CONTROLLER_PERIODIC_TASK_ERROR) { + addMeterWithLabels(meter, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC); + assertMeterExportedCorrectly(meterName, + List.of(ExportedLabelKeys.TABLE, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC), EXPORTED_METRIC_PREFIX); + } else if (meter == ControllerMeter.CONTROLLER_PERIODIC_TASK_RUN) { + addMeterWithLabels(meter, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC); + assertMeterExportedCorrectly( + String.format("%s_%s", strippedMeterName, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC), + EXPORTED_METRIC_PREFIX); + } else if (meter == ControllerMeter.PERIODIC_TASK_ERROR) { + addMeterWithLabels(meter, TABLE_NAME_WITH_TYPE + "." + ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC); + assertMeterExportedCorrectly(meterName, ExportedLabels.CONTROLLER_TASKTYPE_TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + } else { + addMeterWithLabels(meter, TABLE_NAME_WITH_TYPE); + addMeterWithLabels(meter, ExportedLabelValues.TABLENAME); + if (meter == ControllerMeter.CONTROLLER_TABLE_SEGMENT_UPLOAD_ERROR) { + assertMeterExportedCorrectly(meterName, ExportedLabels.TABLENAME_TABLETYPE, EXPORTED_METRIC_PREFIX); + assertMeterExportedCorrectly(meterName, ExportedLabels.TABLENAME, EXPORTED_METRIC_PREFIX); + } else { + assertMeterExportedCorrectly(strippedMeterName, ExportedLabels.TABLENAME_TABLETYPE, EXPORTED_METRIC_PREFIX); + assertMeterExportedCorrectly(strippedMeterName, ExportedLabels.TABLENAME, EXPORTED_METRIC_PREFIX); + } + } + } + } + + @Test(dataProvider = "controllerGauges") + public void gaugeTest(ControllerGauge controllerGauge) { + if (controllerGauge.isGlobal()) { + _controllerMetrics.setValueOfGlobalGauge(controllerGauge, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, 1L); + //some global gauges also accept the taskType (which should not be). todo: this should be fixed + if (GLOBAL_GAUGES_ACCEPTING_TASKTYPE.contains(controllerGauge)) { + _controllerMetrics.setValueOfGlobalGauge(controllerGauge, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, 1L); + String strippedMetricName = getStrippedMetricName(controllerGauge); + assertGaugeExportedCorrectly(strippedMetricName, + List.of(LABEL_KEY_TASK_TYPE, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC), EXPORTED_METRIC_PREFIX); + } else { + _controllerMetrics.setValueOfGlobalGauge(controllerGauge, 1L); + String strippedMetricName = getStrippedMetricName(controllerGauge); + assertGaugeExportedCorrectly(strippedMetricName, EXPORTED_METRIC_PREFIX); + } + } else { + if (GAUGES_ACCEPTING_PARTITION.contains(controllerGauge)) { + _controllerMetrics.setValueOfPartitionGauge(TABLE_NAME_WITH_TYPE, 3, controllerGauge, 10L); + String strippedGaugeName = getStrippedMetricName(controllerGauge); + assertGaugeExportedCorrectly(strippedGaugeName, ExportedLabels.PARTITION_TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + } else if (GAUGES_ACCEPTING_TASKTYPE.contains(controllerGauge)) { + _controllerMetrics.setOrUpdateTableGauge(TABLE_NAME_WITH_TYPE, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, + controllerGauge, () -> 50L); + assertGaugeExportedCorrectly(controllerGauge.getGaugeName(), + ExportedLabels.TABLENAME_TABLETYPE_CONTROLLER_TASKTYPE, EXPORTED_METRIC_PREFIX); + } else if (GAUGES_ACCEPTING_RAW_TABLENAME.contains(controllerGauge)) { + addGaugeWithLabels(controllerGauge, ExportedLabelValues.TABLENAME); + assertGaugeExportedCorrectly(controllerGauge.getGaugeName(), ExportedLabels.TABLENAME, EXPORTED_METRIC_PREFIX); + } else if (controllerGauge == ControllerGauge.CRON_SCHEDULER_JOB_SCHEDULED) { + addGaugeWithLabels(controllerGauge, + String.format("%s.%s", TABLE_NAME_WITH_TYPE, ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC)); + assertGaugeExportedCorrectly(ControllerGauge.CRON_SCHEDULER_JOB_SCHEDULED.getGaugeName(), + ExportedLabels.TABLENAMEWITHTYPE_CONTROLLER_TASKTYPE, EXPORTED_METRIC_PREFIX); + } else if (controllerGauge == ControllerGauge.TASK_STATUS) { + addGaugeWithLabels(controllerGauge, + String.format("%s.%s", ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC, TaskState.IN_PROGRESS)); + assertGaugeExportedCorrectly(ControllerGauge.TASK_STATUS.getGaugeName(), + ExportedLabels.JOBSTATUS_CONTROLLER_TASKTYPE, EXPORTED_METRIC_PREFIX); + } else { + addGaugeWithLabels(controllerGauge, TABLE_NAME_WITH_TYPE); + assertGaugeExportedCorrectly(controllerGauge.getGaugeName(), ExportedLabels.TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + } + } + } + + private void addGaugeWithLabels(ControllerGauge gauge, String labels) { + _controllerMetrics.setValueOfTableGauge(labels, gauge, 5L); + } + + private static String getStrippedMetricName(ControllerGauge controllerGauge) { + return StringUtils.remove(controllerGauge.getGaugeName(), "controller"); + } + + private void addMeterWithLabels(ControllerMeter meter, String labels) { + _controllerMetrics.addMeteredTableValue(labels, meter, 1L); + } + + @DataProvider(name = "controllerTimers") + public Object[] controllerTimers() { + return ControllerTimer.values(); + } + + @DataProvider(name = "controllerMeters") + public Object[] controllerMeters() { + return ControllerMeter.values(); + } + + @DataProvider(name = "controllerGauges") + public Object[] controllerGauges() { + return ControllerGauge.values(); + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java new file mode 100644 index 0000000000..35a766b0ca --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/MinionPrometheusMetricsTest.java @@ -0,0 +1,128 @@ +/** + * 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.pinot.common.metrics.prometheus; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.pinot.common.metrics.MinionGauge; +import org.apache.pinot.common.metrics.MinionMeter; +import org.apache.pinot.common.metrics.MinionMetrics; +import org.apache.pinot.common.metrics.MinionTimer; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + + +public abstract class MinionPrometheusMetricsTest extends PinotPrometheusMetricsTest { + //all exported minion metrics have this prefix + private static final String EXPORTED_METRIC_PREFIX = "pinot_minion_"; + private static final String METER_PREFIX_NO_TASKS = "numberTasks"; + + private MinionMetrics _minionMetrics; + + @BeforeClass + public void setup() { + _minionMetrics = new MinionMetrics(_pinotMetricsFactory.getPinotMetricsRegistry()); + } + + @Test(dataProvider = "minionTimers") + public void timerTest(MinionTimer timer) { + + _minionMetrics.addTimedValue(ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, timer, 30L, TimeUnit.MILLISECONDS); + assertTimerExportedCorrectly(timer.getTimerName(), + List.of(ExportedLabelKeys.ID, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT), EXPORTED_METRIC_PREFIX); + + _minionMetrics.addTimedTableValue(TABLE_NAME_WITH_TYPE, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, timer, 30L, + TimeUnit.MILLISECONDS); + + if (timer == MinionTimer.TASK_THREAD_CPU_TIME_NS) { + assertTimerExportedCorrectly(timer.getTimerName(), + List.of(ExportedLabelKeys.DATABASE, ExportedLabelValues.TABLENAME_WITH_TYPE_REALTIME, ExportedLabelKeys.TABLE, + "myTable_REALTIME.SegmentImportTask"), EXPORTED_METRIC_PREFIX); + } else { + assertTimerExportedCorrectly(timer.getTimerName(), ExportedLabels.TABLENAME_TABLETYPE_MINION_TASKTYPE, + EXPORTED_METRIC_PREFIX); + } + } + + @Test(dataProvider = "minionMeters") + public void meterTest(MinionMeter meter) { + if (meter.isGlobal()) { + validateGlobalMeters(meter); + } else { + validateMetersWithLabels(meter); + } + } + + private void validateGlobalMeters(MinionMeter meter) { + _minionMetrics.addMeteredGlobalValue(meter, 5L); + assertMeterExportedCorrectly(meter.getMeterName(), EXPORTED_METRIC_PREFIX); + } + + private void validateMetersWithLabels(MinionMeter meter) { + if (meter.getMeterName().startsWith(METER_PREFIX_NO_TASKS)) { + _minionMetrics.addMeteredTableValue(ExportedLabelValues.TABLENAME, meter, 1L); + assertMeterExportedCorrectly(meter.getMeterName(), List.of(ExportedLabelKeys.ID, ExportedLabelValues.TABLENAME), + EXPORTED_METRIC_PREFIX); + + _minionMetrics.addMeteredValue(ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, meter, 1L); + assertMeterExportedCorrectly(meter.getMeterName(), + List.of(ExportedLabelKeys.ID, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT), EXPORTED_METRIC_PREFIX); + } else if (meter == MinionMeter.SEGMENT_UPLOAD_FAIL_COUNT || meter == MinionMeter.SEGMENT_DOWNLOAD_FAIL_COUNT) { + + _minionMetrics.addMeteredTableValue(TABLE_NAME_WITH_TYPE, meter, 1L); + assertMeterExportedCorrectly(meter.getMeterName(), List.of(ExportedLabelKeys.ID, TABLE_NAME_WITH_TYPE), + EXPORTED_METRIC_PREFIX); + } else { + //all remaining meters are also being used as global meters, check their usage + _minionMetrics.addMeteredGlobalValue(meter, 1L); + _minionMetrics.addMeteredTableValue(TABLE_NAME_WITH_TYPE, ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT, meter, + 1L); + assertMeterExportedCorrectly(meter.getMeterName(), EXPORTED_METRIC_PREFIX); + assertMeterExportedCorrectly(meter.getMeterName(), ExportedLabels.TABLENAME_TABLETYPE_MINION_TASKTYPE, + EXPORTED_METRIC_PREFIX); + } + } + + @Test(dataProvider = "minionGauges") + public void gaugeTest(MinionGauge gauge) { + if (gauge.isGlobal()) { + _minionMetrics.setValueOfGlobalGauge(gauge, 1L); + assertGaugeExportedCorrectly(gauge.getGaugeName(), EXPORTED_METRIC_PREFIX); + } else { + _minionMetrics.setOrUpdateTableGauge(TABLE_NAME_WITH_TYPE, gauge, 1L); + assertGaugeExportedCorrectly(gauge.getGaugeName(), ExportedLabels.TABLENAME_TABLETYPE, EXPORTED_METRIC_PREFIX); + } + } + + @DataProvider(name = "minionTimers") + public Object[] minionTimers() { + return MinionTimer.values(); + } + + @DataProvider(name = "minionMeters") + public Object[] minionMeters() { + return MinionMeter.values(); + } + + @DataProvider(name = "minionGauges") + public Object[] minionGauges() { + return MinionGauge.values(); + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java new file mode 100644 index 0000000000..a3f21ad91d --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/PinotPrometheusMetricsTest.java @@ -0,0 +1,469 @@ +/** + * 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.pinot.common.metrics.prometheus; + +import com.google.common.base.Objects; +import com.google.common.io.Resources; +import io.prometheus.jmx.JmxCollector; +import io.prometheus.jmx.common.http.HTTPServerFactory; +import io.prometheus.jmx.shaded.io.prometheus.client.CollectorRegistry; +import io.prometheus.jmx.shaded.io.prometheus.client.exporter.HTTPServer; +import io.prometheus.jmx.shaded.io.prometheus.client.hotspot.DefaultExports; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.pinot.common.utils.SimpleHttpResponse; +import org.apache.pinot.common.utils.http.HttpClient; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.apache.pinot.spi.config.table.TableType; +import org.apache.pinot.spi.env.PinotConfiguration; +import org.apache.pinot.spi.metrics.PinotMetricUtils; +import org.apache.pinot.spi.utils.builder.TableNameBuilder; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; + +import static org.apache.pinot.common.metrics.prometheus.PinotPrometheusMetricsTest.ExportedLabelKeys.*; +import static org.apache.pinot.common.metrics.prometheus.PinotPrometheusMetricsTest.ExportedLabelValues.CONTROLLER_PERIODIC_TASK_CHC; +import static org.apache.pinot.common.metrics.prometheus.PinotPrometheusMetricsTest.ExportedLabelValues.IN_PROGRESS; +import static org.apache.pinot.common.metrics.prometheus.PinotPrometheusMetricsTest.ExportedLabelValues.TABLENAME_WITH_TYPE_REALTIME; +import static org.apache.pinot.common.metrics.prometheus.PinotPrometheusMetricsTest.ExportedLabelValues.TABLETYPE_REALTIME; +import static org.apache.pinot.spi.utils.CommonConstants.CONFIG_OF_METRICS_FACTORY_CLASS_NAME; + + +public abstract class PinotPrometheusMetricsTest { + + protected static final String TABLE_NAME_WITH_TYPE = + TableNameBuilder.forType(TableType.REALTIME).tableNameWithType(ExportedLabelValues.TABLENAME); + protected static final String KAFKA_TOPIC = "myTopic"; + protected static final String PARTITION_GROUP_ID = "partitionGroupId"; + protected static final String CLIENT_ID = + String.format("%s-%s-%s", TABLE_NAME_WITH_TYPE, KAFKA_TOPIC, PARTITION_GROUP_ID); + + protected HttpClient _httpClient; + + protected PinotMetricsFactory _pinotMetricsFactory; + + //each meter defined in code is exported with these measurements + private static final List<String> METER_TYPES = + List.of("Count", "FiveMinuteRate", "MeanRate", "OneMinuteRate", "FifteenMinuteRate"); + + //each timer defined in code is exported with these measurements + private static final List<String> TIMER_TYPES = + List.of("Count", "FiveMinuteRate", "Max", "999thPercentile", "95thPercentile", "75thPercentile", "98thPercentile", + "OneMinuteRate", "50thPercentile", "99thPercentile", "FifteenMinuteRate", "Mean", "StdDev", "MeanRate", + "Min"); + + //each gauge defined in code is exported with these measurements + private static final List<String> GAUGE_TYPES = List.of("Value"); + + private HTTPServer _httpServer; + + @BeforeClass + public void setupTest() { + PinotConfiguration pinotConfiguration = new PinotConfiguration(); + + _pinotMetricsFactory = getPinotMetricsFactory(); + pinotConfiguration.setProperty(CONFIG_OF_METRICS_FACTORY_CLASS_NAME, + _pinotMetricsFactory.getClass().getCanonicalName()); + PinotMetricUtils.init(pinotConfiguration); + + _pinotMetricsFactory.makePinotJmxReporter(_pinotMetricsFactory.getPinotMetricsRegistry()).start(); + _httpClient = new HttpClient(); + _httpServer = startExporter(); + } + + @AfterClass + public void cleanup() { + _httpServer.close(); + } + + /** + * Pinot currently uses the JMX->Prom exporter to export metrics to Prometheus. Normally, this runs as an agent in the + * JVM. In this case however, we've got tests using four different config files (server.yml, broker.yml, + * controller.yml and minion.yml). Loading the same agent in the same JVM multiple times isn't allowed, we are copying + * the agent's code to some degree and starting up the HTTP servers manually. For impl, see: + * <a href="https://github.com/prometheus/jmx_exporter/blob/a3b9443564ff5a78c25fd6566396fda2b7cbf216">...</a> + * /jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java#L48 + * + * @return the corresponding HTTP server on a random unoccupied port + */ + protected HTTPServer startExporter() { + String args = String.format("%s:%s", 0, getConfigFile()); + try { + JMXExporterConfig config = parseExporterConfig(args, "0.0.0.0"); + CollectorRegistry registry = new CollectorRegistry(); + JmxCollector jmxCollector = new JmxCollector(new File(config._file), JmxCollector.Mode.AGENT); + jmxCollector.register(registry); + DefaultExports.register(registry); + return (new HTTPServerFactory()).createHTTPServer(config._socket, registry, true, new File(config._file)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void assertGaugeExportedCorrectly(String exportedGaugePrefix, String exportedMetricPrefix) { + List<PromMetric> promMetrics; + try { + promMetrics = parseExportedPromMetrics(getExportedPromMetrics().getResponse()); + for (String gaugeType : GAUGE_TYPES) { + PromMetric expectedMetric = PromMetric.withName(exportedMetricPrefix + exportedGaugePrefix + "_" + gaugeType); + Assert.assertTrue(promMetrics.contains(expectedMetric), + "Cannot find gauge: " + expectedMetric + " in exported metrics"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void assertGaugeExportedCorrectly(String exportedGaugePrefix, List<String> labels, + String exportedMetricPrefix) { + List<PromMetric> promMetrics; + try { + promMetrics = parseExportedPromMetrics(getExportedPromMetrics().getResponse()); + for (String gaugeType : GAUGE_TYPES) { + PromMetric expectedGauge = + PromMetric.withNameAndLabels(exportedMetricPrefix + exportedGaugePrefix + "_" + gaugeType, labels); + Assert.assertTrue(promMetrics.contains(expectedGauge), + "Cannot find gauge: " + expectedGauge + " in exported metrics"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void assertTimerExportedCorrectly(String exportedTimerPrefix, String exportedMetricPrefix) { + List<PromMetric> promMetrics; + try { + promMetrics = parseExportedPromMetrics(getExportedPromMetrics().getResponse()); + for (String meterType : TIMER_TYPES) { + PromMetric expectedTimer = PromMetric.withName(exportedMetricPrefix + exportedTimerPrefix + "_" + meterType); + Assert.assertTrue(promMetrics.contains(expectedTimer), + "Cannot find timer: " + expectedTimer + " in exported metrics"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void assertTimerExportedCorrectly(String exportedTimerPrefix, List<String> labels, + String exportedMetricPrefix) { + List<PromMetric> promMetrics; + try { + promMetrics = parseExportedPromMetrics(getExportedPromMetrics().getResponse()); + for (String meterType : METER_TYPES) { + Assert.assertTrue(promMetrics.contains( + PromMetric.withNameAndLabels(exportedMetricPrefix + exportedTimerPrefix + "_" + meterType, labels)), + exportedTimerPrefix); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void assertMeterExportedCorrectly(String exportedMeterPrefix, String exportedMetricPrefix) { + List<PromMetric> promMetrics; + try { + promMetrics = parseExportedPromMetrics(getExportedPromMetrics().getResponse()); + } catch (Exception e) { + throw new RuntimeException(e); + } + for (String meterType : METER_TYPES) { + PromMetric expectedMetric = PromMetric.withName(exportedMetricPrefix + exportedMeterPrefix + "_" + meterType); + Assert.assertTrue(promMetrics.contains(expectedMetric), + "Cannot find metric: " + expectedMetric + " in the exported metrics"); + } + } + + protected void assertMeterExportedCorrectly(String exportedMeterPrefix, List<String> labels, + String exportedMetricPrefix) { + List<PromMetric> promMetrics; + try { + promMetrics = parseExportedPromMetrics(getExportedPromMetrics().getResponse()); + for (String meterType : METER_TYPES) { + PromMetric expectedMetric = + PromMetric.withNameAndLabels(exportedMetricPrefix + exportedMeterPrefix + "_" + meterType, labels); + Assert.assertTrue(promMetrics.contains(expectedMetric), + "Cannot find metric: " + expectedMetric + " in the exported metrics"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected List<PromMetric> parseExportedPromMetrics(String response) + throws IOException { + + List<PromMetric> exportedPromMetrics = new ArrayList<>(); + + try (BufferedReader reader = new BufferedReader(new StringReader(response))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("pinot_")) { + exportedPromMetrics.add(PromMetric.fromExportedMetric(line)); + } + } + reader.close(); + return exportedPromMetrics; + } + } + + protected SimpleHttpResponse getExportedPromMetrics() { + try { + return _httpClient.sendGetRequest(new URI("http://localhost:" + _httpServer.getPort() + "/metrics")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected abstract PinotMetricsFactory getPinotMetricsFactory(); + + protected abstract String getConfigFile(); + + /* + Implementation copied from: https://github + .com/prometheus/jmx_exporter/blob/a3b9443564ff5a78c25fd6566396fda2b7cbf216/jmx_prometheus_javaagent/src/main/java + /io/prometheus/jmx/JavaAgent.java#L88 + */ + private static JMXExporterConfig parseExporterConfig(String args, String ifc) { + Pattern pattern = Pattern.compile("^(?:((?:[\\w.-]+)|(?:\\[.+])):)?(\\d{1,5}):(.+)"); + Matcher matcher = pattern.matcher(args); + if (!matcher.matches()) { + throw new IllegalArgumentException("Malformed arguments - " + args); + } else { + String givenHost = matcher.group(1); + String givenPort = matcher.group(2); + String givenConfigFile = matcher.group(3); + int port = Integer.parseInt(givenPort); + InetSocketAddress socket; + if (givenHost != null && !givenHost.isEmpty()) { + socket = new InetSocketAddress(givenHost, port); + } else { + socket = new InetSocketAddress(ifc, port); + givenHost = ifc; + } + + return new JMXExporterConfig(givenHost, port, givenConfigFile, socket); + } + } + + private static class JMXExporterConfig { + String _host; + int _port; + String _file; + InetSocketAddress _socket; + + JMXExporterConfig(String host, int port, String file, InetSocketAddress socket) { + _host = host; + _port = port; + _file = file; + _socket = socket; + } + } + + private String loadResourceAsString(String resourceFileName) { + URL url = Resources.getResource(resourceFileName); + try { + return Resources.toString(url, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static class ExportedLabels { + + public static final List<String> TABLENAME = List.of(TABLE, ExportedLabelValues.TABLENAME); + public static final List<String> TABLENAME_TABLETYPE = + List.of(TABLE, ExportedLabelValues.TABLENAME, TABLETYPE, TABLETYPE_REALTIME); + public static final List<String> PARTITION_TABLENAME_TABLETYPE_KAFKATOPIC = + List.of(PARTITION, PARTITION_GROUP_ID, TABLE, ExportedLabelValues.TABLENAME, TABLETYPE, + TableType.REALTIME.toString(), TOPIC, KAFKA_TOPIC); + public static final List<String> PARTITION_TABLENAME_TABLETYPE = + List.of(PARTITION, "3", TABLE, ExportedLabelValues.TABLENAME, TABLETYPE, TableType.REALTIME.toString()); + + public static final List<String> TABLENAME_TABLETYPE_CONTROLLER_TASKTYPE = + List.of(TABLE, ExportedLabelValues.TABLENAME, TABLETYPE, TABLETYPE_REALTIME, TASKTYPE, + CONTROLLER_PERIODIC_TASK_CHC); + + public static final List<String> TABLENAMEWITHTYPE_CONTROLLER_TASKTYPE = + List.of(TABLE, TABLENAME_WITH_TYPE_REALTIME, TASKTYPE, CONTROLLER_PERIODIC_TASK_CHC); + + public static final List<String> JOBSTATUS_CONTROLLER_TASKTYPE = + List.of(STATUS, IN_PROGRESS, TASKTYPE, CONTROLLER_PERIODIC_TASK_CHC); + + public static final List<String> CONTROLLER_TASKTYPE_TABLENAME_TABLETYPE = + List.of(PERIODIC_TASK, CONTROLLER_PERIODIC_TASK_CHC, TABLE, ExportedLabelValues.TABLENAME, TABLETYPE, + TABLETYPE_REALTIME); + + public static final List<String> TABLENAME_TABLETYPE_MINION_TASKTYPE = + List.of(ExportedLabelKeys.TABLE, ExportedLabelValues.TABLENAME, ExportedLabelKeys.TABLETYPE, + ExportedLabelValues.TABLETYPE_REALTIME, ExportedLabelKeys.TASKTYPE, + ExportedLabelValues.MINION_TASK_SEGMENT_IMPORT); + } + + public static class ExportedLabelKeys { + public static final String TABLE = "table"; + public static final String ID = "id"; + public static final String TABLETYPE = "tableType"; + public static final String TOPIC = "topic"; + public static final String PARTITION = "partition"; + public static final String TASKTYPE = "taskType"; + public static final String PERIODIC_TASK = "periodicTask"; + public static final String STATUS = "status"; + public static final String DATABASE = "database"; + } + + public static class ExportedLabelValues { + public static final String TABLENAME = "myTable"; + public static final String TABLETYPE_REALTIME = "REALTIME"; + public static final String TABLENAME_WITH_TYPE_REALTIME = + TableNameBuilder.forType(TableType.REALTIME).tableNameWithType(TABLENAME); + public static final String CONTROLLER_PERIODIC_TASK_CHC = "ClusterHealthCheck"; + public static final String MINION_TASK_SEGMENT_IMPORT = "SegmentImportTask"; + public static final String IN_PROGRESS = "IN_PROGRESS"; + } + + /* + * Represents an exported Prometheus metric. A Prom metric looks like: + * pinot_server_realtimeRowsSanitized_Count{app="pinot", cluster_name="pinot", + * component="pinot-server", component_name="server-default-tenant-1"} + */ + private static class PromMetric { + private final String _metricName; + private final Map<String, String> _labels; + + public String getMetricName() { + return _metricName; + } + + public Map<String, String> getLabels() { + return _labels; + } + + //make constructor private so that it can be instantiated only with the factory methods + private PromMetric(String metricName, Map<String, String> labels) { + _metricName = metricName; + _labels = labels; + } + + /** + * Create an instance of {@link PromMetric} from an exported Prometheus metric + * + * @param exportedMetric the exported Prom metric (name + labels) + * @return the corresponding {@link PromMetric} + */ + public static PromMetric fromExportedMetric(String exportedMetric) { + int spaceIndex = exportedMetric.indexOf(' '); + String metricWithoutVal = exportedMetric.substring(0, spaceIndex); + int braceIndex = metricWithoutVal.indexOf('{'); + + if (braceIndex != -1) { + String metricName = metricWithoutVal.substring(0, braceIndex); + String labelsString = metricWithoutVal.substring(braceIndex + 1, metricWithoutVal.lastIndexOf('}')); + Map<String, String> labels = parseLabels(labelsString); + return new PromMetric(metricName, labels); + } else { + return new PromMetric(metricWithoutVal, new LinkedHashMap<>()); + } + } + + /** + * Creates an instance of {@link PromMetric} with a name and an empty label list + * + * @param metricName the metric name + * @return the corresponding PromMetric + */ + public static PromMetric withName(String metricName) { + return new PromMetric(metricName, new LinkedHashMap<>()); + } + + /** + * Creates an instance of {@link PromMetric} with a name and an label list + * + * @param metricName the metric name + * @param labels the labels + * @return the corresponding PromMetric + */ + public static PromMetric withNameAndLabels(String metricName, List<String> labels) { + Map<String, String> labelMap = new LinkedHashMap<>(); + for (int i = 0; i < labels.size(); i += 2) { + labelMap.put(labels.get(i), labels.get(i + 1)); + } + return new PromMetric(metricName, labelMap); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PromMetric that = (PromMetric) o; + return metricNamesAreSimilar(that) && Objects.equal(_labels, that._labels); + } + + @Override + public int hashCode() { + return Objects.hashCode(_metricName, _labels); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(_metricName); + if (!_labels.isEmpty()) { + sb.append('{'); + sb.append(_labels.entrySet().stream().map(e -> e.getKey() + "=\"" + e.getValue() + "\"") + .collect(Collectors.joining(","))); + sb.append('}'); + } + return sb.toString(); + } + + private boolean metricNamesAreSimilar(PromMetric that) { + String processedMetricNameThis = StringUtils.remove(_metricName, "_"); + String processedMetricNameThat = StringUtils.remove(that._metricName, "_"); + return StringUtils.equalsIgnoreCase(processedMetricNameThis, processedMetricNameThat); + } + + private static Map<String, String> parseLabels(String labelsString) { + return labelsString.isEmpty() ? new LinkedHashMap<>() + : java.util.Arrays.stream(labelsString.split(",")).map(kvPair -> kvPair.split("=")) + .collect(Collectors.toMap(kv -> kv[0], kv -> removeQuotes(kv[1]), (v1, v2) -> v2, LinkedHashMap::new)); + } + + private static String removeQuotes(String value) { + return value.startsWith("\"") ? value.substring(1, value.length() - 1) : value; + } + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ServerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ServerPrometheusMetricsTest.java new file mode 100644 index 0000000000..366d538ec1 --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/ServerPrometheusMetricsTest.java @@ -0,0 +1,196 @@ +/** + * 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.pinot.common.metrics.prometheus; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.pinot.common.metrics.ServerGauge; +import org.apache.pinot.common.metrics.ServerMeter; +import org.apache.pinot.common.metrics.ServerMetrics; +import org.apache.pinot.common.metrics.ServerTimer; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + + +public abstract class ServerPrometheusMetricsTest extends PinotPrometheusMetricsTest { + //all exported server metrics have this prefix + private static final String EXPORTED_METRIC_PREFIX = "pinot_server_"; + + private static final List<ServerMeter> METERS_ACCEPTING_CLIENT_ID = + List.of(ServerMeter.REALTIME_ROWS_CONSUMED, ServerMeter.REALTIME_ROWS_SANITIZED, + ServerMeter.REALTIME_ROWS_FETCHED, ServerMeter.REALTIME_ROWS_FILTERED, + ServerMeter.INVALID_REALTIME_ROWS_DROPPED, ServerMeter.INCOMPLETE_REALTIME_ROWS_CONSUMED, + ServerMeter.STREAM_CONSUMER_CREATE_EXCEPTIONS, ServerMeter.ROWS_WITH_ERRORS); + + private static final List<ServerMeter> METERS_ACCEPTING_RAW_TABLE_NAMES = + List.of(ServerMeter.SEGMENT_UPLOAD_FAILURE, ServerMeter.SEGMENT_UPLOAD_SUCCESS, + ServerMeter.SEGMENT_UPLOAD_TIMEOUT); + + //gauges that accept clientId + private static final List<ServerGauge> GAUGES_ACCEPTING_CLIENT_ID = + List.of(ServerGauge.LLC_PARTITION_CONSUMING, ServerGauge.HIGHEST_STREAM_OFFSET_CONSUMED, + ServerGauge.LAST_REALTIME_SEGMENT_CREATION_DURATION_SECONDS, + ServerGauge.LAST_REALTIME_SEGMENT_CREATION_WAIT_TIME_SECONDS, + ServerGauge.LAST_REALTIME_SEGMENT_INITIAL_CONSUMPTION_DURATION_SECONDS, + ServerGauge.LAST_REALTIME_SEGMENT_CATCHUP_DURATION_SECONDS, + ServerGauge.LAST_REALTIME_SEGMENT_COMPLETION_DURATION_SECONDS); + + private static final List<ServerGauge> GAUGES_ACCEPTING_PARTITION = + List.of(ServerGauge.UPSERT_VALID_DOC_ID_SNAPSHOT_COUNT, ServerGauge.UPSERT_PRIMARY_KEYS_IN_SNAPSHOT_COUNT, + ServerGauge.REALTIME_INGESTION_OFFSET_LAG, ServerGauge.REALTIME_INGESTION_DELAY_MS, + ServerGauge.UPSERT_PRIMARY_KEYS_COUNT, ServerGauge.END_TO_END_REALTIME_INGESTION_DELAY_MS, + ServerGauge.DEDUP_PRIMARY_KEYS_COUNT); + + private static final List<ServerGauge> GAUGES_ACCEPTING_RAW_TABLE_NAME = + List.of(ServerGauge.REALTIME_OFFHEAP_MEMORY_USED, ServerGauge.REALTIME_SEGMENT_NUM_PARTITIONS, + ServerGauge.LUCENE_INDEXING_DELAY_MS, ServerGauge.LUCENE_INDEXING_DELAY_DOCS); + + private ServerMetrics _serverMetrics; + + @BeforeClass + public void setup() + throws Exception { + _serverMetrics = new ServerMetrics(_pinotMetricsFactory.getPinotMetricsRegistry()); + } + + @Test(dataProvider = "serverTimers") + public void timerTest(ServerTimer serverTimer) { + if (serverTimer.isGlobal()) { + _serverMetrics.addTimedValue(serverTimer, 30_000, TimeUnit.MILLISECONDS); + assertTimerExportedCorrectly(serverTimer.getTimerName(), EXPORTED_METRIC_PREFIX); + } else { + _serverMetrics.addTimedTableValue(TABLE_NAME_WITH_TYPE, serverTimer, 30_000L, TimeUnit.MILLISECONDS); + _serverMetrics.addTimedTableValue(ExportedLabelValues.TABLENAME, serverTimer, 30_000L, TimeUnit.MILLISECONDS); + + assertTimerExportedCorrectly(serverTimer.getTimerName(), ExportedLabels.TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + assertTimerExportedCorrectly(serverTimer.getTimerName(), ExportedLabels.TABLENAME, EXPORTED_METRIC_PREFIX); + } + } + + @Test(dataProvider = "serverMeters") + public void meterTest(ServerMeter serverMeter) { + if (serverMeter.isGlobal()) { + _serverMetrics.addMeteredGlobalValue(serverMeter, 4L); + //we cannot use raw meter names for all meters as exported metrics don't follow any convention currently. + // For example, meters that track realtime exceptions start with prefix "realtime_exceptions" + if (meterTrackingRealtimeExceptions(serverMeter)) { + assertMeterExportedCorrectly(getRealtimeExceptionMeterName(serverMeter)); + } else { + assertMeterExportedCorrectly(serverMeter.getMeterName()); + } + } else { + if (METERS_ACCEPTING_CLIENT_ID.contains(serverMeter)) { + addMeterWithLabels(serverMeter, CLIENT_ID); + assertMeterExportedCorrectly(serverMeter.getMeterName(), + ExportedLabels.PARTITION_TABLENAME_TABLETYPE_KAFKATOPIC); + } else if (METERS_ACCEPTING_RAW_TABLE_NAMES.contains(serverMeter)) { + addMeterWithLabels(serverMeter, ExportedLabelValues.TABLENAME); + assertMeterExportedCorrectly(serverMeter.getMeterName(), ExportedLabels.TABLENAME); + } else { + //we pass tableNameWithType to all remaining meters + addMeterWithLabels(serverMeter, TABLE_NAME_WITH_TYPE); + assertMeterExportedCorrectly(serverMeter.getMeterName(), ExportedLabels.TABLENAME_TABLETYPE); + } + } + } + + @Test(dataProvider = "serverGauges") + public void gaugeTest(ServerGauge serverGauge) { + if (serverGauge.isGlobal()) { + _serverMetrics.setValueOfGlobalGauge(serverGauge, 10L); + assertGaugeExportedCorrectly(serverGauge.getGaugeName(), EXPORTED_METRIC_PREFIX); + } else { + if (serverGauge == ServerGauge.DEDUP_PRIMARY_KEYS_COUNT) { + //this gauge is currently exported as: `pinot_server_${partitionId}_Value{database="dedupPrimaryKeysCount", + // table="dedupPrimaryKeysCount.myTable",tableType="REALTIME",}`. We add an explicit test for it to maintain + // backward compatibility. todo: ServerGauge.DEDUP_PRIMARY_KEYS_COUNT should be moved to + // gaugesThatAcceptPartition. It should be exported as: + // `pinot_server_dedupPrimaryKeysCount_Value{partition="3", table="myTable",tableType="REALTIME",}` + addPartitionGaugeWithLabels(serverGauge, TABLE_NAME_WITH_TYPE); + assertGaugeExportedCorrectly(String.valueOf(3), + List.of(ExportedLabelKeys.DATABASE, serverGauge.getGaugeName(), ExportedLabelKeys.TABLE, + "dedupPrimaryKeysCount.myTable", ExportedLabelKeys.TABLETYPE, ExportedLabelValues.TABLETYPE_REALTIME), + EXPORTED_METRIC_PREFIX); + } else if (GAUGES_ACCEPTING_CLIENT_ID.contains(serverGauge)) { + addGaugeWithLabels(serverGauge, CLIENT_ID); + assertGaugeExportedCorrectly(serverGauge.getGaugeName(), + ExportedLabels.PARTITION_TABLENAME_TABLETYPE_KAFKATOPIC, EXPORTED_METRIC_PREFIX); + } else if (GAUGES_ACCEPTING_PARTITION.contains(serverGauge)) { + addPartitionGaugeWithLabels(serverGauge, TABLE_NAME_WITH_TYPE); + assertGaugeExportedCorrectly(serverGauge.getGaugeName(), ExportedLabels.PARTITION_TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + } else if (GAUGES_ACCEPTING_RAW_TABLE_NAME.contains(serverGauge)) { + addGaugeWithLabels(serverGauge, ExportedLabelValues.TABLENAME); + assertGaugeExportedCorrectly(serverGauge.getGaugeName(), ExportedLabels.TABLENAME, EXPORTED_METRIC_PREFIX); + } else { + addGaugeWithLabels(serverGauge, TABLE_NAME_WITH_TYPE); + assertGaugeExportedCorrectly(serverGauge.getGaugeName(), ExportedLabels.TABLENAME_TABLETYPE, + EXPORTED_METRIC_PREFIX); + } + } + } + + private void addGaugeWithLabels(ServerGauge serverGauge, String labels) { + _serverMetrics.setValueOfTableGauge(labels, serverGauge, 100L); + } + + private void addPartitionGaugeWithLabels(ServerGauge serverGauge, String labels) { + _serverMetrics.setValueOfPartitionGauge(labels, 3, serverGauge, 100L); + } + + public void addMeterWithLabels(ServerMeter serverMeter, String labels) { + _serverMetrics.addMeteredTableValue(labels, serverMeter, 4L); + } + + @DataProvider(name = "serverTimers") + public Object[] serverTimers() { + return ServerTimer.values(); // Provide all values of ServerTimer enum + } + + @DataProvider(name = "serverMeters") + public Object[] serverMeter() { + return ServerMeter.values(); // Provide all values of ServerTimer enum + } + + @DataProvider(name = "serverGauges") + public Object[] serverGauge() { + return ServerGauge.values(); // Provide all values of ServerTimer enum + } + + private boolean meterTrackingRealtimeExceptions(ServerMeter serverMeter) { + return serverMeter == ServerMeter.REQUEST_DESERIALIZATION_EXCEPTIONS + || serverMeter == ServerMeter.RESPONSE_SERIALIZATION_EXCEPTIONS + || serverMeter == ServerMeter.SCHEDULING_TIMEOUT_EXCEPTIONS || serverMeter == ServerMeter.UNCAUGHT_EXCEPTIONS; + } + + private String getRealtimeExceptionMeterName(ServerMeter serverMeter) { + String meterName = serverMeter.getMeterName(); + return "realtime_exceptions_" + meterName.substring(0, meterName.lastIndexOf("Exceptions")); + } + + private void assertMeterExportedCorrectly(String exportedMeterName) { + assertMeterExportedCorrectly(exportedMeterName, EXPORTED_METRIC_PREFIX); + } + + private void assertMeterExportedCorrectly(String exportedMeterName, List<String> labels) { + assertMeterExportedCorrectly(exportedMeterName, labels, EXPORTED_METRIC_PREFIX); + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardBrokerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardBrokerPrometheusMetricsTest.java new file mode 100644 index 0000000000..8d1abd44b2 --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardBrokerPrometheusMetricsTest.java @@ -0,0 +1,43 @@ +/** + * 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.pinot.common.metrics.prometheus.dropwizard; + +import org.apache.pinot.common.metrics.prometheus.BrokerPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.dropwizard.DropwizardMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.testng.annotations.Test; + + +/** + * Disabling tests as Pinot currently uses Yammer and these tests fail for for {@link DropwizardMetricsFactory} + */ +@Test(enabled = false) +public class DropwizardBrokerPrometheusMetricsTest extends BrokerPrometheusMetricsTest { + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new DropwizardMetricsFactory(); + } + + @Override + protected String getConfigFile() { + //todo: return the correct dir once this test is enabled + return null; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardControllerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardControllerPrometheusMetricsTest.java new file mode 100644 index 0000000000..005d83466b --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardControllerPrometheusMetricsTest.java @@ -0,0 +1,44 @@ +/** + * 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.pinot.common.metrics.prometheus.dropwizard; + +import org.apache.pinot.common.metrics.prometheus.ControllerPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.dropwizard.DropwizardMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.testng.annotations.Test; + + +/** + * Disabling tests as Pinot currently uses Yammer and these tests fail for for {@link DropwizardMetricsFactory} + */ +@Test(enabled = false) +public class DropwizardControllerPrometheusMetricsTest extends ControllerPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new DropwizardMetricsFactory(); + } + + @Override + protected String getConfigFile() { + //todo: return the correct dir once this test is enabled + return null; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardMinionPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardMinionPrometheusMetricsTest.java new file mode 100644 index 0000000000..08183428a8 --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardMinionPrometheusMetricsTest.java @@ -0,0 +1,44 @@ +/** + * 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.pinot.common.metrics.prometheus.dropwizard; + +import org.apache.pinot.common.metrics.prometheus.MinionPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.dropwizard.DropwizardMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.testng.annotations.Test; + + +/** + * Disabling tests as Pinot currently uses Yammer and these tests fail for for {@link DropwizardMetricsFactory} + */ +@Test(enabled = false) +public class DropwizardMinionPrometheusMetricsTest extends MinionPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new DropwizardMetricsFactory(); + } + + @Override + protected String getConfigFile() { + //todo: return the correct dir once this test is enabled + return null; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardServerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardServerPrometheusMetricsTest.java new file mode 100644 index 0000000000..9fc5bf4b96 --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/dropwizard/DropwizardServerPrometheusMetricsTest.java @@ -0,0 +1,45 @@ +/** + * 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.pinot.common.metrics.prometheus.dropwizard; + +import org.apache.pinot.common.metrics.prometheus.ServerPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.dropwizard.DropwizardMetricsFactory; +import org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; +import org.testng.annotations.Test; + + +/** + * Disabling tests as Pinot currently uses Yammer and these tests fail for for {@link DropwizardMetricsFactory} + */ +@Test(enabled = false) +public class DropwizardServerPrometheusMetricsTest extends ServerPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new YammerMetricsFactory(); + } + + @Override + protected String getConfigFile() { + //todo: return the correct dir once this test is enabled + return null; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerBrokerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerBrokerPrometheusMetricsTest.java new file mode 100644 index 0000000000..d58a3fdd3c --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerBrokerPrometheusMetricsTest.java @@ -0,0 +1,38 @@ +/** + * 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.pinot.common.metrics.prometheus.yammer; + +import org.apache.pinot.common.metrics.prometheus.BrokerPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; + + +public class YammerBrokerPrometheusMetricsTest extends BrokerPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new YammerMetricsFactory(); + } + + @Override + protected String getConfigFile() { + return "../docker/images/pinot/etc/jmx_prometheus_javaagent/configs/broker.yml"; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerControllerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerControllerPrometheusMetricsTest.java new file mode 100644 index 0000000000..e7f7f11dc8 --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerControllerPrometheusMetricsTest.java @@ -0,0 +1,38 @@ +/** + * 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.pinot.common.metrics.prometheus.yammer; + +import org.apache.pinot.common.metrics.prometheus.ControllerPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; + + +public class YammerControllerPrometheusMetricsTest extends ControllerPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new YammerMetricsFactory(); + } + + @Override + protected String getConfigFile() { + return "../docker/images/pinot/etc/jmx_prometheus_javaagent/configs/controller.yml"; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerMinionPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerMinionPrometheusMetricsTest.java new file mode 100644 index 0000000000..520c809e7a --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerMinionPrometheusMetricsTest.java @@ -0,0 +1,38 @@ +/** + * 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.pinot.common.metrics.prometheus.yammer; + +import org.apache.pinot.common.metrics.prometheus.MinionPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; + + +public class YammerMinionPrometheusMetricsTest extends MinionPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new YammerMetricsFactory(); + } + + @Override + protected String getConfigFile() { + return "../docker/images/pinot/etc/jmx_prometheus_javaagent/configs/minion.yml"; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerServerPrometheusMetricsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerServerPrometheusMetricsTest.java new file mode 100644 index 0000000000..e32228716d --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/metrics/prometheus/yammer/YammerServerPrometheusMetricsTest.java @@ -0,0 +1,38 @@ +/** + * 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.pinot.common.metrics.prometheus.yammer; + +import org.apache.pinot.common.metrics.prometheus.ServerPrometheusMetricsTest; +import org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory; +import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory; + + +public class YammerServerPrometheusMetricsTest extends ServerPrometheusMetricsTest { + + @Override + protected PinotMetricsFactory getPinotMetricsFactory() { + return new YammerMetricsFactory(); + } + + @Override + protected String getConfigFile() { + return "../docker/images/pinot/etc/jmx_prometheus_javaagent/configs/server.yml"; + } +} diff --git a/pom.xml b/pom.xml index f5d381a119..e00cfae2f0 100644 --- a/pom.xml +++ b/pom.xml @@ -801,6 +801,14 @@ </exclusions> </dependency> + <!-- JMX exporter--> + <dependency> + <groupId>io.prometheus.jmx</groupId> + <artifactId>jmx_prometheus_javaagent</artifactId> + <version>0.19.0</version> + <scope>test</scope> + </dependency> + <!-- netty BOM --> <dependency> <groupId>io.netty</groupId> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For additional commands, e-mail: commits-h...@pinot.apache.org