Skip to content

[#13936] fix(server): expose metrics only on main server - #13938

Merged
roryqi merged 7 commits into
apache:mainfrom
qqqttt123:fix/auxiliary-metrics-endpoints
Oct 20, 2026
Merged

roryqi merged 7 commits into
apache:mainfrom
qqqttt123:fix/auxiliary-metrics-endpoints

Conversation

@roryqi

@roryqi roryqi commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Make metrics servlet registration opt-in. Iceberg and Lance auxiliary listeners never expose /metrics or /prometheus/metrics; their metrics remain available through the Gravitino server endpoint. Standalone services can expose the endpoints only when their service-specific metrics.enabled setting is explicitly enabled.

Why are the changes needed?

The Iceberg REST and Lance REST listeners may be reachable by external query engines. Serving unauthenticated metrics endpoints on those ports can expose operational metrics and metric names. The standalone opt-in preserves monitoring for trusted deployments without reopening these endpoints in auxiliary mode.

Fixes: #13936

Does this PR introduce any user-facing change?

Standalone Iceberg and Lance REST deployments no longer serve metrics endpoints by default. Set gravitino.iceberg-rest.metrics.enabled=true or gravitino.lance-rest.metrics.enabled=true to enable them. Auxiliary mode ignores these settings and keeps metrics on the Gravitino server endpoint.

How was this patch tested?

  • ./gradlew :server-common:test --tests org.apache.gravitino.server.web.TestJettyServer
  • ./gradlew :server:test --tests org.apache.gravitino.server.TestGravitinoServer :iceberg:iceberg-rest-server:compileJava :lance:lance-rest-server:compileJava
  • ./gradlew :iceberg:iceberg-rest-server:test --tests org.apache.gravitino.iceberg.TestRESTService :lance:lance-rest-server:test --tests org.apache.gravitino.lance.TestLanceRESTService

Comment thread docs/iceberg-rest-service.md Outdated
| `gravitino.iceberg-rest.requestHeaderSize` | The maximum size of an HTTP request. | `131072` | No |
| `gravitino.iceberg-rest.responseHeaderSize` | The maximum size of an HTTP response. | `131072` | No |
| `gravitino.iceberg-rest.includeErrorStackTrace` | Whether error responses include server-side stack traces. Set this to `false` in new deployments because responses can expose internal implementation details. | `true` | No |
| `gravitino.iceberg-rest.metrics.enabled` | Whether standalone mode serves `/metrics` and `/prometheus/metrics`. Ignored in auxiliary mode, where metrics remain available on the Gravitino server endpoint. Enable only when the standalone service port is trusted. | `false` | No |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table and the one below are not aligned.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aligned the metrics row with the surrounding configuration table in commit 44dd3299b8. Thanks for catching that.

@yuqi1129 yuqi1129 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for closing this gap. One blocking issue and a few smaller ones below.

Blocking: the standalone Iceberg REST ITs will time out at startup

IcebergRESTServerManager (iceberg/iceberg-rest-server/src/test/.../integration/test/util/IcebergRESTServerManager.java:149) waits for http://host:port/metrics to return 200 before it treats the server as started. Both the embedded and the deploy managers use that checkUri. With this PR, a standalone server returns 404 for /metrics by default, so the manager waits the full 60 seconds and then fails with "GravitinoIcebergRESTServer start failed". This file is not in the diff, so I'm raising it here.

That affects 15 IT classes, including IcebergRESTServiceIT, the Memory/Jdbc/Hive backends, the S3/OSS/GCS/ADLS credential ITs, the Kerberos ITs, IcebergCacheIT, and TestIcebergExtendAPI.

I checked it locally with TestIcebergExtendAPI:

  • On this PR, the server starts ("iceberg-rest web server started ... port 2100") but the probe never gets a 200, and the test fails.
  • On the base commit with the same setup, the test passes.
  • I pointed the probe at 127.0.0.1 for both runs because 0.0.0.0 does not connect on my machine. That edit was local only and has been reverted.

Suggestion: probe /iceberg/health/live instead. IcebergHealthOperations serves it, and LanceNamespaceAuthorizationIT already probes /lance/health/live. I'd avoid enabling metrics.enabled in the IT config, because then every IT would run with a non-default setting. I did not run the deploy-mode ITs locally, but they use the same checkUri.

List<String> customFilterPaths = new ArrayList<>(JettyServer.METRICS_PATH_SPECS);
customFilterPaths.add(ICEBERG_SPEC);
server.addCustomFilters(customFilterPaths.toArray(new String[0]));
server.addCustomFilters(ICEBERG_SPEC);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When standalone metrics.enabled=true, the metrics paths now lose the filter coverage they had before:

  • registerMetricsPathFilters is removed, so /metrics and /prometheus/metrics no longer get RequestContextFilter or HttpAuditFilter. That brings back the gap Servlets mounted outside the /api/* pathspec receive no authentication and no audit #12760 closed, and testMetricsPathsHaveAuditFilterCoverage was deleted along with it.
  • addCustomFilters now covers only ICEBERG_SPEC. Some deployments use customFilters for their own authentication, and before this PR those filters also covered the metrics paths. With the opt-in, the endpoints would be served without them, which works against the goal of this PR.

Could you keep both when metrics are enabled? Something like:

List<String> customFilterPaths = new ArrayList<>();
if (shouldEnableMetrics(auxMode, config)) {
  registerMetricsPathFilters(server, eventBus);
  customFilterPaths.addAll(JettyServer.METRICS_PATH_SPECS);
}
customFilterPaths.add(ICEBERG_SPEC);
server.addCustomFilters(customFilterPaths.toArray(new String[0]));

Then the deleted test could come back as "metrics enabled -> paths covered; disabled -> no metrics servlet".

@roryqi roryqi Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 44dd329: when standalone metrics are enabled, both metrics paths receive RequestContextFilter, HttpAuditFilter, and configured custom filters again. I also added a direct main-server endpoint test. I did not restore the separate standalone audit-path coverage test in this commit; I’ll keep this thread open so that gap is visible.

List<String> customFilterPaths = new ArrayList<>(JettyServer.METRICS_PATH_SPECS);
customFilterPaths.add(LANCE_SPEC);
server.addCustomFilters(customFilterPaths.toArray(new String[0]));
server.addCustomFilters(LANCE_SPEC);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in RESTService: with standalone metrics.enabled=true, the metrics paths lose RequestContextFilter/HttpAuditFilter (GH-12760) and the custom filters. It would be good to restore both when metrics are enabled, and to bring back the audit-coverage test.

@roryqi roryqi Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 44dd329: standalone metrics-enabled mode now applies request-context, audit, and configured custom filters to both metrics paths. I did not add the dedicated standalone audit-coverage test yet, so I’m leaving this thread open.

JettyServerConfig jettyServerConfig =
JettyServerConfig.fromConfig(serverConfig, WEBSERVER_CONF_PREFIX);
server.initialize(jettyServerConfig, SERVER_NAME);
server.addMetricsServlets(gravitinoEnv.metricsSystem());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no unit test guards this line. Locally, deleting it still passes all 13 tests in TestGravitinoServer. Only the ITs would catch it, indirectly, because BaseIT/MiniGravitino use the main /metrics as their startup probe. Maybe add a direct assertion that the main server serves /metrics and /prometheus/metrics?

@roryqi roryqi Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the direct assertion for both metrics endpoints on the main server in commit 44dd329; the targeted TestGravitinoServer test passes.

Comment thread docs/iceberg-rest-service.md Outdated
| `gravitino.iceberg-rest.requestHeaderSize` | The maximum size of an HTTP request. | `131072` | No |
| `gravitino.iceberg-rest.responseHeaderSize` | The maximum size of an HTTP response. | `131072` | No |
| `gravitino.iceberg-rest.includeErrorStackTrace` | Whether error responses include server-side stack traces. Set this to `false` in new deployments because responses can expose internal implementation details. | `true` | No |
| `gravitino.iceberg-rest.metrics.enabled` | Whether standalone mode serves `/metrics` and `/prometheus/metrics`. Ignored in auxiliary mode, where metrics remain available on the Gravitino server endpoint. Enable only when the standalone service port is trusted. | `false` | No |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this changes the default for existing standalone deployments, but there is no way to turn it back on except editing the conf file by hand:

  • Neither the gravitino-iceberg-rest-server nor the gravitino-lance-rest-server chart has a value for metrics.enabled, even though both charts' tests use prometheus.io/scrape: "true".
  • dev/docker/iceberg-rest-server/rewrite_config.py has no env var mapping for it.

Scrapes would silently start returning 404 after upgrade. Could you add a chart value and an env mapping, note the opt-in in docs/metrics.md, and call it out in the release note? A follow-up issue for the chart and image would also be fine.

@roryqi roryqi Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed the deployment opt-in pieces in commit 44dd329: added Helm values for both standalone charts, Docker environment-variable mappings, and the opt-in/security note in docs/metrics.md. I couldn’t find a versioned release-note file in this repository, so the behavior change is documented in the metrics guide for now.

@roryqi

roryqi commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

The standalone Iceberg IT startup probe now checks /iceberg/health/live instead of /metrics, and uses 127.0.0.1 when the configured bind host is 0.0.0.0. The change is in commit 44dd3299b8. I have not resolved any review threads.

@roryqi
roryqi requested a review from yuqi1129 October 19, 2026 11:04
@github-actions

Copy link
Copy Markdown

Code Coverage Report

Overall Project 72.14% -0.33% 🟢
Files changed 49.83% 🔴

Module Coverage
aliyun 19.74% 🔴
api 55.09% 🟢
authorization-common 85.96% 🟢
authorization-ranger 14.44% 🔴
aws 53.54% +14.67% 🟢
aws-bundle 0.0% 🔴
azure 32.1% -14.75% 🔴
azure-bundle 0.0% 🔴
catalog-common 29.49% 🔴
catalog-fileset 82.37% 🟢
catalog-glue 73.24% 🟢
catalog-hive 84.5% 🟢
catalog-jdbc-common 57.98% 🟢
catalog-jdbc-doris 87.56% 🟢
catalog-jdbc-mysql 81.91% 🟢
catalog-jdbc-postgresql 83.76% 🟢
catalog-jdbc-starrocks 79.44% 🟢
catalog-kafka 75.21% 🟢
catalog-lakehouse-generic 86.91% 🟢
catalog-lakehouse-hudi 79.1% 🟢
catalog-lakehouse-iceberg 86.8% 🟢
catalog-lakehouse-paimon 84.36% 🟢
catalog-model 77.99% 🟢
cli 44.62% 🟢
client-java 79.44% 🟢
client-java-runtime 0.0% 🔴
common 60.9% 🟢
core 87.51% 🟢
filesystem-hadoop3 76.89% 🟢
flink 55.44% 🟢
flink-common 32.2% 🔴
flink-runtime 0.0% 🔴
gcp 32.2% 🔴
hadoop-auth 68.0% 🟢
hadoop-common 17.84% 🔴
hive-metastore-common 62.3% 🟢
hive-metastore3-libs 58.8% 🟢
iceberg-aliyun-bundle 0.0% 🔴
iceberg-common 67.14% +8.44% 🟢
iceberg-rest-server 77.45% -2.32% 🟢
idp-basic 86.99% 🟢
integration-test-common 0.0% 🔴
jobs 71.35% 🟢
k8s-job-executor 97.81% 🟢
lance-common 40.73% +2.97% 🟢
lance-rest-server 68.04% -12.17% 🟢
lineage 59.39% 🟢
optimizer 84.27% 🟢
optimizer-api 46.58% 🟢
server 91.44% -0.94% 🟢
server-common 84.03% -1.59% 🟢
spark 61.93% 🟢
tencent 81.78% 🟢
tms 80.0% 🟢
trino-connector 64.88% 🟢
Files
Module File Coverage
aws AwsIrsaCredentialGenerator.java 78.11% 🟢
azure ADLSTokenGenerator.java 0.0% 🔴
iceberg-common IcebergConfig.java 100.0% 🟢
iceberg-rest-server RESTService.java 1.59% 🔴
lance-common LanceConfig.java 95.31% 🟢
lance-rest-server LanceRESTService.java 2.3% 🔴
server GravitinoServer.java 67.72% 🟢
server-common JettyServer.java 65.13% 🟢
spark-common BaseCatalog.java 24.48% 🔴

@roryqi
roryqi merged commit f5060c2 into apache:main Oct 20, 2026
43 checks passed
@roryqi roryqi self-assigned this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug report] Metrics endpoints are exposed without authentication on auxiliary service ports

2 participants