feat(metrics): Add MetricsPersistence SPI for backend-agnostic metrics storage (#3337)#3616
feat(metrics): Add MetricsPersistence SPI for backend-agnostic metrics storage (#3337)#3616obelix74 wants to merge 11 commits intoapache:mainfrom
Conversation
|
@dimas-b This is the PR for the SPI, as you suggested. This contains the document feedback incorporated. |
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/ScanMetricsRecord.java
Outdated
Show resolved
Hide resolved
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/ScanMetricsRecord.java
Outdated
Show resolved
Hide resolved
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsPersistence.java
Outdated
Show resolved
Hide resolved
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/ReportIdToken.java
Show resolved
Hide resolved
...ris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsQueryCriteria.java
Outdated
Show resolved
Hide resolved
...s-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsRecordConverter.java
Outdated
Show resolved
Hide resolved
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/ScanMetricsRecord.java
Outdated
Show resolved
Hide resolved
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/ScanMetricsRecord.java
Outdated
Show resolved
Hide resolved
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/CommitMetricsRecord.java
Outdated
Show resolved
Hide resolved
...ore/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsPersistenceFactory.java
Outdated
Show resolved
Hide resolved
|
@dimas-b Thank you again for the detailed review. I have addressed all your questions. There are a few open items.
|
21b290d to
8f1e143
Compare
...ris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsQueryCriteria.java
Outdated
Show resolved
Hide resolved
...ris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsQueryCriteria.java
Outdated
Show resolved
Hide resolved
...ris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsQueryCriteria.java
Outdated
Show resolved
Hide resolved
dimas-b
left a comment
There was a problem hiding this comment.
LGTM overall... Just a couple more minor comments.
...ris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsQueryCriteria.java
Outdated
Show resolved
Hide resolved
...is-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsRecordIdentity.java
Outdated
Show resolved
Hide resolved
|
@obelix74 : please rebase to fix CI |
dimas-b
left a comment
There was a problem hiding this comment.
Ok, last comment from me :)
...ris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsQueryCriteria.java
Outdated
Show resolved
Hide resolved
…s storage This commit introduces a Service Provider Interface (SPI) for persisting Iceberg metrics reports, addressing the extensibility concerns raised in the design review. Key components: - MetricsPersistence: Main SPI interface with write and query operations - NoOpMetricsPersistence: Default do-nothing implementation for backends that don't support metrics persistence - ScanMetricsRecord: Immutable interface for scan metrics data - CommitMetricsRecord: Immutable interface for commit metrics data - MetricsQueryCriteria: Query parameters with filtering and pagination - MetricsContext: Context for conversion (realm, catalog, principal info) - MetricsPersistenceFactory: Factory for realm-scoped instances - MetricsRecordConverter: Converts Iceberg reports to SPI records Design principles: - Backend-agnostic: Can be implemented by JDBC, NoSQL, or custom backends - No instanceof checks: Service code calls interface methods directly - Idempotent writes: Same reportId written twice has no effect - Graceful degradation: Unsupported backends return empty results Relates to: apache#3337
Remove fields that can be obtained from ambient request context at write time: - principalName: Available from SecurityContext/PolarisPrincipal - requestId: Not well-defined in Polaris; unclear what request it refers to - otelTraceId/otelSpanId: Available from OTel context via Span.current() Keep reportTraceId as it's a client-provided value from the report metadata that cannot be obtained from the ambient context. Rename otelTraceId filter in MetricsQueryCriteria to reportTraceId to match the field that is actually stored in the records. This keeps the SPI focused on business data (the metrics themselves) rather than infrastructure concerns (tracing, authentication) which the persistence implementation can obtain from the ambient context at write time if needed.
…tern - Create ReportIdToken for cursor-based pagination using report ID (UUID) - Remove limit() and offset() from MetricsQueryCriteria - Update MetricsPersistence to use PageToken parameter and return Page<T> - Update NoOpMetricsPersistence to return empty Page objects - Register ReportIdToken via service loader This change makes the SPI truly backend-agnostic by using the existing Polaris PageToken pattern instead of RDBMS-specific offset pagination. Each backend can implement cursors in their optimal way (keyset for RDBMS, continuation tokens for NoSQL). Addresses reviewer feedback on MetricsQueryCriteria.offset() field.
Per reviewer feedback: - Replace Iceberg's TableIdentifier with separate namespace/tableName strings in MetricsRecordIdentity to avoid Iceberg dependencies in Polaris SPI - Remove catalogName from records, keep only catalogId since catalog names can change over time (via rename operations) - Update MetricsQueryCriteria to use catalogId (OptionalLong) instead of catalogName - Update MetricsRecordConverter to extract namespace/tableName from TableIdentifier The service layer (MetricsRecordConverter) still accepts TableIdentifier and performs the conversion to primitives for the SPI records.
Per reviewer feedback, namespace is now represented as a List<String> of individual levels rather than a dot-separated string. This avoids ambiguity when namespace segments contain dots. Changes: - MetricsRecordIdentity: namespace() now returns List<String> - MetricsQueryCriteria: namespace() now returns List<String> - MetricsRecordConverter: namespaceToList() converts Iceberg Namespace to List<String> using Arrays.asList() The persistence implementation handles the serialization format.
Per reviewer feedback: - r2766326028: Use table ID (same as catalog ID) since table names can change - r2766343275: Avoid denormalizing table names to prevent correctness issues - r2766321215: Return builder with table info, add time ranges at call site Changes: - MetricsRecordIdentity: tableName() -> tableId() (long) - MetricsQueryCriteria: tableName() -> tableId() (OptionalLong) - MetricsQueryCriteria.forTable(): Returns builder with catalogId/tableId - MetricsRecordConverter: tableIdentifier(TableIdentifier) -> tableId(long) + namespace(List<String>) The caller (PersistingMetricsReporter) now needs to resolve table entity ID before creating records, similar to how catalogId is resolved.
Per reviewer feedback - since we query by tableId, namespace is implicit. If users want to query by namespace, the service layer should resolve namespace to table IDs using the current catalog state, then query by those IDs. This avoids confusion with table moves over time. Namespace is still stored in MetricsRecordIdentity for display purposes.
9a9e149 to
7d4212c
Compare
dimas-b
left a comment
There was a problem hiding this comment.
I think this PR is reasonable to merge at this point, assume future SPI changes are not ruled out.
polaris-core/src/main/java/org/apache/polaris/core/persistence/metrics/MetricsPersistence.java
Show resolved
Hide resolved
Added @beta annotation from Guava to all public types in the Metrics Persistence SPI package to signal that this API is experimental and may change in future releases. Annotated types: - MetricsPersistence - ScanMetricsRecord - CommitMetricsRecord - MetricsQueryCriteria - MetricsRecordIdentity - ReportIdToken
|
This PR is one week old and it looks like it did not attract attention from many contributors 😅 (thanks for your review, @evindj !). So, unless fresh comments appear, I'm planning to merge tomorrow. Since the new SPI is marked as |
Allow callers to specify the timestamp for metrics records, defaulting to Instant.now() if not provided. This enables the reporter to use the received timestamp rather than the conversion time.
This commit introduces a Service Provider Interface (SPI) for persisting Iceberg metrics reports, addressing the extensibility concerns raised in the design review.
Key components:
Relates to: #3337
Checklist
CHANGELOG.md(if needed)site/content/in-dev/unreleased(if needed)