Skip to content

perf(ui): single-commit GenericProvider column registration (R1) - #31023

Open
harsh-vador wants to merge 1 commit into
mainfrom
perf/generic-provider-single-commit
Open

perf(ui): single-commit GenericProvider column registration (R1)#31023
harsh-vador wants to merge 1 commit into
mainfrom
perf/generic-provider-single-commit

Conversation

@harsh-vador

Copy link
Copy Markdown
Contributor

Problem

Nine entity-detail tabs (SchemaTable, ModelTab, SearchIndexFieldsTable, TopicSchema, ContainerDataModel, PipelineTaskTab, MlModelFeaturesList, WorksheetColumnsTable, APIEndpointSchema) push their sorted/filtered/paginated/searched column list into GenericProvider via a useEffect + useState setter. A setter fired from a child effect re-renders the provider after the child commits, so every entity detail page double-renders (2 commits) on load.

Fix

displayedColumns' only consumer is columnsForPanelColumnDetailPanel's allColumns, which is mounted only when a column is selected (a user action). So the value is never needed on the initial, panel-closed render.

  • Store the child registration in a useRef (displayedColumnsRef) — the write no longer re-renders the provider.
  • setDisplayedColumns is now a stable useCallback; it mirrors into new panelColumns state only while the panel is open (selectedColumnRef gate).
  • openColumnDetailPanel snapshots the ref at click-time; columnsForPanel reads panelColumns.

Result: load path is one commit; prev/next navigation over the displayed list is byte-for-byte unchanged. No child changes, no context-interface change.

Testing

  • Jest: GenericProvider + all 6 child specs + ColumnDetailPanel10 suites / 127 tests pass, unmodified.
  • tsc --noEmit: no new errors (2 pre-existing WidgetConfig[] layout errors on main are untouched).
  • lint:base clean.
  • React DevTools Profiler on a Table detail tab load: 2 commits → 1 (provider absent from the 2nd commit).

Part of the UI Quality Audit epic — open-metadata/openmetadata-collate#5442, ticket open-metadata/openmetadata-collate#5446 (R1).

🤖 Generated with Claude Code

Nine entity detail tabs push their sorted/filtered/paginated column list
into GenericProvider via a useEffect + useState setter. A setter fired from
a child effect re-renders the provider after the child commits, so every
entity page double-renders on load.

The value's only consumer is ColumnDetailPanel's `allColumns`, which is
mounted only when a column is selected (user action). Store the child
registration in a ref (no re-render), and mirror it into `panelColumns`
state only while the panel is open, snapshotting at open time. Load path
is now one commit; prev/next navigation over the displayed list is
unchanged. No child or context-interface changes.

Refs #5446

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@harsh-vador
harsh-vador requested a review from a team as a code owner August 5, 2026 11:35
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions github-actions Bot added the UI UI specific issues label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

✅ Playwright Results — workflow succeeded

Validated commit 1f3cf474e5194159e3b2cf9717777183aa65d8ab in Playwright run 31003936593, attempt 1.

✅ 550 passed · ❌ 0 failed · 🟡 0 flaky · ⏭️ 0 skipped · 🧰 0 lifecycle flaky

Performance

Blocking targets: ✅ met · Optimization targets: 🟡 in progress

Shard-job maxima below are not the full workflow wall time; the linked run includes build, fixture, planning, and reporting.

🕒 Full workflow signal wall (to summary) 1h 3m 32s

⏱️ Max setup 2m 58s · max shard execution 18m 53s · max shard-job elapsed before upload 22m 22s · reporting 4s

🌐 203.62 requests/attempt · 2.83 app boots/UI scenario · 10.86% common-shard skew

Optimization targets still in progress:

  • Browser traffic was 203.62 requests per attempt (convergence target: fewer than 200).
  • Application boot ratio was 2.83 per UI scenario (1617 boots / 571 scenarios; convergence target: at most 1).
Shard Passed Failed Flaky Skipped Lifecycle failed Lifecycle flaky
✅ Shard chromium-01 137 0 0 0 0 0
✅ Shard chromium-02 126 0 0 0 0 0
✅ Shard chromium-03 136 0 0 0 0 0
✅ Shard data-asset-rules-01 61 0 0 0 0 0
✅ Shard domain-isolation-01 14 0 0 0 0 0
✅ Shard global-state-01 34 0 0 0 0 0
✅ Shard ingestion-01 1 0 0 0 0 0
✅ Shard reindex-01 2 0 0 0 0 0
✅ Shard search-01 10 0 0 0 0 0
✅ Shard search-rbac-01 29 0 0 0 0 0

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Comment on lines +106 to +112
const setDisplayedColumns = useCallback((cols: ColumnOrTask[]) => {
displayedColumnsRef.current = cols;
// Keep the open panel's navigation list live; when closed, skip the re-render.
if (selectedColumnRef.current) {
setPanelColumns(cols);
}
}, []);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Edge Case: Deep-linked column panel loses child's sorted/paginated navigation list

