diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMeterFilterProducer.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMeterFilterProducer.java index 28f77e9130..85a3f98370 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMeterFilterProducer.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMeterFilterProducer.java @@ -20,6 +20,8 @@ import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.config.MeterFilter; +import io.micrometer.core.instrument.internal.OnlyOnceLoggingDenyMeterFilter; +import io.quarkus.micrometer.runtime.binder.HttpBinderConfiguration; import jakarta.enterprise.inject.Produces; import jakarta.inject.Inject; import jakarta.inject.Singleton; @@ -27,14 +29,33 @@ public class QuarkusMeterFilterProducer { - @Inject QuarkusMetricsConfiguration configuration; + @Inject QuarkusMetricsConfiguration metricsConfiguration; + @Inject HttpBinderConfiguration binderConfiguration; @Produces @Singleton - public MeterFilter produceGlobalMeterFilter() { + public MeterFilter commonTagsFilter() { return MeterFilter.commonTags( - this.configuration.tags().entrySet().stream() + this.metricsConfiguration.tags().entrySet().stream() .map(e -> Tag.of(e.getKey(), e.getValue())) .collect(Collectors.toSet())); } + + @Produces + @Singleton + public MeterFilter maxRealmIdTagsInHttpMetricsFilter() { + MeterFilter denyFilter = + new OnlyOnceLoggingDenyMeterFilter( + () -> + String.format( + "Reached the maximum number (%s) of '%s' tags for '%s'", + metricsConfiguration.realmIdTag().httpMetricsMaxCardinality(), + RealmIdTagContributor.TAG_REALM, + binderConfiguration.getHttpServerRequestsName())); + return MeterFilter.maximumAllowableTags( + binderConfiguration.getHttpServerRequestsName(), + RealmIdTagContributor.TAG_REALM, + metricsConfiguration.realmIdTag().httpMetricsMaxCardinality(), + denyFilter); + } } diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMetricsConfiguration.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMetricsConfiguration.java index 756de6adb5..34ae813a3f 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMetricsConfiguration.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusMetricsConfiguration.java @@ -19,6 +19,8 @@ package org.apache.polaris.service.quarkus.metrics; import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import jakarta.validation.constraints.Min; import java.util.Map; @ConfigMapping(prefix = "polaris.metrics") @@ -26,4 +28,41 @@ public interface QuarkusMetricsConfiguration { /** Additional tags to include in the metrics. */ Map tags(); + + /** Configuration for the Realm ID metric tag. */ + RealmIdTag realmIdTag(); + + interface RealmIdTag { + + /** + * Whether to include the Realm ID tag in the API request metrics. + * + *

Beware that if the cardinality of this tag is too high, it can cause performance issues or + * even crash the server. + */ + @WithDefault("false") + boolean enableInApiMetrics(); + + /** + * Whether to include the Realm ID tag in the HTTP server request metrics. + * + *

Beware that if the cardinality of this tag is too high, it can cause performance issues or + * even crash the server. + */ + @WithDefault("false") + boolean enableInHttpMetrics(); + + /** + * The maximum number of Realm ID tag values allowed for the HTTP server request metrics. + * + *

This is used to prevent the number of tags from growing indefinitely and causing + * performance issues or crashing the server. + * + *

If the number of tags exceeds this value, a warning will be logged and no more HTTP server + * request metrics will be recorded. + */ + @WithDefault("100") + @Min(1) + int httpMetricsMaxCardinality(); + } } diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java index 98783de60f..2c2af93368 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java @@ -22,17 +22,23 @@ import io.micrometer.common.lang.Nullable; import jakarta.annotation.Nonnull; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import org.apache.polaris.core.context.RealmContext; @ApplicationScoped public class QuarkusValueExpressionResolver implements ValueExpressionResolver { + @Inject QuarkusMetricsConfiguration metricsConfiguration; + @Override public String resolve(@Nonnull String expression, @Nullable Object parameter) { // TODO maybe replace with CEL of some expression engine and make this more generic - if (parameter instanceof RealmContext realmContext && expression.equals("realmIdentifier")) { + if (metricsConfiguration.realmIdTag().enableInApiMetrics() + && parameter instanceof RealmContext realmContext + && expression.equals("realmIdentifier")) { return realmContext.getRealmIdentifier(); } - return null; + // FIXME cannot return null here, see https://github.com/quarkusio/quarkus/issues/47891 + return ""; } } diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java index 1d13c97b73..232d126683 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java @@ -33,12 +33,16 @@ public class RealmIdTagContributor implements HttpServerMetricsTagsContributor { private static final Tags UNFINISHED_RESOLUTION_TAGS = Tags.of(TAG_REALM, "???"); + @Inject QuarkusMetricsConfiguration metricsConfiguration; @Inject RealmContextResolver realmContextResolver; @Override public Tags contribute(Context context) { - // FIXME retrieve the realm context from context.requestContextLocalData() when this PR is in: - // https://github.com/quarkusio/quarkus/pull/47887 + // FIXME retrieve the realm context from context.requestContextLocalData() + // after upgrading to Quarkus 3.24 + if (!metricsConfiguration.realmIdTag().enableInHttpMetrics()) { + return Tags.empty(); + } HttpServerRequest request = context.request(); try { return realmContextResolver diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/MetricsTestBase.java similarity index 63% rename from quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java rename to quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/MetricsTestBase.java index 66a8aa388b..590712403c 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/MetricsTestBase.java @@ -16,20 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.polaris.service.quarkus; +package org.apache.polaris.service.quarkus.metrics; import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.type; import io.micrometer.core.instrument.MeterRegistry; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.QuarkusTestProfile; -import io.quarkus.test.junit.TestProfile; import jakarta.inject.Inject; import jakarta.ws.rs.core.Response; import java.util.Map; -import org.apache.polaris.service.quarkus.TimedApplicationEventListenerTest.Profile; import org.apache.polaris.service.quarkus.test.PolarisIntegrationTestFixture; import org.apache.polaris.service.quarkus.test.PolarisIntegrationTestHelper; import org.apache.polaris.service.quarkus.test.TestEnvironment; @@ -46,27 +42,18 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -@QuarkusTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(TestEnvironmentExtension.class) -@TestProfile(Profile.class) -public class TimedApplicationEventListenerTest { - - public static class Profile implements QuarkusTestProfile { - - @Override - public Map getConfigOverrides() { - return Map.of( - "polaris.metrics.tags.environment", "prod", "polaris.realm-context.type", "test"); - } - } +public abstract class MetricsTestBase { private static final int ERROR_CODE = Response.Status.NOT_FOUND.getStatusCode(); private static final String ENDPOINT = "api/management/v1/principals"; - private static final String METRIC_NAME = "polaris_principals_getPrincipal_seconds"; + private static final String API_METRIC_NAME = "polaris_principals_getPrincipal_seconds"; + private static final String HTTP_METRIC_NAME = "http_server_requests_seconds"; @Inject PolarisIntegrationTestHelper helper; @Inject MeterRegistry registry; + @Inject QuarkusMetricsConfiguration metricsConfiguration; private TestEnvironment testEnv; private PolarisIntegrationTestFixture fixture; @@ -88,15 +75,19 @@ public void testMetricsEmittedOnSuccessfulRequest(String endpoint) { sendSuccessfulRequest(); Map allMetrics = TestMetricsUtil.fetchMetrics(fixture.client, testEnv.baseManagementUri(), endpoint); - assertThat(allMetrics).containsKey(METRIC_NAME); - assertThat(allMetrics.get(METRIC_NAME).getMetrics()) + assertThat(allMetrics).containsKey(API_METRIC_NAME); + assertThat(allMetrics.get(API_METRIC_NAME).getMetrics()) .satisfiesOnlyOnce( metric -> { assertThat(metric.getLabels()) .contains( Map.entry("application", "Polaris"), Map.entry("environment", "prod"), - Map.entry("realm_id", fixture.realm), + Map.entry( + "realm_id", + metricsConfiguration.realmIdTag().enableInApiMetrics() + ? fixture.realm + : ""), Map.entry( "class", "org.apache.polaris.service.admin.api.PolarisPrincipalsApi"), Map.entry("exception", "none"), @@ -106,6 +97,28 @@ public void testMetricsEmittedOnSuccessfulRequest(String endpoint) { .extracting(Summary::getSampleCount) .isEqualTo(1L); }); + assertThat(allMetrics).containsKey(HTTP_METRIC_NAME); + assertThat(allMetrics.get(HTTP_METRIC_NAME).getMetrics()) + .satisfiesOnlyOnce( + metric -> { + assertThat(metric.getLabels()) + .contains( + Map.entry("application", "Polaris"), + Map.entry("environment", "prod"), + Map.entry("method", "GET"), + Map.entry("outcome", "SUCCESS"), + Map.entry("status", "200"), + Map.entry("uri", "/api/management/v1/principals/{principalName}")); + if (metricsConfiguration.realmIdTag().enableInHttpMetrics()) { + assertThat(metric.getLabels()).containsEntry("realm_id", fixture.realm); + } else { + assertThat(metric.getLabels()).doesNotContainKey("realm_id"); + } + assertThat(metric) + .asInstanceOf(type(Summary.class)) + .extracting(Summary::getSampleCount) + .isEqualTo(1L); + }); } @ParameterizedTest @@ -114,15 +127,19 @@ public void testMetricsEmittedOnFailedRequest(String endpoint) { sendFailingRequest(); Map allMetrics = TestMetricsUtil.fetchMetrics(fixture.client, testEnv.baseManagementUri(), endpoint); - assertThat(allMetrics).containsKey(METRIC_NAME); - assertThat(allMetrics.get(METRIC_NAME).getMetrics()) + assertThat(allMetrics).containsKey(API_METRIC_NAME); + assertThat(allMetrics.get(API_METRIC_NAME).getMetrics()) .satisfiesOnlyOnce( metric -> { assertThat(metric.getLabels()) .contains( Map.entry("application", "Polaris"), Map.entry("environment", "prod"), - Map.entry("realm_id", fixture.realm), + Map.entry( + "realm_id", + metricsConfiguration.realmIdTag().enableInApiMetrics() + ? fixture.realm + : ""), Map.entry( "class", "org.apache.polaris.service.admin.api.PolarisPrincipalsApi"), Map.entry("exception", "NotFoundException"), @@ -132,6 +149,27 @@ public void testMetricsEmittedOnFailedRequest(String endpoint) { .extracting(Summary::getSampleCount) .isEqualTo(1L); }); + assertThat(allMetrics.get(HTTP_METRIC_NAME).getMetrics()) + .satisfiesOnlyOnce( + metric -> { + assertThat(metric.getLabels()) + .contains( + Map.entry("application", "Polaris"), + Map.entry("environment", "prod"), + Map.entry("method", "GET"), + Map.entry("outcome", "CLIENT_ERROR"), + Map.entry("status", "404"), + Map.entry("uri", "/api/management/v1/principals/{principalName}")); + if (metricsConfiguration.realmIdTag().enableInHttpMetrics()) { + assertThat(metric.getLabels()).containsEntry("realm_id", fixture.realm); + } else { + assertThat(metric.getLabels()).doesNotContainKey("realm_id"); + } + assertThat(metric) + .asInstanceOf(type(Summary.class)) + .extracting(Summary::getSampleCount) + .isEqualTo(1L); + }); } private int sendRequest(String principalName) { diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagDisabledMetricsTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagDisabledMetricsTest.java new file mode 100644 index 0000000000..87af357751 --- /dev/null +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagDisabledMetricsTest.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.polaris.service.quarkus.metrics; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import java.util.Map; + +@QuarkusTest +@TestProfile(RealmIdTagDisabledMetricsTest.Profile.class) +public class RealmIdTagDisabledMetricsTest extends MetricsTestBase { + + public static class Profile implements QuarkusTestProfile { + + @Override + public Map getConfigOverrides() { + return Map.of( + "polaris.metrics.tags.environment", "prod", "polaris.realm-context.type", "test"); + } + } +} diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagEnabledMetricsTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagEnabledMetricsTest.java new file mode 100644 index 0000000000..6d261dc65b --- /dev/null +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagEnabledMetricsTest.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.polaris.service.quarkus.metrics; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import java.util.Map; + +@QuarkusTest +@TestProfile(RealmIdTagEnabledMetricsTest.Profile.class) +public class RealmIdTagEnabledMetricsTest extends MetricsTestBase { + + public static class Profile implements QuarkusTestProfile { + + @Override + public Map getConfigOverrides() { + return Map.of( + "polaris.metrics.tags.environment", + "prod", + "polaris.realm-context.type", + "test", + "polaris.metrics.realm-id-tag.enable-in-api-metrics", + "true", + "polaris.metrics.realm-id-tag.enable-in-http-metrics", + "true"); + } + } +} diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/RateLimiterFilterTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/RateLimiterFilterTest.java index 48aa450019..f81ffe7744 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/RateLimiterFilterTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/RateLimiterFilterTest.java @@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.type; +import com.google.common.collect.ImmutableMap; import io.micrometer.core.instrument.MeterRegistry; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.QuarkusTestProfile; @@ -68,25 +69,21 @@ public Set> getEnabledAlternatives() { @Override public Map getConfigOverrides() { - return Map.of( - "polaris.rate-limiter.filter.type", - "default", - "polaris.rate-limiter.token-bucket.type", - "default", - "polaris.rate-limiter.token-bucket.requests-per-second", - String.valueOf(REQUESTS_PER_SECOND), - "polaris.rate-limiter.token-bucket.window", - WINDOW.toString(), - "polaris.metrics.tags.environment", - "prod", - "polaris.realm-context.type", - "test", - "polaris.authentication.token-broker.type", - "symmetric-key", - "polaris.authentication.token-broker.symmetric-key.secret", - "secret", - "polaris.event-listener.type", - "test"); + return ImmutableMap.builder() + .put("polaris.rate-limiter.filter.type", "default") + .put("polaris.rate-limiter.token-bucket.type", "default") + .put( + "polaris.rate-limiter.token-bucket.requests-per-second", + String.valueOf(REQUESTS_PER_SECOND)) + .put("polaris.rate-limiter.token-bucket.window", WINDOW.toString()) + .put("polaris.metrics.tags.environment", "prod") + .put("polaris.metrics.realm-id-tag.enable-in-api-metrics", "true") + .put("polaris.metrics.realm-id-tag.enable-in-http-metrics", "true") + .put("polaris.realm-context.type", "test") + .put("polaris.authentication.token-broker.type", "symmetric-key") + .put("polaris.authentication.token-broker.symmetric-key.secret", "secret") + .put("polaris.event-listener.type", "test") + .build(); } } diff --git a/site/content/in-dev/unreleased/configuration.md b/site/content/in-dev/unreleased/configuration.md index b195d05a5b..9da84c7d5a 100644 --- a/site/content/in-dev/unreleased/configuration.md +++ b/site/content/in-dev/unreleased/configuration.md @@ -77,45 +77,45 @@ read-only mode, as Polaris only reads the configuration file once, at startup. ## Polaris Configuration Options Reference -| Configuration Property | Default Value | Description | -|--------------------------------------------------------------------------------------------|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `polaris.persistence.type` | `relational-jdbc` | Define the persistence backend used by Polaris (`in-memory`, `relational-jdbc`, `eclipse-link` (deprecated)). See [Configuring Apache Polaris for Production)[{{% ref "configuring-polaris-for-production.md" %}}) | -| `polaris.persistence.relational.jdbc.max-retries` | `1` | Total number of retries JDBC persistence will attempt on connection resets or serialization failures before giving up. | -| `polaris.persistence.relational.jdbc.max_duaration_in_ms` | `5000 ms` | Max time interval (ms) since the start of a transaction when retries can be attempted. | -| `polaris.persistence.relational.jdbc.initial_delay_in_ms` | `100 ms` | Initial delay before retrying. The delay is doubled after each retry. | -| `polaris.persistence.eclipselink.configurationFile` | | Define the location of the `persistence.xml`. By default, it's the built-in `persistence.xml` in use. | -| `polaris.persistence.eclipselink.persistenceUnit` | `polaris` | Define the name of the persistence unit to use, as defined in the `persistence.xml`. | -| `polaris.realm-context.type` | `default` | Define the type of the Polaris realm to use. | -| `polaris.realm-context.realms` | `POLARIS` | Define the list of realms to use. | -| `polaris.realm-context.header-name` | `Polaris-Realm` | Define the header name defining the realm context. | -| `polaris.features."ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING"` | `false` | Flag to enforce check if credential rotation. | -| `polaris.features."SUPPORTED_CATALOG_STORAGE_TYPES"` | `FILE` | Define the catalog supported storage. Supported values are `S3`, `GCS`, `AZURE`, `FILE`. | -| `polaris.features.realm-overrides."my-realm"."SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION"` | `true` | "Override" realm features, here the skip credential subscoping indirection flag. | -| `polaris.authentication.authenticator.type` | `default` | Define the Polaris authenticator type. | -| `polaris.authentication.token-service.type` | `default` | Define the Polaris token service type. | -| `polaris.authentication.token-broker.type` | `rsa-key-pair` | Define the Polaris token broker type. | -| `polaris.authentication.token-broker.max-token-generation` | `PT1H` | Define the max token generation policy on the token broker. | -| `polaris.authentication.token-broker.rsa-key-pair.public-key-file` | `/tmp/public.key` | Define the location of the public key file. | -| `polaris.authentication.token-broker.rsa-key-pair.private-key-file` | `/tmp/private.key` | Define the location of the private key file. | -| `polaris.authentication.token-broker.symmetric-key.secret` | `secret` | Define the secret of the symmetric key. | -| `polaris.authentication.token-broker.symmetric-key.file` | `/tmp/symmetric.key` | Define the location of the symmetric key file. | -| `polaris.storage.aws.access-key` | `accessKey` | Define the AWS S3 access key. If unset, the default credential provider chain will be used. | -| `polaris.storage.aws.secret-key` | `secretKey` | Define the AWS S3 secret key. If unset, the default credential provider chain will be used. | -| `polaris.storage.gcp.token` | `token` | Define the Google Cloud Storage token. If unset, the default credential provider chain will be used. | -| `polaris.storage.gcp.lifespan` | `PT1H` | Define the Google Cloud Storage lifespan type. If unset, the default credential provider chain will be used. | -| `polaris.log.request-id-header-name` | `Polaris-Request-Id` | Define the header name to match request ID in the log. | -| `polaris.log.mdc.aid` | `polaris` | Define the log context (e.g. MDC) AID. | -| `polaris.log.mdc.sid` | `polaris-service` | Define the log context (e.g. MDC) SID. | -| `polaris.rate-limiter.filter.type` | `no-op` | Define the Polaris rate limiter. Supported values are `no-op`, `token-bucket`. | -| `polaris.rate-limiter.token-bucket.type` | `default` | Define the token bucket rate limiter. | -| `polaris.rate-limiter.token-bucket.requests-per-second` | `9999` | Define the number of requests per second for the token bucket rate limiter. | -| `polaris.rate-limiter.token-bucket.window` | `PT10S` | Define the window type for the token bucket rate limiter. | -| `polaris.metrics.tags.application` | `Polaris` | Define the application name tag in metrics. | -| `polaris.metrics.tags.service` | `polaris` | Define the service tag in metrics. | -| `polaris.metrics.tags.environment` | `prod` | Define the environement tag in metrics. | -| `polaris.metrics.tags.region` | `us-west-2` | Define the region tag in metrics. | -| `polaris.tasks.max-concurrent-tasks` | `100` | Define the max number of concurrent tasks. | -| `polaris.tasks.max-queued-tasks` | `1000` | Define the max number of tasks in queue. | +| Configuration Property | Default Value | Description | +|----------------------------------------------------------------------------------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `polaris.persistence.type` | `relational-jdbc` | Define the persistence backend used by Polaris (`in-memory`, `relational-jdbc`, `eclipse-link` (deprecated)). See [Configuring Apache Polaris for Production)[{{% ref "configuring-polaris-for-production.md" %}}) | +| `polaris.persistence.relational.jdbc.max-retries` | `1` | Total number of retries JDBC persistence will attempt on connection resets or serialization failures before giving up. | +| `polaris.persistence.relational.jdbc.max_duaration_in_ms` | `5000 ms` | Max time interval (ms) since the start of a transaction when retries can be attempted. | +| `polaris.persistence.relational.jdbc.initial_delay_in_ms` | `100 ms` | Initial delay before retrying. The delay is doubled after each retry. | +| `polaris.persistence.eclipselink.configurationFile` | | Define the location of the `persistence.xml`. By default, it's the built-in `persistence.xml` in use. | +| `polaris.persistence.eclipselink.persistenceUnit` | `polaris` | Define the name of the persistence unit to use, as defined in the `persistence.xml`. | +| `polaris.realm-context.type` | `default` | Define the type of the Polaris realm to use. | +| `polaris.realm-context.realms` | `POLARIS` | Define the list of realms to use. | +| `polaris.realm-context.header-name` | `Polaris-Realm` | Define the header name defining the realm context. | +| `polaris.features."ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING"` | `false` | Flag to enforce check if credential rotation. | +| `polaris.features."SUPPORTED_CATALOG_STORAGE_TYPES"` | `FILE` | Define the catalog supported storage. Supported values are `S3`, `GCS`, `AZURE`, `FILE`. | +| `polaris.features.realm-overrides."my-realm"."SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION"` | `true` | "Override" realm features, here the skip credential subscoping indirection flag. | +| `polaris.authentication.authenticator.type` | `default` | Define the Polaris authenticator type. | +| `polaris.authentication.token-service.type` | `default` | Define the Polaris token service type. | +| `polaris.authentication.token-broker.type` | `rsa-key-pair` | Define the Polaris token broker type. | +| `polaris.authentication.token-broker.max-token-generation` | `PT1H` | Define the max token generation policy on the token broker. | +| `polaris.authentication.token-broker.rsa-key-pair.public-key-file` | `/tmp/public.key` | Define the location of the public key file. | +| `polaris.authentication.token-broker.rsa-key-pair.private-key-file` | `/tmp/private.key` | Define the location of the private key file. | +| `polaris.authentication.token-broker.symmetric-key.secret` | `secret` | Define the secret of the symmetric key. | +| `polaris.authentication.token-broker.symmetric-key.file` | `/tmp/symmetric.key` | Define the location of the symmetric key file. | +| `polaris.storage.aws.access-key` | `accessKey` | Define the AWS S3 access key. If unset, the default credential provider chain will be used. | +| `polaris.storage.aws.secret-key` | `secretKey` | Define the AWS S3 secret key. If unset, the default credential provider chain will be used. | +| `polaris.storage.gcp.token` | `token` | Define the Google Cloud Storage token. If unset, the default credential provider chain will be used. | +| `polaris.storage.gcp.lifespan` | `PT1H` | Define the Google Cloud Storage lifespan type. If unset, the default credential provider chain will be used. | +| `polaris.log.request-id-header-name` | `Polaris-Request-Id` | Define the header name to match request ID in the log. | +| `polaris.log.mdc.aid` | `polaris` | Define the log context (e.g. MDC) AID. | +| `polaris.log.mdc.sid` | `polaris-service` | Define the log context (e.g. MDC) SID. | +| `polaris.rate-limiter.filter.type` | `no-op` | Define the Polaris rate limiter. Supported values are `no-op`, `token-bucket`. | +| `polaris.rate-limiter.token-bucket.type` | `default` | Define the token bucket rate limiter. | +| `polaris.rate-limiter.token-bucket.requests-per-second` | `9999` | Define the number of requests per second for the token bucket rate limiter. | +| `polaris.rate-limiter.token-bucket.window` | `PT10S` | Define the window type for the token bucket rate limiter. | +| `polaris.metrics.tags.=` | `application=Polaris` | Define arbitrary metric tags to include in every request. | +| `polaris.metrics.realm-id-tag.api-metrics-enabled` | `false` | Whether to enable the `realm_id` metric tag in API metrics. | +| `polaris.metrics.realm-id-tag.http-metrics-enabled` | `false` | Whether to enable the `realm_id` metric tag in HTTP request metrics. | +| `polaris.metrics.realm-id-tag.http-metrics-max-cardinality` | `100` | The maximum cardinality for the `realm_id` tag in HTTP request metrics. | +| `polaris.tasks.max-concurrent-tasks` | `100` | Define the max number of concurrent tasks. | +| `polaris.tasks.max-queued-tasks` | `1000` | Define the max number of tasks in queue. | There are non Polaris configuration properties that can be useful: diff --git a/site/content/in-dev/unreleased/telemetry.md b/site/content/in-dev/unreleased/telemetry.md index 1e378991ee..8df97f505d 100644 --- a/site/content/in-dev/unreleased/telemetry.md +++ b/site/content/in-dev/unreleased/telemetry.md @@ -47,6 +47,25 @@ polaris.metrics.tags.region=us-west-2 Note that by default Polaris adds one tag: `application=Polaris`. You can override this tag by setting the `polaris.metrics.tags.application=` property. +### Realm ID Tag + +Polaris can add the realm ID as a tag to all API and HTTP request metrics. This is disabled by +default to prevent high cardinality issues, but can be enabled by setting the following properties: + +```properties +polaris.metrics.realm-id-tag.enable-in-api-metrics=true +polaris.metrics.realm-id-tag.enable-in-http-metrics=true +``` + +You should be particularly careful when enabling the realm ID tag in HTTP request metrics, as these +metrics typically have a much higher cardinality than API request metrics. + +In order to prevent the number of tags from growing indefinitely and causing performance issues or +crashing the server, the number of unique realm IDs in HTTP request metrics is limited to 100 by +default. If the number of unique realm IDs exceeds this value, a warning will be logged and no more +HTTP request metrics will be recorded. This threshold can be changed by setting the +`polaris.metrics.realm-id-tag.http-metrics-max-cardinality` property. + ## Traces Traces are published using [OpenTelemetry].