Skip to content

feat(optimizer): [0/N] Optimizer Data Model#527

Open
mkuchenbecker wants to merge 6 commits intolinkedin:mainfrom
mkuchenbecker:mkuchenb/optimizer-0
Open

feat(optimizer): [0/N] Optimizer Data Model#527
mkuchenbecker wants to merge 6 commits intolinkedin:mainfrom
mkuchenbecker:mkuchenb/optimizer-0

Conversation

@mkuchenbecker
Copy link
Copy Markdown
Collaborator

@mkuchenbecker mkuchenbecker commented Apr 3, 2026

Optimizer Stack

PR Content
#527 (this) Data Model
#530 Database Repos
#531 REST service
#533 Analyzer app
#534 Scheduler app
#tbd Spark BatchedOFD app
#tbd Infra, docker-compose, smoke test

Summary

PR 0 of N in the optimizer stack.
Overall Project
Service Design doc.

Introduces the optimizer service module mysql data model.

image

Changes

  • Client-facing API Changes
  • Internal API Changes
  • Bug Fixes
  • New Features
  • Performance Improvements
  • Code Style
  • Refactoring
  • Documentation
  • Tests

Testing Done

  • Manually Tested on local docker setup. Please include commands ran, and their output.
  • Added new tests for the changes made.
  • Updated existing tests to reflect the changes made.
  • No tests added or updated. Please explain why. If unsure, please feel free to ask for help.
  • Some other form of testing like staging or soak time in production. Please explain.

This PR contains only the data model (entities, DTOs, converters). Repository tests follow in PR 1. Verified:

  • ./gradlew :services:optimizer:compileJava passes
  • ./gradlew compileJava (full project) passes with no regressions
  • Spotless formatting passes

Additional Information

  • Breaking Changes
  • Deprecations
  • Large PR broken into smaller PRs, and PR plan linked in the description.

Introduces the optimizer service module with:
- MySQL/H2 schema for table_operations, table_stats, table_stats_history,
  and table_operations_history
- JPA entities with JSON column support (vladmihalcea hibernate-types)
- All model/DTO/enum types: OperationType, OperationStatus, TableStats,
  CompleteOperationRequest, JobResult, OperationMetrics, etc.
- JPA AttributeConverters for JobResult and OperationMetrics JSON columns
- MapStruct mapper (OptimizerMapper) for entity→DTO conversion
- Spring Boot application shell and build wiring (settings.gradle,
  build.gradle dockerPrereqs)

No repositories, controllers, or service layer yet — those follow in
subsequent PRs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove OperationMetrics class and converter; stats are read
  directly from table_stats instead of duplicating into operations
- Remove orphanFilesDeleted/orphanBytesDeleted from history entity,
  DTO, and schema; operation-specific data belongs in the result JSON
- Add addedSizeBytes to CommitDelta for tracking write volume
- Fix OperationType javadoc to describe current state, not roadmap
- Fix TableOperationsHistoryRow javadoc: written on operation
  complete, not by Spark app directly
- Add field comments to all DTOs and request objects

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mkuchenbecker mkuchenbecker changed the title feat(optimizer): add data model — schema, entities, DTOs, converters feat(optimizer): [1/N] data model Apr 6, 2026
These fields never belonged in the data model — remove them at the
source rather than adding then deleting in a later PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mkuchenbecker mkuchenbecker changed the title feat(optimizer): [1/N] data model feat(optimizer): [1/N] Optimizer Data Model Apr 6, 2026
@mkuchenbecker mkuchenbecker marked this pull request as ready for review April 6, 2026 19:46
@mkuchenbecker mkuchenbecker changed the title feat(optimizer): [1/N] Optimizer Data Model feat(optimizer): [0/N] Optimizer Data Model Apr 6, 2026

