Conversation
Table detail reads fetched custom metrics once per column and column extensions once per column, so a 100-column table with customMetrics cost 101 entity_extension queries per request. TableMetadataLoader now loads table and column metrics for a whole batch of tables with one prefix query, and column extensions with one keyed query, keeping the per-request count independent of column width. The paginated column endpoints share the same loader. Part of #32946 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| @@ -2936,11 +2877,7 @@ private ResultList<Column> getTableColumnsInternal( | |||
| } | |||
|
|
|||
| if (fieldsParam != null && fieldsParam.contains("customMetrics")) { | |||
There was a problem hiding this comment.
💡 Bug: Paginated /columns extension read not routed through loader
In getTableColumns, the customMetrics branch was converted to metadataLoader.loadColumnMetrics, but the adjacent extension branch (lines 2883-2906) still reads via getExtensionsByJsonSchema(id, COLUMN_EXTENSION_JSON_SCHEMA), filtering on jsonschema = 'columnExtension'. The new loadColumnExtensions used by setFields and the single-column read (enrichSingleColumnFields) resolves by the exact persisted key regardless of jsonSchema, so a legacy column-extension row stored under a different jsonSchema will be returned by GET /v1/tables/{id}?fields=columns,extension and the single-column read but NOT by the paginated GET /v1/tables/{id}/columns?fields=extension. This contradicts the PR's stated claim that "the paginated path already read by key." Consider routing this branch through metadataLoader.loadColumnExtensions(table.getId(), paginatedColumns) for consistency.
Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 0 resolved / 1 findingsBatch table custom-metric and column-extension reads reduce per-request queries from 101 down to 1 across 100-column tables, with comprehensive test coverage. Consider routing the paginated 💡 Bug: Paginated /columns extension read not routed through loader📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java:2879-2893 In 🤖 Prompt for agentsOptionsDisplay: compact → Counting what did not apply, without listing it. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
Describe your changes:
Fixes #33353
Part of #32946, shipped on its own as the reviewer suggested: this is the "505 → 5 statements on table metrics" win from #33248, ported onto the unchanged
EntityRepositoryso it can merge and cherry-pick today.Table detail reads fetched custom metrics once per column and column extensions once per column, so
GET /v1/tables/{id}?fields=columns,customMetricson a 100-column table ran 101entity_extensionqueries.TableMetadataLoadernow loads table and column metrics for a whole batch of tables with one prefix query, and column extensions with one keyed query (getExtensionsByKeys, chunked like the otherINlists), so the per-request count no longer depends on column width. The paginated column endpoints and the single-column read share the same loader, andTableRepositoryloses its four per-column readers.Type of change:
High-level design:
TableMetadataLoader(new,jdbi3): three read operations, one query each:loadMetrics(tables, includeColumns),loadColumnMetrics(tableId, columns),loadColumnExtensions(tableId, columns). It takes aSupplier<EntityExtensionDAO>so the reads join whatever transaction the caller already holds.EntityExtensionDAO.getExtensionsByKeys:WHERE id = :id AND extension IN (<keys>), chunked throughEntityDAO.queryInChunks. Column extensions are looked up by their exact persisted key, so legacy rows stored under anotherjsonSchemaare still returned (the paginated path already read by key).TableRepository:setFields,fetchAndSetCustomMetrics, the two paginated column reads and the single-column read call the loader;getCustomMetrics,batchFetchCustomMetrics,batchFetchCustomMetricsByColumnandgetColumnExtensionare deleted. The public constants keep their values (they now alias the loader's).[].SqlQueryCounter(test util) is byte-identical to the one in Fixes 32946: Pin entity repository lifecycle and cache contracts (1/7) #33344 so the two PRs merge in either order.Tests:
Use cases covered
GET /v1/tables/{id}?fields=columns,customMetricsat 3, 100 and 1,000 columns: one extension query, table metric and column metric both present, unrelatedcustomMetrics.table.tableish.*rows ignored.fields=columns,extension: one table-extension query plus one keyed query; a legacy column-extension row stored under anotherjsonSchemais returned.fields=columnsalone runs zero extension queries and leavescustomMetricsnull.setFieldsInBulkacross two tables with the same column names: one query, metrics not mixed across tables.GET /v1/tables/{id}/columns?fields=customMetrics&limit=3: one extension query, counted only inside the HTTP request.Unit tests
TableMetadataLoaderTest(8 tests, no database): absent inputs need no DAO, selected-column filtering, table-only reads do not decode column metrics, overlapping column names across tables stay distinct, malformed or unresolvable keys do not stop later columns, DAO failure clears stale values.Backend integration tests
TableMetadataReadIT(8 tests, real SQL statement counter,@Isolated).main'sTableRepository(PostgreSQL): 5 of 8 fail on the query budget alone: 3 columns → 4 queries, 100 → 101, 1,000 → 1,001;fields=columns,extension→ 101; two-table bulk read → 202. The three that pass before (fields=columnsruns zero queries, the paginated page already ran one, the transaction check) are regression guards.TableResourceIT(294 cases, includingcreateUpdateDelete_tableCustomMetrics_200andtest_getTableColumnsWithCustomMetrics_200) andColumnCustomPropertiesIT(65 cases): 364 run, 0 failures, 0 errors, 9 pre-existing skips.Ingestion integration tests
Not applicable (no ingestion changes).
Playwright (UI) tests
Not applicable (no UI changes).
Manual testing performed
None beyond the automated runs above; all evidence is from the integration tests listed.
UI screen recording / screenshots:
Not applicable.
Checklist:
Fixes <issue-number>: <short explanation>Fixes #<issue-number>above.