Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
283 commits
Select commit Hold shift + click to select a range
d4b9bca
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 1, 2026
79753f1
fix(optimizer): index table_operations_history on (database_name, tab…
mkuchenbecker May 1, 2026
ae610ae
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 1, 2026
85a432f
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 1, 2026
dceef97
feat(optimizer): unify REST prefix to /v1/optimizer; add name-based h…
mkuchenbecker May 1, 2026
7170599
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 1, 2026
3e03fdb
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 1, 2026
bf04488
fix(optimizer): align apps/optimizer entities with services schema
mkuchenbecker May 12, 2026
f2ac002
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 12, 2026
5f6fa3b
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 12, 2026
6e44c50
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 12, 2026
8054586
refactor(optimizer-analyzer): delete unused AnalyzerConfig
mkuchenbecker May 12, 2026
a9104ae
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 12, 2026
5af5f14
refactor(optimizer-analyzer): remove circuit breaker, defer with TODO
mkuchenbecker May 12, 2026
cbddc5d
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 12, 2026
62f426a
feat(optimizer): add findLatestPerTable to history repo
mkuchenbecker May 12, 2026
e13a31b
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 12, 2026
442f1e4
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 12, 2026
c4f194a
perf(optimizer-analyzer): use findLatestPerTable for history lookup
mkuchenbecker May 12, 2026
790994e
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 12, 2026
6da624a
refactor(optimizer-analyzer): typed OperationType/Status, polish cade…
mkuchenbecker May 12, 2026
9913b77
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 12, 2026
52ba858
refactor(optimizer-analyzer): rename OrphanFilesDeletionAnalyzer → Ca…
mkuchenbecker May 12, 2026
beedad8
fix(optimizer-analyzer): update class name inside renamed files
mkuchenbecker May 12, 2026
f63a952
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 12, 2026
3483b25
perf(optimizer): index table_operations_history for findLatestPerTable
mkuchenbecker May 13, 2026
9748548
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 13, 2026
e4a1ad1
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 13, 2026
f663537
docs(optimizer-analyzer): add scale roadmap as block comment
mkuchenbecker May 13, 2026
8ad9cac
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 13, 2026
d7e3a65
docs(optimizer-analyzer): move scale roadmap to BDP-102182
mkuchenbecker May 13, 2026
0069b92
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 13, 2026
0293009
feat(optimizer): add findDistinctDatabaseNames to TableStatsRepository
mkuchenbecker May 13, 2026
0efab45
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 13, 2026
6fa885d
refactor(optimizer): Optional<T> for optional filter params in servic…
mkuchenbecker May 13, 2026
a5585f4
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 13, 2026
dd4faf2
refactor(optimizer-analyzer): address PR review — required op, per-db…
mkuchenbecker May 13, 2026
0668486
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 13, 2026
91ba362
style(optimizer-analyzer): tighten AnalyzerRunner.analyze body
mkuchenbecker May 13, 2026
b315c11
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 13, 2026
eba1392
feat(optimizer): promote internal model types to shared apps/optimizer
mkuchenbecker May 14, 2026
10ed1bb
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
35bcd38
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
0dbe3d9
refactor(optimizer-analyzer): import shared model + explanatory comment
mkuchenbecker May 14, 2026
e80f9ce
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
ded97d0
refactor(optimizer-scheduler): per-op BinPacker interface, TableOpera…
mkuchenbecker May 14, 2026
e576593
refactor(optimizer): rename apps/optimizer entities + repos to plural…
mkuchenbecker May 14, 2026
9f88a4a
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
6f98e1a
refactor(optimizer): consolidate entities/repos into apps/optimizer; …
mkuchenbecker May 14, 2026
d44783f
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
ef78c17
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
d90c26f
refactor(optimizer): move apps/optimizer module into services/optimizer
mkuchenbecker May 14, 2026
62f33b7
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
17e280f
refactor(optimizer): drop apps/optimizer-data dep; simplify history API
mkuchenbecker May 14, 2026
a5df7e4
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
b0898e3
refactor(optimizer-analyzer): depend on :services:optimizer
mkuchenbecker May 14, 2026
7a0d6d2
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
8142af3
refactor(optimizer-scheduler): depend on :services:optimizer; drop de…
mkuchenbecker May 14, 2026
9a129a8
refactor(optimizer): align data model — rename HistoryStatus; String …
mkuchenbecker May 14, 2026
a8978a0
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
dfb9102
refactor(optimizer): realign entity shapes with optimizer-0
mkuchenbecker May 14, 2026
e3bf9e1
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
fb71bd9
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
681407e
feat(optimizer): add internal model layer
mkuchenbecker May 14, 2026
2005bca
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
b689969
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
7ea8868
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
d7767e8
fix(optimizer-analyzer): rewrite AnalyzerRunnerTest to use entity bui…
mkuchenbecker May 14, 2026
3ec0b0a
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
e3fb777
perf(optimizer): index table_operations_history for findLatestPerTable
mkuchenbecker May 14, 2026
f89889d
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
beaaf88
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
4a8796c
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
949e814
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
d3e1726
refactor(optimizer): enforce layer boundaries in api/ + model/
mkuchenbecker May 14, 2026
db9513a
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
1d469a7
refactor(optimizer): remove db-layer types from optimizer-0
mkuchenbecker May 14, 2026
eee8eca
refactor(optimizer): remove DB schema + schema-init properties
mkuchenbecker May 14, 2026
0567753
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
328e5b9
refactor(optimizer): scrub MySQL / JPA / datasource references
mkuchenbecker May 14, 2026
f7a5d20
refactor(optimizer): drop UpsertTableOperationsRequest
mkuchenbecker May 14, 2026
2a532b5
refactor(optimizer): drop JobResult from the wire and internal model
mkuchenbecker May 14, 2026
2e3a231
feat(optimizer): add debug echo fields to CompleteOperationRequest
mkuchenbecker May 14, 2026
db5eb29
refactor(optimizer): move application.properties out of optimizer-0
mkuchenbecker May 14, 2026
bbcf84a
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
ac3abc0
feat(optimizer): introduce db/ layer with per-layer types
mkuchenbecker May 14, 2026
e79eec7
refactor(optimizer): split TableStats envelope into snapshot + delta …
mkuchenbecker May 14, 2026
f955ded
fix(optimizer): drop CommitDeltaMetrics from TableStatsRow
mkuchenbecker May 14, 2026
13987c1
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
969949d
refactor(optimizer): rewire service layer onto api/model/db mappers
mkuchenbecker May 14, 2026
861b584
feat(optimizer): extend model layer for service-only types
mkuchenbecker May 14, 2026
41d4c6d
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
69d9e8f
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
b60a3bf
feat(optimizer): extend ModelDbMapper for service-only types
mkuchenbecker May 14, 2026
eb6e3be
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
b80b2e5
refactor(optimizer): service layer returns only model/ types
mkuchenbecker May 14, 2026
ef453ca
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
ad11533
refactor(optimizer-analyzer): consume model/ types and ModelDbMapper
mkuchenbecker May 14, 2026
0c36ff2
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
25d98aa
feat(optimizer): restore batch CAS methods on TableOperationsRepository
mkuchenbecker May 14, 2026
31fac5b
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
51dab67
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
3a4f0a8
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
3fee7ab
refactor(optimizer-scheduler): consume model/ types and ModelDbMapper
mkuchenbecker May 14, 2026
188713d
docs(optimizer): comment every field on opt-0 api/ and model/ types
mkuchenbecker May 14, 2026
f060b5e
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
1119699
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
619df83
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
a04cad6
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
8d64273
refactor(optimizer): remove clusterId from SnapshotMetrics
mkuchenbecker May 14, 2026
ee7bcab
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
c1ad246
refactor(optimizer): comment every db/ field; drop clusterId and version
mkuchenbecker May 14, 2026
72b431c
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
0b30130
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
e183906
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
b2fd321
fix(optimizer-scheduler): drop .version(0L) from SchedulerRunnerTest
mkuchenbecker May 14, 2026
c72aae8
refactor(optimizer): move api↔model conversion onto api types; delete…
mkuchenbecker May 14, 2026
1fca287
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 14, 2026
8ae8777
refactor(optimizer): move model↔db conversion onto model types; delet…
mkuchenbecker May 14, 2026
b3aacff
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 14, 2026
bb8aa4d
refactor(optimizer): service + controllers use type to/from methods
mkuchenbecker May 14, 2026
6a23755
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 14, 2026
95456be
refactor(optimizer-analyzer): use type to/from methods; drop ModelDbM…
mkuchenbecker May 14, 2026
b9620e3
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 14, 2026
de9b0e1
refactor(optimizer-scheduler): use type to/from methods; drop ModelDb…
mkuchenbecker May 14, 2026
af23d5e
fix(optimizer): make TableStats self-describing; route DTO conversion…
mkuchenbecker May 15, 2026
3864e42
chore(optimizer): cascade self-describing TableStats from opt-0 to opt-1
mkuchenbecker May 15, 2026
0a1125b
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 15, 2026
a6045b5
feat(optimizer): add TableStats↔TableStatsRow conversion on model
mkuchenbecker May 15, 2026
4427de0
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 15, 2026
db5921e
refactor(optimizer): service stats methods take/return TableStats, no…
mkuchenbecker May 15, 2026
d82c17f
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 15, 2026
f74c6f9
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 15, 2026
3aebf64
chore(optimizer): enable toBuilder on model.Table and model.TableOper…
mkuchenbecker May 15, 2026
bf30f86
chore(optimizer): cascade toBuilder annotations from opt-0 to opt-1
mkuchenbecker May 15, 2026
faba6d7
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 15, 2026
1d56fa6
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 15, 2026
d0ec73e
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 15, 2026
b6c7f42
refactor(optimizer): drop fileCount enrichment from model.TableOperation
mkuchenbecker May 18, 2026
177af95
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 18, 2026
487ac56
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 18, 2026
6ffc703
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 18, 2026
7f51360
chore(analyzer): add TODOs for scale tests and query-builder migration
mkuchenbecker May 18, 2026
2903e0b
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 18, 2026
9ad8861
refactor(scheduler): TableStats at BinPacker interface; Bin owns its …
mkuchenbecker May 18, 2026
a325684
refactor(scheduler): BinPacker takes List<SchedulingCandidate> pairs
mkuchenbecker May 18, 2026
9e5fbae
style(scheduler): convert FileCountBinPacker for-loops to functional …
mkuchenbecker May 18, 2026
7dd1e97
refactor(scheduler): final resultsEndpoint, throw on missing packer, …
mkuchenbecker May 18, 2026
2b06c92
feat(repo): add findClaimedIds for transactional batch-claim verifica…
mkuchenbecker May 18, 2026
5b5aae2
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 18, 2026
c862777
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 18, 2026
7935f43
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 18, 2026
c73d8cb
fix(scheduler): only launch + mark SCHEDULED the ops actually claimed
mkuchenbecker May 18, 2026
ad1bf4c
feat(scheduler): add SingletonBinPacker; register for SNAPSHOT_EXPIRA…
mkuchenbecker May 18, 2026
d9ffe48
docs(scheduler): strip editorial commentary from SingletonBinPacker j…
mkuchenbecker May 18, 2026
ad88893
Revert "docs(scheduler): strip editorial commentary from SingletonBin…
mkuchenbecker May 18, 2026
ac7f84d
Revert "feat(scheduler): add SingletonBinPacker; register for SNAPSHO…
mkuchenbecker May 18, 2026
437a0ed
refactor(optimizer): add Dto suffix to all api/model classes (PR #527…
mkuchenbecker May 19, 2026
aabb51c
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 19, 2026
928d537
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 19, 2026
eedf6d0
refactor(optimizer): update controllers for renamed api/model Dto types
mkuchenbecker May 19, 2026
1166efb
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 19, 2026
e492f7c
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 19, 2026
4f98c22
refactor(optimizer): rename api.model package to api.spec (PR #527 re…
mkuchenbecker May 19, 2026
2c26872
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 19, 2026
b849b7d
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 19, 2026
231efde
refactor(optimizer): update controller imports for api.model -> api.s…
mkuchenbecker May 19, 2026
a2580b1
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 19, 2026
9a0ab09
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 19, 2026
f1c500b
refactor(optimizer): hard-fail in AnalyzerRunner if no analyzer is re…
mkuchenbecker May 19, 2026
d315227
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 19, 2026
f788ab6
fix(analyzer): point @EntityScan at the actual db package
mkuchenbecker May 20, 2026
bb52e7a
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 20, 2026
0b87381
fix(scheduler): point @EntityScan at the actual db package
mkuchenbecker May 20, 2026
b31decf
refactor(optimizer): move Dto suffix from api/spec to model
mkuchenbecker May 20, 2026
caf3294
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 20, 2026
c6a64bf
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 20, 2026
91e89ef
refactor(optimizer): update controller + service refs after Dto suffi…
mkuchenbecker May 20, 2026
8a4251d
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 20, 2026
c305aa9
refactor(analyzer): update model refs after Dto suffix swap
mkuchenbecker May 20, 2026
428cb17
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 20, 2026
f6c4674
refactor(scheduler): update model refs after Dto suffix swap
mkuchenbecker May 20, 2026
4e86569
feat(optimizer): propagate jobId through model + api conversions
mkuchenbecker May 20, 2026
cc8aa80
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 20, 2026
efcceea
feat(optimizer): propagate jobId through model ↔ db conversions
mkuchenbecker May 20, 2026
f85edd5
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 20, 2026
c00f201
chore(optimizer): rename OPTIMIZER_DB_USERNAME → OPTIMIZER_DB_USER
mkuchenbecker May 20, 2026
6998a0f
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 20, 2026
5cbdbcb
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 20, 2026
a89a600
fix(scheduler): four runtime gaps surfaced by docker e2e
mkuchenbecker May 20, 2026
1fe71f0
refactor(optimizer): rename CompleteOperationRequest → UpdateOperatio…
mkuchenbecker May 20, 2026
fb5e726
Merge branch 'mkuchenb/optimizer-0' into mkuchenb/optimizer-1
mkuchenbecker May 20, 2026
ad0c0f1
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 20, 2026
947bedf
refactor(optimizer): rename completeOperation → updateOperation
mkuchenbecker May 20, 2026
ce5745b
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 20, 2026
c99bc3a
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 20, 2026
b96c388
Merge remote-tracking branch 'linkedin/main' into mkuchenb/optimizer-1
mkuchenbecker May 20, 2026
d65b511
refactor(optimizer-repo): unify find/updateBatch with Optional params
mkuchenbecker May 21, 2026
78de390
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 21, 2026
49e43bc
refactor(optimizer-service): use Optional repo API + configurable limit
mkuchenbecker May 21, 2026
210d5f0
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 21, 2026
040046e
refactor(analyzer): switch to Optional repo API + configurable limit
mkuchenbecker May 21, 2026
6dd07bd
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 21, 2026
66aa3e7
refactor(scheduler): switch to Optional repo API + dedup per cycle
mkuchenbecker May 21, 2026
b69e09a
test(optimizer-repo): truncate Instant to micros for CI precision
mkuchenbecker May 21, 2026
a028a98
Merge branch 'mkuchenb/optimizer-1' into mkuchenb/optimizer-2
mkuchenbecker May 21, 2026
2b6f67c
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 21, 2026
231320c
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 21, 2026
6eb6a1e
Merge remote-tracking branch 'linkedin/main' into mkuchenb/optimizer-2
mkuchenbecker May 21, 2026
de39a78
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 21, 2026
3309983
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 21, 2026
a89e037
feat(optimizer): require limit on list-API endpoints
mkuchenbecker May 21, 2026
1e361af
feat(optimizer): basic error-code handling across controllers
mkuchenbecker May 22, 2026
a37169d
refactor(optimizer): simplify error handling per PR review
mkuchenbecker May 22, 2026
6416c9d
refactor(optimizer): drop GlobalExceptionHandler + ApiError; use Spri…
mkuchenbecker May 22, 2026
bbef386
refactor(optimizer): revert UpdateOperationRequest doc edits
mkuchenbecker May 22, 2026
02bbc5c
(wip) feat(optimizer): basic error-code handling across controllers (…
mkuchenbecker May 22, 2026
266e2a7
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 22, 2026
e8d2c7a
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 22, 2026
144da72
Merge remote-tracking branch 'linkedin/mkuchenb/optimizer-2' into mku…
mkuchenbecker May 22, 2026
c74ad5f
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 22, 2026
1866033
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 22, 2026
6ef7964
docs(optimizer): add @ApiResponses to controllers for OpenAPI spec
mkuchenbecker May 22, 2026
357d5c2
Merge branch 'mkuchenb/optimizer-2' into mkuchenb/optimizer-3
mkuchenbecker May 22, 2026
020e94a
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 22, 2026
08bebcb
Merge commit '78ba0abd' into mkuchenb/optimizer-3
mkuchenbecker May 22, 2026
d7dead0
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 22, 2026
9abde3b
refactor(optimizer): inline analyzer.getOperationType().toDb() per re…
mkuchenbecker May 22, 2026
e7321d0
refactor(optimizer-analyzer): audit fixes per review
mkuchenbecker May 23, 2026
eae3a4a
refactor(optimizer-analyzer): nest under apps/optimizer/analyzer
mkuchenbecker May 23, 2026
3ef5eaf
Merge mkuchenb/optimizer-3 + nest scheduler under apps/optimizer/sche…
mkuchenbecker May 23, 2026
a638541
style: spotless format on SchedulerRunner after package rename
mkuchenbecker May 26, 2026
2477530
refactor(optimizer-analyzer): move from apps/ to services/optimizer/a…
mkuchenbecker May 26, 2026
f529a5c
refactor(optimizer-analyzer): rename property to analyzer.max-tables-…
mkuchenbecker May 26, 2026
8e5e97c
style: spotless format on AnalyzerRunner
mkuchenbecker May 26, 2026
87b9a75
fix(optimizer-analyzer): paginate per-DB scan to process all tables
mkuchenbecker May 26, 2026
4b05cb2
style: spotless format on AnalyzerRunner
mkuchenbecker May 26, 2026
21dbf34
Update services/optimizer/analyzer/src/main/java/com/linkedin/openhou…
mkuchenbecker May 26, 2026
adbff95
(wip) refactor(optimizer-analyzer): split AnalyzerApplication into ap…
mkuchenbecker May 27, 2026
eccd2bc
Merge mkuchenb/optimizer-3 into mkuchenb/optimizer-4
mkuchenbecker May 27, 2026
b183f9c
refactor(optimizer-scheduler): split into services/optimizer/schedule…
mkuchenbecker May 27, 2026
663564c
docs(optimizer-analyzer): strip BDP ticket references from javadoc
mkuchenbecker May 27, 2026
cfe5509
Merge branch 'mkuchenb/optimizer-3' into mkuchenb/optimizer-4
mkuchenbecker May 27, 2026
f3fe80a
docs(optimizer-scheduler): strip BDP ticket reference from javadoc
mkuchenbecker May 27, 2026
8cb1a37
Merge remote-tracking branch 'linkedin/main' into mkuchenb/optimizer-4
mkuchenbecker May 27, 2026
9630e57
test(scheduler): use canonical optimizer-schema.sql; drop duplicate
mkuchenbecker May 27, 2026
84a3532
Update services/optimizer/scheduler/src/main/java/com/linkedin/openho…
mkuchenbecker May 27, 2026
2cb7c54
refactor(scheduler): prefix all scheduler config properties with opti…
mkuchenbecker May 27, 2026
3f1173b
refactor(scheduler): exit JVM with Spring exit code for clean shutdown
mkuchenbecker May 27, 2026
10a5dda
refactor(scheduler): adopt full ExitCodeGenerator pattern from review
mkuchenbecker May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/optimizer/schedulerapp/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id 'openhouse.springboot-ext-conventions'
id 'org.springframework.boot' version '2.7.8'
}

// Deployable Spring Boot wrapper around the scheduler library. Holds SchedulerApplication (the
// @SpringBootApplication entry point) and application.properties; the scheduling logic lives in
// :services:optimizer:scheduler.
dependencies {
implementation project(':services:optimizer:scheduler')
implementation 'org.springframework.boot:spring-boot-starter:2.7.8'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.7.8'
runtimeOnly 'mysql:mysql-connector-java:8.0.33'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.linkedin.openhouse.optimizer.scheduler;

import com.linkedin.openhouse.optimizer.model.OperationTypeDto;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

/**
* Entry point for the Optimizer Scheduler application.
*
* <p>Spring Batch–style: implements {@link CommandLineRunner} so the work runs after context
* startup, and {@link ExitCodeGenerator} so the JVM exit code reflects batch outcome. {@code
* SpringApplication.exit(...)} closes the context (triggers {@code @PreDestroy} hooks, drains the
* JPA pool, etc.) so the k8s CronJob pod terminates cleanly with a status reflecting reality.
*/
@Slf4j
@SpringBootApplication
@EntityScan(basePackages = "com.linkedin.openhouse.optimizer.db")
@EnableJpaRepositories(basePackages = "com.linkedin.openhouse.optimizer.repository")
public class SchedulerApplication implements CommandLineRunner, ExitCodeGenerator {

private final SchedulerRunner runner;
private final Map<OperationTypeDto, BinPacker> binPackers;
private int exitCode = 0;

@Autowired
public SchedulerApplication(SchedulerRunner runner, Map<OperationTypeDto, BinPacker> binPackers) {
this.runner = runner;
this.binPackers = binPackers;
}

public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(SchedulerApplication.class, args)));
}

/**
* Runs the scheduler once per registered {@link BinPacker} per process invocation. Each call is
* scoped to one operation type. Any thrown exception is logged and surfaces as a non-zero exit
* code via {@link #getExitCode()} after the context is shut down cleanly.
*/
@Override
public void run(String... args) {
try {
log.info("Scheduler starting; operation types: {}", binPackers.keySet());
binPackers.keySet().forEach(runner::schedule);
log.info("Scheduler completed successfully");
} catch (Exception e) {
log.error("Scheduler failed", e);
exitCode = 1;
}
}

@Override
public int getExitCode() {
return exitCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
spring.application.name=openhouse-optimizer-scheduler
spring.main.web-application-type=none
spring.main.banner-mode=off
spring.datasource.url=${OPTIMIZER_DB_URL:jdbc:h2:mem:schedulerdb;DB_CLOSE_DELAY=-1;MODE=MySQL}
spring.datasource.username=${OPTIMIZER_DB_USER:sa}
spring.datasource.password=${OPTIMIZER_DB_PASSWORD:}
spring.jpa.hibernate.ddl-auto=none
optimizer.scheduler.jobs.base-uri=${JOBS_BASE_URI:http://localhost:8002}
optimizer.scheduler.ofd.max-files-per-bin=${SCHEDULER_OFD_MAX_FILES_PER_BIN:1000000}
optimizer.scheduler.results-endpoint=${SCHEDULER_RESULTS_ENDPOINT:http://openhouse-optimizer:8080/v1/optimizer/operations}
optimizer.scheduler.cluster-id=${SCHEDULER_CLUSTER_ID:LocalHadoopCluster}
33 changes: 33 additions & 0 deletions services/optimizer/scheduler/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
plugins {
id 'openhouse.springboot-ext-conventions'
id 'org.springframework.boot' version '2.7.8'
}

// Library jar — the @SpringBootApplication entry point lives in :apps:optimizer:schedulerapp.
// Disable bootJar so we don't try to assemble a runnable jar from a library that has no main
// class; keep jar enabled so consumers (the apps wrapper) get a normal library artifact.
bootJar {
enabled = false
}

jar {
enabled = true
archiveClassifier = ''
}

dependencies {
// api: the scheduler's public types (e.g. BinPacker, OperationTypeDto) come from
// :services:optimizer, so consumers of this library see them on their compile classpath.
api project(':services:optimizer')
implementation 'org.springframework.boot:spring-boot-starter:2.7.8'
implementation 'org.springframework.boot:spring-boot-starter-webflux:2.7.8'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.7.8'
implementation 'org.springframework.boot:spring-boot-starter-aop:2.7.8'
runtimeOnly 'mysql:mysql-connector-java:8.0.33'
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.8'
testRuntimeOnly 'com.h2database:h2'
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.linkedin.openhouse.optimizer.scheduler;

import com.linkedin.openhouse.optimizer.model.OperationTypeDto;
import com.linkedin.openhouse.optimizer.model.TableOperationDto;
import com.linkedin.openhouse.optimizer.scheduler.client.JobsServiceClient;
import java.time.Instant;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

/**
* A set of operations the scheduler will submit together as a single Spark job. A bin owns its own
* launch — callers ask it to schedule itself and react to the returned job id. The surrounding
* status-update machinery (claim, mark-scheduled, revert-to-pending) lives in the scheduler because
* it is shared across all bins regardless of operation type.
*/
@RequiredArgsConstructor
public class Bin {
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.

I am introducing BinItem as part of this PR - https://github.com/linkedin/openhouse/pull/599/changes#diff-5e026d8449953ed5f2853964c9fc6427827dac24fa3b9dba10318fb6618fb703 to represent data a granular level. I will rebase my PR once this PR is merged.


@Getter private final OperationTypeDto operationType;
@Getter private final List<TableOperationDto> operations;
Comment on lines +25 to +26
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.

Shall we keep bin packing generic as common utility instead of referencing internal models and operations?

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.

That should give more flexibility and we should be able to integrate well with the optimizer flow as well existing scheduler flow. I am planning to leverage as a common lib as used in this PR - https://github.com/linkedin/openhouse/pull/604/changes#diff-bd8bddafa29e6a0d0dcc04642cf89b969c4890f53efa9828826e51c25f970a7d.


/** Operation UUIDs in this bin, parallel to {@link #getTableNames()}. */
public List<String> getOperationIds() {
return operations.stream().map(TableOperationDto::getId).collect(Collectors.toList());
}

/** Fully-qualified {@code database.table} identifiers for the operations in this bin. */
public List<String> getTableNames() {
return operations.stream()
.map(op -> op.getDatabaseName() + "." + op.getTableName())
.collect(Collectors.toList());
}

/**
* Return a new {@link Bin} containing only the operations whose IDs are in {@code keepIds}. Used
* by the scheduler to narrow the bin to the rows it actually claimed before launching the job.
*/
public Bin subset(Collection<String> keepIds) {
Set<String> keep = new HashSet<>(keepIds);
List<TableOperationDto> filtered =
operations.stream().filter(op -> keep.contains(op.getId())).collect(Collectors.toList());
return new Bin(operationType, filtered);
}

/**
* Submit this bin as a single Spark job. Returns the job id on success, or empty on submission
* failure — the caller is responsible for the surrounding status updates.
*/
public Optional<String> schedule(JobsServiceClient client, String resultsEndpoint) {
String jobName =
"batched-" + operationType.name().toLowerCase() + "-" + Instant.now().toEpochMilli();
return client.launch(
jobName, operationType.name(), getTableNames(), getOperationIds(), resultsEndpoint);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.linkedin.openhouse.optimizer.scheduler;

import com.linkedin.openhouse.optimizer.model.TableStatsDto;
import java.util.List;

/**
* Strategy for packing a set of operations into bins for batched job submission. Implementations
* encode the constraints of a particular packing dimension (file count, partition count, etc.);
* binding to an operation type is the responsibility of the scheduler configuration, not the
* strategy class.
*
* <p>{@link TableStatsDto} is the cost source at the interface boundary, carried alongside each
* operation in a {@link SchedulingCandidate}. Implementations project the stats down to the minimal
* data needed to make their packing decision (e.g. file count for OFD) and do not retain the full
* stats payload in the returned bins.
*/
public interface BinPacker {

/**
* Pack {@code pending} into one or more {@link Bin}s. Each returned bin is non-empty; the
* scheduler dispatches one Spark job per bin.
*/
List<Bin> pack(List<SchedulingCandidate> pending);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.linkedin.openhouse.optimizer.scheduler;

import com.linkedin.openhouse.optimizer.model.OperationTypeDto;
import com.linkedin.openhouse.optimizer.model.TableOperationDto;
import com.linkedin.openhouse.optimizer.model.TableStatsDto;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import lombok.RequiredArgsConstructor;

/**
* Greedy first-fit-descending bin-packer keyed on per-table file count, projected from each
* candidate's {@link TableStatsDto}.
*
* <p>Candidates are sorted by descending file count, then assigned to the first bin whose running
* total stays at or below {@code maxFilesPerBin}. An operation larger than the limit gets its own
* bin (oversized bins are allowed — we never drop an operation).
*/
@RequiredArgsConstructor
public class FileCountBinPacker implements BinPacker {

private final OperationTypeDto operationType;
private final long maxFilesPerBin;

@Override
public List<Bin> pack(List<SchedulingCandidate> pending) {
if (pending.isEmpty()) {
return List.of();
}

// Project once: each candidate's packing cost is just a long, keyed by operation id.
Map<String, Long> costByOperationId =
pending.stream()
.collect(Collectors.toMap(c -> c.getOperation().getId(), c -> cost(c.getStats())));

List<TableOperationDto> sorted =
pending.stream()
.map(SchedulingCandidate::getOperation)
.sorted(
Comparator.comparingLong(
(TableOperationDto op) -> costByOperationId.get(op.getId()))
.reversed())
.collect(Collectors.toList());

// First-fit-descending is inherently stateful — each placement depends on the running totals
// for bins assembled so far.
List<List<TableOperationDto>> binContents = new ArrayList<>();
List<Long> binTotals = new ArrayList<>();
sorted.forEach(
op -> {
long c = costByOperationId.get(op.getId());
OptionalInt placed =
IntStream.range(0, binContents.size())
.filter(i -> binTotals.get(i) + c <= maxFilesPerBin || binTotals.get(i) == 0)
.findFirst();
if (placed.isPresent()) {
int idx = placed.getAsInt();
binContents.get(idx).add(op);
binTotals.set(idx, binTotals.get(idx) + c);
} else {
List<TableOperationDto> newBin = new ArrayList<>();
newBin.add(op);
binContents.add(newBin);
binTotals.add(c);
}
});

return binContents.stream()
.map(ops -> new Bin(operationType, ops))
.collect(Collectors.toList());
}

private static long cost(TableStatsDto stats) {
if (stats == null || stats.getSnapshot() == null) {
return 0L;
}
Long n = stats.getSnapshot().getNumCurrentFiles();
return n != null ? n : 0L;
}
}
Loading