/** Terminal states for a completed Spark maintenance job. */
public enum OperationHistoryStatus {
SUCCESS,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should have keep the existing status such as canceled, queued etc. These are valid status as some times jobs could not be submitted due to GGW/Yarn issue etc.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Happy to add these, but it's unclear how they would be populated given the current lifecycle. My general position is to add states as they are actually used.

private String jobId;

/** Reserved for future per-operation metadata; currently unused. */
private String metrics;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we have a class instead to capture more info? Or do we plan to capture json string here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Removed the metrics field from the DTO, entity, and schema since it is unused. A -- TODO block comment in the schema notes that per-operation metric columns will be added as operations are onboarded.

/** Same UUID as the originating {@code table_operations.id}. Set by the caller; not generated. */
@Id
@Column(name = "id", nullable = false, length = 36)
private String id;
Copy link
Copy Markdown
Member

@abhisheknath2011 abhisheknath2011 Apr 8, 2026

Choose a reason for hiding this comment

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

Looks like this UUID and generated as part of job submission?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Yes, it is a UUID, but it is not generated at submission. It is the same UUID as the originating table_operations.id, set by the Analyzer when the recommendation row is first created. The history row reuses that UUID when the complete endpoint is called, so each cycle is traceable end-to-end by a single id.

private String tableUuid;

@Column(name = "database_name", nullable = false, length = 255)
private String databaseName;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems to be 128 char long in the current prod schema.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Updated to VARCHAR(128) to align with the existing prod schema. Applied across TableOperationsRow, TableOperationsHistoryRow, TableStatsRow, TableStatsHistoryRow, and the SQL schema.

@Column(name = "database_name", nullable = false, length = 255)
private String databaseName;

@Column(name = "table_name", nullable = false, length = 255)
Copy link
Copy Markdown
Member

@abhisheknath2011 abhisheknath2011 Apr 8, 2026

Choose a reason for hiding this comment

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

table name is also 128 char long. But yeah we can double check.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Updated to VARCHAR(128) for consistency.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this auto increment id or primary key?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Changed to a UUID (VARCHAR(36)) for consistency with TableOperationsRow and TableOperationsHistoryRow. It is the primary key, set by the caller; no DB-side auto-allocation.

@Column(name = "table_uuid", nullable = false, length = 36)
private String tableUuid;

@Column(name = "database_id", nullable = false, length = 255)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we use only database_name for consistency?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Renamed databaseIddatabaseName (column database_iddatabase_name) in both TableStatsRow and TableStatsHistoryRow, matching the operations entities.

-- Optimizer Service Schema
-- Compatible with MySQL (production) and H2 in MySQL mode (tests).
CREATE TABLE IF NOT EXISTS table_operations (
id VARCHAR(36) NOT NULL,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we consider adding indexes for these tables too?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Plan is to add indexes when query patterns require them — current PKs cover the access paths used by the analyzer and scheduler today. All key columns are VARCHAR/TIMESTAMP, so secondary indexes can be added cheaply once a real query path needs one.


/** When the operation completed, as recorded by the complete endpoint. */
@Column(name = "submitted_at", nullable = false)
private Instant submittedAt;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

SHould this be completionTime instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Renamed submittedAtcompletedAt (column submitted_atcompleted_at, index idx_submitted_atidx_completed_at). The history row is written when the complete endpoint is called, so the timestamp captures completion. Submission time is already on table_operations.scheduled_at.

@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public static class CommitDelta {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

does this also require @JsonIgnoreProperties ? could provide forward compatibility or safeguard during upgrades in case of new fields addition

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude: Added @JsonIgnoreProperties(ignoreUnknown = true) to CommitDelta for consistency with TableStats and SnapshotMetrics.

mkuchenbecker and others added 3 commits April 30, 2026 09:55
- Widen-to-tighten: VARCHAR(255) -> VARCHAR(128) for database_name
  and table_name across all entities and the schema, aligning with
  prod conventions (can always be widened later, not tightened).
- Rename databaseId -> databaseName in TableStatsRow,
  TableStatsHistoryRow, TableStatsDto, TableStatsHistoryDto, and
  UpsertTableStatsRequest for consistency with the operations
  entities and DTOs.
- Drop the unused metrics field from TableOperationsRow,
  TableOperationsDto, and the schema. Add a TODO note in the schema
  that per-operation metric columns will be added as operations are
  onboarded.
- Rename submittedAt -> completedAt in TableOperationsHistoryRow,
  TableOperationsHistoryDto, and the schema (column submitted_at ->
  completed_at, index idx_submitted_at -> idx_completed_at). The
  history row is written when the complete endpoint is called, so
  the timestamp captures completion; submission time is already on
  table_operations.scheduled_at.
- Change TableStatsHistoryRow.id from BIGINT auto-increment to
  VARCHAR(36) UUID, set by the caller, matching the other
  id-bearing entities.
- Add @JsonIgnoreProperties(ignoreUnknown = true) to CommitDelta
  for consistency with TableStats and SnapshotMetrics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…le_name)

Add a composite secondary index on (database_name, table_name) to
table_operations_history at the schema and entity layers. This
backs a new name-based history-lookup endpoint added on
optimizer-2; without the index, the query degrades to a full scan
on a table that grows with every operation completion.

The other three optimizer tables get no new indexes — no new query
patterns on them this round.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

3 participants