|
| 1 | +package com.linkedin.davinci.stats; |
| 2 | + |
| 3 | +import static com.linkedin.davinci.stats.DaVinciRecordTransformerOtelMetricEntity.RECORD_TRANSFORMER_ERROR_COUNT; |
| 4 | +import static com.linkedin.davinci.stats.DaVinciRecordTransformerOtelMetricEntity.RECORD_TRANSFORMER_LATENCY; |
| 5 | +import static com.linkedin.davinci.stats.ServerMetricEntity.SERVER_METRIC_ENTITIES; |
| 6 | +import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_CLUSTER_NAME; |
| 7 | +import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_RECORD_TRANSFORMER_OPERATION; |
| 8 | +import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_STORE_NAME; |
| 9 | +import static org.mockito.ArgumentMatchers.anyString; |
| 10 | +import static org.mockito.Mockito.doReturn; |
| 11 | +import static org.mockito.Mockito.mock; |
| 12 | + |
| 13 | +import com.linkedin.davinci.config.VeniceServerConfig; |
| 14 | +import com.linkedin.venice.meta.ReadOnlyStoreRepository; |
| 15 | +import com.linkedin.venice.meta.Store; |
| 16 | +import com.linkedin.venice.stats.VeniceMetricsConfig; |
| 17 | +import com.linkedin.venice.stats.VeniceMetricsRepository; |
| 18 | +import com.linkedin.venice.stats.dimensions.VeniceRecordTransformerOperation; |
| 19 | +import com.linkedin.venice.utils.OpenTelemetryDataTestUtils; |
| 20 | +import io.opentelemetry.api.common.Attributes; |
| 21 | +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; |
| 22 | +import io.tehuti.metrics.MetricsRepository; |
| 23 | +import java.util.Collections; |
| 24 | +import org.testng.annotations.AfterMethod; |
| 25 | +import org.testng.annotations.BeforeMethod; |
| 26 | +import org.testng.annotations.Test; |
| 27 | + |
| 28 | + |
| 29 | +public class AggVersionedDaVinciRecordTransformerStatsOtelTest { |
| 30 | + private static final String TEST_METRIC_PREFIX = "davinci_client"; |
| 31 | + private static final String TEST_CLUSTER_NAME = "test-cluster"; |
| 32 | + private static final String TEST_STORE_NAME = "test-store"; |
| 33 | + |
| 34 | + private InMemoryMetricReader inMemoryMetricReader; |
| 35 | + private VeniceMetricsRepository metricsRepository; |
| 36 | + private AggVersionedDaVinciRecordTransformerStats aggStats; |
| 37 | + |
| 38 | + @BeforeMethod |
| 39 | + public void setUp() { |
| 40 | + inMemoryMetricReader = InMemoryMetricReader.create(); |
| 41 | + metricsRepository = new VeniceMetricsRepository( |
| 42 | + new VeniceMetricsConfig.Builder().setMetricPrefix(TEST_METRIC_PREFIX) |
| 43 | + .setMetricEntities(SERVER_METRIC_ENTITIES) |
| 44 | + .setEmitOtelMetrics(true) |
| 45 | + .setOtelAdditionalMetricsReader(inMemoryMetricReader) |
| 46 | + .build()); |
| 47 | + aggStats = createAggStats(metricsRepository); |
| 48 | + } |
| 49 | + |
| 50 | + @AfterMethod |
| 51 | + public void tearDown() { |
| 52 | + if (metricsRepository != null) { |
| 53 | + metricsRepository.close(); |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + @Test |
| 58 | + public void testRecordPutLatency() { |
| 59 | + long timestamp = System.currentTimeMillis(); |
| 60 | + aggStats.recordPutLatency(TEST_STORE_NAME, 1, 50.0, timestamp); |
| 61 | + aggStats.recordPutLatency(TEST_STORE_NAME, 1, 100.0, timestamp); |
| 62 | + |
| 63 | + OpenTelemetryDataTestUtils.validateExponentialHistogramPointData( |
| 64 | + inMemoryMetricReader, |
| 65 | + 50.0, |
| 66 | + 100.0, |
| 67 | + 2, |
| 68 | + 150.0, |
| 69 | + buildAttributes(VeniceRecordTransformerOperation.PUT), |
| 70 | + RECORD_TRANSFORMER_LATENCY.getMetricEntity().getMetricName(), |
| 71 | + TEST_METRIC_PREFIX); |
| 72 | + } |
| 73 | + |
| 74 | + @Test |
| 75 | + public void testRecordDeleteLatency() { |
| 76 | + long timestamp = System.currentTimeMillis(); |
| 77 | + aggStats.recordDeleteLatency(TEST_STORE_NAME, 1, 25.0, timestamp); |
| 78 | + |
| 79 | + OpenTelemetryDataTestUtils.validateExponentialHistogramPointData( |
| 80 | + inMemoryMetricReader, |
| 81 | + 25.0, |
| 82 | + 25.0, |
| 83 | + 1, |
| 84 | + 25.0, |
| 85 | + buildAttributes(VeniceRecordTransformerOperation.DELETE), |
| 86 | + RECORD_TRANSFORMER_LATENCY.getMetricEntity().getMetricName(), |
| 87 | + TEST_METRIC_PREFIX); |
| 88 | + } |
| 89 | + |
| 90 | + @Test |
| 91 | + public void testRecordPutError() { |
| 92 | + long timestamp = System.currentTimeMillis(); |
| 93 | + aggStats.recordPutError(TEST_STORE_NAME, 1, timestamp); |
| 94 | + aggStats.recordPutError(TEST_STORE_NAME, 1, timestamp); |
| 95 | + |
| 96 | + OpenTelemetryDataTestUtils.validateLongPointDataFromCounter( |
| 97 | + inMemoryMetricReader, |
| 98 | + 2, |
| 99 | + buildAttributes(VeniceRecordTransformerOperation.PUT), |
| 100 | + RECORD_TRANSFORMER_ERROR_COUNT.getMetricEntity().getMetricName(), |
| 101 | + TEST_METRIC_PREFIX); |
| 102 | + } |
| 103 | + |
| 104 | + @Test |
| 105 | + public void testRecordDeleteError() { |
| 106 | + long timestamp = System.currentTimeMillis(); |
| 107 | + aggStats.recordDeleteError(TEST_STORE_NAME, 1, timestamp); |
| 108 | + |
| 109 | + OpenTelemetryDataTestUtils.validateLongPointDataFromCounter( |
| 110 | + inMemoryMetricReader, |
| 111 | + 1, |
| 112 | + buildAttributes(VeniceRecordTransformerOperation.DELETE), |
| 113 | + RECORD_TRANSFORMER_ERROR_COUNT.getMetricEntity().getMetricName(), |
| 114 | + TEST_METRIC_PREFIX); |
| 115 | + } |
| 116 | + |
| 117 | + @Test |
| 118 | + public void testOperationDimensionIsolation() { |
| 119 | + long timestamp = System.currentTimeMillis(); |
| 120 | + aggStats.recordPutError(TEST_STORE_NAME, 1, timestamp); |
| 121 | + aggStats.recordPutError(TEST_STORE_NAME, 1, timestamp); |
| 122 | + aggStats.recordDeleteError(TEST_STORE_NAME, 1, timestamp); |
| 123 | + |
| 124 | + OpenTelemetryDataTestUtils.validateLongPointDataFromCounter( |
| 125 | + inMemoryMetricReader, |
| 126 | + 2, |
| 127 | + buildAttributes(VeniceRecordTransformerOperation.PUT), |
| 128 | + RECORD_TRANSFORMER_ERROR_COUNT.getMetricEntity().getMetricName(), |
| 129 | + TEST_METRIC_PREFIX); |
| 130 | + |
| 131 | + OpenTelemetryDataTestUtils.validateLongPointDataFromCounter( |
| 132 | + inMemoryMetricReader, |
| 133 | + 1, |
| 134 | + buildAttributes(VeniceRecordTransformerOperation.DELETE), |
| 135 | + RECORD_TRANSFORMER_ERROR_COUNT.getMetricEntity().getMetricName(), |
| 136 | + TEST_METRIC_PREFIX); |
| 137 | + } |
| 138 | + |
| 139 | + // --- NPE prevention tests --- |
| 140 | + |
| 141 | + @Test |
| 142 | + public void testNoNpeWhenOtelDisabled() { |
| 143 | + try (VeniceMetricsRepository disabledRepo = new VeniceMetricsRepository( |
| 144 | + new VeniceMetricsConfig.Builder().setMetricPrefix(TEST_METRIC_PREFIX).setEmitOtelMetrics(false).build())) { |
| 145 | + exerciseAllRecordingPaths(disabledRepo); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + @Test |
| 150 | + public void testNoNpeWhenPlainMetricsRepository() { |
| 151 | + exerciseAllRecordingPaths(new MetricsRepository()); |
| 152 | + } |
| 153 | + |
| 154 | + private void exerciseAllRecordingPaths(MetricsRepository repo) { |
| 155 | + AggVersionedDaVinciRecordTransformerStats safeStats = createAggStats(repo); |
| 156 | + long ts = System.currentTimeMillis(); |
| 157 | + safeStats.recordPutLatency(TEST_STORE_NAME, 1, 10.0, ts); |
| 158 | + safeStats.recordDeleteLatency(TEST_STORE_NAME, 1, 10.0, ts); |
| 159 | + safeStats.recordPutError(TEST_STORE_NAME, 1, ts); |
| 160 | + safeStats.recordDeleteError(TEST_STORE_NAME, 1, ts); |
| 161 | + } |
| 162 | + |
| 163 | + // --- Helpers --- |
| 164 | + |
| 165 | + private static AggVersionedDaVinciRecordTransformerStats createAggStats(MetricsRepository repo) { |
| 166 | + ReadOnlyStoreRepository metadataRepository = mock(ReadOnlyStoreRepository.class); |
| 167 | + Store mockStore = mock(Store.class); |
| 168 | + doReturn(TEST_STORE_NAME).when(mockStore).getName(); |
| 169 | + doReturn(Collections.emptyList()).when(mockStore).getVersions(); |
| 170 | + doReturn(0).when(mockStore).getCurrentVersion(); |
| 171 | + doReturn(mockStore).when(metadataRepository).getStoreOrThrow(anyString()); |
| 172 | + |
| 173 | + VeniceServerConfig serverConfig = mock(VeniceServerConfig.class); |
| 174 | + doReturn(false).when(serverConfig).isUnregisterMetricForDeletedStoreEnabled(); |
| 175 | + doReturn(TEST_CLUSTER_NAME).when(serverConfig).getClusterName(); |
| 176 | + |
| 177 | + return new AggVersionedDaVinciRecordTransformerStats(repo, metadataRepository, serverConfig); |
| 178 | + } |
| 179 | + |
| 180 | + private Attributes buildAttributes(VeniceRecordTransformerOperation operation) { |
| 181 | + return Attributes.builder() |
| 182 | + .put(VENICE_CLUSTER_NAME.getDimensionNameInDefaultFormat(), TEST_CLUSTER_NAME) |
| 183 | + .put(VENICE_STORE_NAME.getDimensionNameInDefaultFormat(), TEST_STORE_NAME) |
| 184 | + .put(VENICE_RECORD_TRANSFORMER_OPERATION.getDimensionNameInDefaultFormat(), operation.getDimensionValue()) |
| 185 | + .build(); |
| 186 | + } |
| 187 | +} |
0 commit comments