On the columnFqn deep-link path (GenericProvider.tsx:156-164), setSelectedColumn runs but panelColumns is never seeded. Because children register their displayed list via setDisplayedColumns during their mount effect — which fires before the provider's selectedColumnRef.current = selectedColumn effect (line 120-122) commits — the gate at line 109 is still null, so setPanelColumns is skipped and the ref-write is dropped. As a result columnsForPanel falls back to extractedColumns, so prev/next navigation in a deep-linked/shared-URL column panel follows the unsorted, non-paginated order instead of the child's displayed list (previously preserved because displayedColumns was React state). The click-to-open path is unaffected because it snapshots the ref directly. Fix: seed panelColumns when selecting a column on the deep-link path.

Snapshot the child's displayed list into panelColumns when opening the panel via deep link, mirroring openColumnDetailPanel.:

// Sync selected column from prop (deep link)
useEffect(() => {
  // If we have a direct columnFqn from props, try to find and select it
  if (columnFqn && extractedColumns.length > 0) {
    const col = findFieldByFQN(extractedColumns as Column[], columnFqn);
    if (col) {
      setPanelColumns(
        displayedColumnsRef.current.length > 0
          ? displayedColumnsRef.current
          : extractedColumns
      );
      setSelectedColumn(cleanColumn(col));
    }
  }
}, [extractedColumns, columnFqn, cleanColumn]);
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 resolved / 1 findings

Optimizes GenericProvider column registration using a ref to eliminate double-renders on load, but deep-linked column panels lose the child's sorted and paginated navigation list on the columnFqn path.

⚠️ Edge Case: Deep-linked column panel loses child's sorted/paginated navigation list

📄 openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:106-112 📄 openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:120-122 📄 openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:142-144 📄 openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:156-164

On the columnFqn deep-link path (GenericProvider.tsx:156-164), setSelectedColumn runs but panelColumns is never seeded. Because children register their displayed list via setDisplayedColumns during their mount effect — which fires before the provider's selectedColumnRef.current = selectedColumn effect (line 120-122) commits — the gate at line 109 is still null, so setPanelColumns is skipped and the ref-write is dropped. As a result columnsForPanel falls back to extractedColumns, so prev/next navigation in a deep-linked/shared-URL column panel follows the unsorted, non-paginated order instead of the child's displayed list (previously preserved because displayedColumns was React state). The click-to-open path is unaffected because it snapshots the ref directly. Fix: seed panelColumns when selecting a column on the deep-link path.

Snapshot the child's displayed list into panelColumns when opening the panel via deep link, mirroring openColumnDetailPanel.
// Sync selected column from prop (deep link)
useEffect(() => {
  // If we have a direct columnFqn from props, try to find and select it
  if (columnFqn && extractedColumns.length > 0) {
    const col = findFieldByFQN(extractedColumns as Column[], columnFqn);
    if (col) {
      setPanelColumns(
        displayedColumnsRef.current.length > 0
          ? displayedColumnsRef.current
          : extractedColumns
      );
      setSelectedColumn(cleanColumn(col));
    }
  }
}, [extractedColumns, columnFqn, cleanColumn]);
🤖 Prompt for agents
Code Review: Optimizes GenericProvider column registration using a ref to eliminate double-renders on load, but deep-linked column panels lose the child's sorted and paginated navigation list on the columnFqn path.

1. ⚠️ Edge Case: Deep-linked column panel loses child's sorted/paginated navigation list
   Files: openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:106-112, openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:120-122, openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:142-144, openmetadata-ui/src/main/resources/ui/src/components/Customization/GenericProvider/GenericProvider.tsx:156-164

   On the `columnFqn` deep-link path (GenericProvider.tsx:156-164), `setSelectedColumn` runs but `panelColumns` is never seeded. Because children register their displayed list via `setDisplayedColumns` during their mount effect — which fires before the provider's `selectedColumnRef.current = selectedColumn` effect (line 120-122) commits — the gate at line 109 is still `null`, so `setPanelColumns` is skipped and the ref-write is dropped. As a result `columnsForPanel` falls back to `extractedColumns`, so prev/next navigation in a deep-linked/shared-URL column panel follows the unsorted, non-paginated order instead of the child's displayed list (previously preserved because `displayedColumns` was React state). The click-to-open path is unaffected because it snapshots the ref directly. Fix: seed `panelColumns` when selecting a column on the deep-link path.

   Fix (Snapshot the child's displayed list into panelColumns when opening the panel via deep link, mirroring openColumnDetailPanel.):
   // Sync selected column from prop (deep link)
   useEffect(() => {
     // If we have a direct columnFqn from props, try to find and select it
     if (columnFqn && extractedColumns.length > 0) {
       const col = findFieldByFQN(extractedColumns as Column[], columnFqn);
       if (col) {
         setPanelColumns(
           displayedColumnsRef.current.length > 0
             ? displayedColumnsRef.current
             : extractedColumns
         );
         setSelectedColumn(cleanColumn(col));
       }
     }
   }, [extractedColumns, columnFqn, cleanColumn]);

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar | Powered by Gitar — free for open source

@harsh-vador harsh-vador self-assigned this Aug 5, 2026
@harsh-vador harsh-vador added the safe to test Add this label to run secure Github workflows on PRs label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 66%
66.14% (77968/117883) 50.1% (47075/93960) 51.29% (14155/27597)

@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant