-
Notifications
You must be signed in to change notification settings - Fork 77
[BDP-102028] feat(optimizer): [0/N] Optimizer API and internal model #527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mkuchenbecker
merged 29 commits into
linkedin:main
from
mkuchenbecker:mkuchenb/optimizer-0
May 20, 2026
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
2119555
feat(optimizer): add data model — schema, entities, DTOs, converters
mkuchenbecker 3c93d52
fix: address PR review feedback on optimizer data model
mkuchenbecker 02a5ab3
fix: remove orphan fields from CompleteOperationRequest
mkuchenbecker 1cbe556
Merge branch 'main' into mkuchenb/optimizer-0
mkuchenbecker f82d1b3
fix(optimizer): address PR #527 review feedback
mkuchenbecker 79753f1
fix(optimizer): index table_operations_history on (database_name, tab…
mkuchenbecker 9a129a8
refactor(optimizer): align data model — rename HistoryStatus; String …
mkuchenbecker 681407e
feat(optimizer): add internal model layer
mkuchenbecker e3fb777
perf(optimizer): index table_operations_history for findLatestPerTable
mkuchenbecker d3e1726
refactor(optimizer): enforce layer boundaries in api/ + model/
mkuchenbecker 1d469a7
refactor(optimizer): remove db-layer types from optimizer-0
mkuchenbecker eee8eca
refactor(optimizer): remove DB schema + schema-init properties
mkuchenbecker 328e5b9
refactor(optimizer): scrub MySQL / JPA / datasource references
mkuchenbecker f7a5d20
refactor(optimizer): drop UpsertTableOperationsRequest
mkuchenbecker 2a532b5
refactor(optimizer): drop JobResult from the wire and internal model
mkuchenbecker 2e3a231
feat(optimizer): add debug echo fields to CompleteOperationRequest
mkuchenbecker db5eb29
refactor(optimizer): move application.properties out of optimizer-0
mkuchenbecker 861b584
feat(optimizer): extend model layer for service-only types
mkuchenbecker 188713d
docs(optimizer): comment every field on opt-0 api/ and model/ types
mkuchenbecker 8d64273
refactor(optimizer): remove clusterId from SnapshotMetrics
mkuchenbecker c72aae8
refactor(optimizer): move api↔model conversion onto api types; delete…
mkuchenbecker af23d5e
fix(optimizer): make TableStats self-describing; route DTO conversion…
mkuchenbecker 3aebf64
chore(optimizer): enable toBuilder on model.Table and model.TableOper…
mkuchenbecker b6c7f42
refactor(optimizer): drop fileCount enrichment from model.TableOperation
mkuchenbecker 437a0ed
refactor(optimizer): add Dto suffix to all api/model classes (PR #527…
mkuchenbecker 4f98c22
refactor(optimizer): rename api.model package to api.spec (PR #527 re…
mkuchenbecker b31decf
refactor(optimizer): move Dto suffix from api/spec to model
mkuchenbecker 4e86569
feat(optimizer): propagate jobId through model + api conversions
mkuchenbecker 1fe71f0
refactor(optimizer): rename CompleteOperationRequest → UpdateOperatio…
mkuchenbecker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| plugins { | ||
| id 'openhouse.springboot-ext-conventions' | ||
| id 'org.springframework.boot' version '2.7.8' | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation 'org.springframework.boot:spring-boot-starter-web:2.7.8' | ||
| testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.8' | ||
| } | ||
|
|
||
| test { | ||
| useJUnitPlatform() | ||
| } |
13 changes: 13 additions & 0 deletions
13
...optimizer/src/main/java/com/linkedin/openhouse/optimizer/OptimizerServiceApplication.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.linkedin.openhouse.optimizer; | ||
|
|
||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
|
||
| /** Spring Boot entry point for the Optimizer Service. */ | ||
| @SpringBootApplication | ||
| public class OptimizerServiceApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| SpringApplication.run(OptimizerServiceApplication.class, args); | ||
| } | ||
| } |
21 changes: 21 additions & 0 deletions
21
...ices/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/spec/HistoryStatus.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.linkedin.openhouse.optimizer.api.spec; | ||
|
|
||
| /** Terminal states for a completed Spark maintenance job. */ | ||
| public enum HistoryStatus { | ||
|
|
||
| /** The Spark job for this operation completed successfully. */ | ||
| SUCCESS, | ||
|
|
||
| /** The Spark job for this operation failed. */ | ||
| FAILED; | ||
|
|
||
| /** Convert to the internal-model counterpart. */ | ||
| public com.linkedin.openhouse.optimizer.model.HistoryStatusDto toModel() { | ||
| return com.linkedin.openhouse.optimizer.model.HistoryStatusDto.valueOf(name()); | ||
| } | ||
|
|
||
| /** Build the api-layer enum from the internal-model counterpart. */ | ||
| public static HistoryStatus fromModel(com.linkedin.openhouse.optimizer.model.HistoryStatusDto v) { | ||
| return v == null ? null : HistoryStatus.valueOf(v.name()); | ||
| } | ||
| } | ||
32 changes: 32 additions & 0 deletions
32
...es/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/spec/OperationStatus.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package com.linkedin.openhouse.optimizer.api.spec; | ||
|
|
||
| /** Lifecycle states for a table operation recommendation. */ | ||
| public enum OperationStatus { | ||
|
|
||
| /** Recommended by the Analyzer but not yet claimed by the Scheduler. */ | ||
| PENDING, | ||
|
|
||
| /** Claimed by the Scheduler; waiting for the Jobs Service to return a job ID. */ | ||
| SCHEDULING, | ||
|
|
||
| /** Job submitted to the Jobs Service; the row now carries a {@code jobId}. */ | ||
| SCHEDULED, | ||
|
|
||
| /** | ||
| * Marked by the Scheduler when it detects duplicate PENDING rows for the same {@code (table_uuid, | ||
| * operation_type)}. Only the most-recent PENDING row is claimed; older duplicates are CANCELED | ||
| * before the claim step. | ||
| */ | ||
| CANCELED; | ||
|
|
||
| /** Convert to the internal-model counterpart. */ | ||
| public com.linkedin.openhouse.optimizer.model.OperationStatusDto toModel() { | ||
| return com.linkedin.openhouse.optimizer.model.OperationStatusDto.valueOf(name()); | ||
| } | ||
|
|
||
| /** Build the api-layer enum from the internal-model counterpart. */ | ||
| public static OperationStatus fromModel( | ||
| com.linkedin.openhouse.optimizer.model.OperationStatusDto v) { | ||
| return v == null ? null : OperationStatus.valueOf(v.name()); | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
...ices/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/spec/OperationType.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.linkedin.openhouse.optimizer.api.spec; | ||
|
|
||
| /** Maintenance operation types supported by the continuous optimizer. */ | ||
| public enum OperationType { | ||
| /** Removes orphaned data files no longer referenced by table metadata. */ | ||
| ORPHAN_FILES_DELETION; | ||
|
mkuchenbecker marked this conversation as resolved.
|
||
|
|
||
| /** Convert to the internal-model counterpart. */ | ||
| public com.linkedin.openhouse.optimizer.model.OperationTypeDto toModel() { | ||
| return com.linkedin.openhouse.optimizer.model.OperationTypeDto.valueOf(name()); | ||
| } | ||
|
|
||
| /** Build the api-layer enum from the internal-model counterpart. */ | ||
| public static OperationType fromModel(com.linkedin.openhouse.optimizer.model.OperationTypeDto v) { | ||
| return v == null ? null : OperationType.valueOf(v.name()); | ||
| } | ||
| } | ||
76 changes: 76 additions & 0 deletions
76
...es/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/spec/TableOperations.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package com.linkedin.openhouse.optimizer.api.spec; | ||
|
|
||
| import com.linkedin.openhouse.optimizer.model.TableOperationDto; | ||
| import java.time.Instant; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| /** DTO for {@code table_operations} — Analyzer recommendations read by the Scheduler. */ | ||
| @Data | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class TableOperations { | ||
|
|
||
| /** Client-generated UUID identifying this specific operation recommendation. */ | ||
| private String id; | ||
|
|
||
| /** Stable table identity from the Tables Service. */ | ||
| private String tableUuid; | ||
|
|
||
| /** Denormalized database name for display; not part of the primary key. */ | ||
| private String databaseName; | ||
|
|
||
| /** Denormalized table name for display; not part of the primary key. */ | ||
| private String tableName; | ||
|
|
||
| /** The type of maintenance operation (e.g. ORPHAN_FILES_DELETION). */ | ||
| private OperationType operationType; | ||
|
|
||
| /** {@code PENDING} or {@code SCHEDULED}. Defaults to {@code PENDING} on creation. */ | ||
| private OperationStatus status; | ||
|
|
||
| /** Server-set when the row is first created by the Analyzer. */ | ||
| private Instant createdAt; | ||
|
|
||
| /** Set by the Scheduler when claiming; {@code null} while PENDING. */ | ||
| private Instant scheduledAt; | ||
|
|
||
| /** Job ID returned by the Jobs Service after successful submission. */ | ||
| private String jobId; | ||
|
|
||
| /** Convert to the internal-model counterpart. */ | ||
| public TableOperationDto toModel() { | ||
| return TableOperationDto.builder() | ||
| .id(id) | ||
| .tableUuid(tableUuid) | ||
| .databaseName(databaseName) | ||
| .tableName(tableName) | ||
| .operationType(operationType == null ? null : operationType.toModel()) | ||
| .status(status == null ? null : status.toModel()) | ||
| .createdAt(createdAt) | ||
| .scheduledAt(scheduledAt) | ||
| .jobId(jobId) | ||
| .build(); | ||
| } | ||
|
|
||
| /** Build a wire DTO from the internal-model counterpart. */ | ||
| public static TableOperations fromModel(TableOperationDto op) { | ||
| if (op == null) { | ||
| return null; | ||
| } | ||
| return TableOperations.builder() | ||
| .id(op.getId()) | ||
| .tableUuid(op.getTableUuid()) | ||
| .databaseName(op.getDatabaseName()) | ||
| .tableName(op.getTableName()) | ||
| .operationType(OperationType.fromModel(op.getOperationType())) | ||
| .status(OperationStatus.fromModel(op.getStatus())) | ||
| .createdAt(op.getCreatedAt()) | ||
| .scheduledAt(op.getScheduledAt()) | ||
| .jobId(op.getJobId()) | ||
| .build(); | ||
| } | ||
| } |
66 changes: 66 additions & 0 deletions
66
...mizer/src/main/java/com/linkedin/openhouse/optimizer/api/spec/TableOperationsHistory.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package com.linkedin.openhouse.optimizer.api.spec; | ||
|
|
||
| import com.linkedin.openhouse.optimizer.model.TableOperationsHistoryDto; | ||
| import java.time.Instant; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| /** DTO for {@code table_operations_history} — append-only operation results. */ | ||
| @Data | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class TableOperationsHistory { | ||
|
|
||
| /** Same UUID as the originating {@code table_operations.id}; supplied by the caller. */ | ||
| private String id; | ||
|
|
||
| /** Stable table identity from the Tables Service. */ | ||
| private String tableUuid; | ||
|
|
||
| /** Denormalized database name for display. */ | ||
| private String databaseName; | ||
|
|
||
| /** Denormalized table name for display. */ | ||
| private String tableName; | ||
|
|
||
| /** The type of maintenance operation this history row records. */ | ||
| private OperationType operationType; | ||
|
|
||
| /** When the operation completed, as recorded by the complete endpoint. */ | ||
| private Instant completedAt; | ||
|
|
||
| /** {@code SUCCESS} or {@code FAILED}. */ | ||
| private HistoryStatus status; | ||
|
|
||
| /** Convert to the internal-model counterpart. */ | ||
| public TableOperationsHistoryDto toModel() { | ||
| return TableOperationsHistoryDto.builder() | ||
| .id(id) | ||
| .tableUuid(tableUuid) | ||
| .databaseName(databaseName) | ||
| .tableName(tableName) | ||
| .operationType(operationType == null ? null : operationType.toModel()) | ||
| .completedAt(completedAt) | ||
| .status(status == null ? null : status.toModel()) | ||
| .build(); | ||
| } | ||
|
|
||
| /** Build a wire DTO from the internal-model counterpart. */ | ||
| public static TableOperationsHistory fromModel(TableOperationsHistoryDto h) { | ||
| if (h == null) { | ||
| return null; | ||
| } | ||
| return TableOperationsHistory.builder() | ||
| .id(h.getId()) | ||
| .tableUuid(h.getTableUuid()) | ||
| .databaseName(h.getDatabaseName()) | ||
| .tableName(h.getTableName()) | ||
| .operationType(OperationType.fromModel(h.getOperationType())) | ||
| .completedAt(h.getCompletedAt()) | ||
| .status(HistoryStatus.fromModel(h.getStatus())) | ||
| .build(); | ||
| } | ||
| } |
70 changes: 70 additions & 0 deletions
70
services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/spec/TableStats.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package com.linkedin.openhouse.optimizer.api.spec; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.Collections; | ||
| import java.util.Map; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| /** DTO for {@code table_stats} — used for response payloads. */ | ||
| @Data | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class TableStats { | ||
|
|
||
| /** Stable Iceberg table UUID. Primary key of the stats row. */ | ||
| private String tableUuid; | ||
|
|
||
| /** Denormalized database name for display. */ | ||
| private String databaseName; | ||
|
|
||
| /** Denormalized table name for display. */ | ||
| private String tableName; | ||
|
|
||
| /** Combined snapshot + delta stats payload, stored as JSON. */ | ||
| private TableStatsPayload stats; | ||
|
|
||
| /** Current table properties snapshot (e.g. maintenance opt-in flags). */ | ||
| private Map<String, String> tableProperties; | ||
|
|
||
| /** When this row was last written. Used for staleness monitoring. */ | ||
| private Instant updatedAt; | ||
|
mkuchenbecker marked this conversation as resolved.
|
||
|
|
||
| /** Convert to the internal-model counterpart. */ | ||
| public com.linkedin.openhouse.optimizer.model.TableStatsDto toModel() { | ||
| com.linkedin.openhouse.optimizer.model.TableStatsDto payload = | ||
| stats == null | ||
| ? new com.linkedin.openhouse.optimizer.model.TableStatsDto() | ||
| : stats.toModel(); | ||
| return payload | ||
| .toBuilder() | ||
| .tableUuid(tableUuid) | ||
| .databaseName(databaseName) | ||
| .tableName(tableName) | ||
| .tableProperties(tableProperties != null ? tableProperties : Collections.emptyMap()) | ||
| .updatedAt(updatedAt) | ||
| .build(); | ||
| } | ||
|
|
||
| /** Build a wire DTO from the internal-model counterpart. */ | ||
| public static TableStats fromModel(com.linkedin.openhouse.optimizer.model.TableStatsDto m) { | ||
| if (m == null) { | ||
| return null; | ||
| } | ||
| return TableStats.builder() | ||
| .tableUuid(m.getTableUuid()) | ||
| .databaseName(m.getDatabaseName()) | ||
| .tableName(m.getTableName()) | ||
| .stats( | ||
| TableStatsPayload.builder() | ||
| .snapshot(TableStatsPayload.SnapshotMetricsDto.fromModel(m.getSnapshot())) | ||
| .delta(TableStatsPayload.CommitDeltaDto.fromModel(m.getDelta())) | ||
| .build()) | ||
| .tableProperties(m.getTableProperties()) | ||
| .updatedAt(m.getUpdatedAt()) | ||
| .build(); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.