perf(roms): avoid hydrating full Rom rows for siblings on list endpoint#3432
Conversation
The paginated ROM list eager-loaded sibling_roms via selectinload, which hydrated full Rom ORM instances (including heavy JSON metadata columns) for every sibling even though only an existence/count check was needed on the frontend. On large collections this dominated request latency. Split sibling handling by response shape: - SimpleRomSchema (list): siblings is now list[int]; populated per page by a single SELECT against the sibling_roms view projecting only (rom_id, sibling_rom_id) — no Rom row hydration. - DetailedRomSchema (detail): keeps full SiblingRomSchema objects, with load_only on (id, name, fs_name_no_tags, fs_name_no_ext) so sibling rows stop dragging in JSON metadata. Frontend usage already only consumes siblings.length on list views; the detail-page VersionSwitcher continues to receive the richer schema.
There was a problem hiding this comment.
Pull request overview
This PR optimizes the ROM list endpoint by avoiding eager-loading full sibling Rom ORM instances (and their heavy JSON metadata) when the list UI only needs sibling existence/count semantics.
Changes:
- Changed
SimpleRomSchema.siblings(list endpoint) tolist[int]and populated it via a single query against thesibling_romsview. - Kept detailed endpoint sibling objects, but limited sibling ROM column hydration via
load_only(...). - Removed
selectinload(Rom.sibling_roms)from the list-query options to prevent unnecessary ORM hydration.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| frontend/src/generated/models/SimpleRomSchema.ts | Updates generated TS type so list responses expose siblings: number[]. |
| backend/handler/database/roms_handler.py | Adds get_sibling_ids_for_roms helper and limits sibling hydration in detail queries via load_only. |
| backend/endpoints/roms/init.py | Updates paginated list endpoint transformer to attach sibling ID lists per page. |
| backend/endpoints/responses/rom.py | Splits siblings field by schema type: int[] for simple, SiblingRomSchema[] for detailed. |
Files not reviewed (1)
- frontend/src/generated/models/SimpleRomSchema.ts: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 7 changed files in this pull request and generated 3 comments.
Files not reviewed (2)
- frontend/src/generated/models/DetailedRomSchema.ts: Language not supported
- frontend/src/generated/models/SimpleRomSchema.ts: Language not supported
Test Results (postgresql) 1 files 1 suites 4m 25s ⏱️ Results for commit fb3cc1d. ♻️ This comment has been updated with latest results. |
Test Results (mariadb) 1 files 1 suites 4m 8s ⏱️ Results for commit fb3cc1d. ♻️ This comment has been updated with latest results. |
☂️ Python Coverage
Overall Coverage
New FilesNo new covered files... Modified Files
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The paginated ROM list eager-loaded sibling_roms via selectinload, which
hydrated full Rom ORM instances (including heavy JSON metadata columns)
for every sibling even though only an existence/count check was needed
on the frontend. On large collections this dominated request latency.
Split sibling handling by response shape:
by a single SELECT against the sibling_roms view projecting only
(rom_id, sibling_rom_id) — no Rom row hydration.
load_only on (id, name, fs_name_no_tags, fs_name_no_ext) so sibling
rows stop dragging in JSON metadata.
Frontend usage already only consumes siblings.length on list views; the
detail-page VersionSwitcher continues to receive the richer schema.