Skip to content
Open
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
7251661
feat(ui-perf): If-None-Match → 304 short-circuit + client ETag cache
harshach May 10, 2026
aed45a1
feat(ui-perf): defer Queries-tab fetch until tab activated
harshach May 10, 2026
101a972
feat(ui-perf): lazy-mount below-fold widgets on landing page
harshach May 10, 2026
f6654fc
fix(ui-perf): address PR review feedback for P1 perceived-latency
harshach May 10, 2026
5ab5276
test(ui-perf): unblock tests against DeferredWidget on landing page
harshach May 10, 2026
3782a0c
test(ui): force single React copy for core-components in jest
harshach May 10, 2026
c515580
revert(ui-perf): drop P1.3 DeferredWidget — broke Playwright shards
harshach May 11, 2026
d825f3d
fix(ui-perf): drop Cache-Control on ETag responses — broke DataContra…
harshach May 17, 2026
4775d71
Merge branch 'main' into harshach/perceived-latency-p1
harshach May 17, 2026
45c9730
fix(ui-perf): invalidate in-memory ETag cache on any mutation response
harshach May 17, 2026
3f03ee1
Merge branch 'main' into harshach/perceived-latency-p1
harshach May 17, 2026
94e5062
fix(ui-perf): address PR review on ETag cache (clone-on-write + 304 r…
harshach May 18, 2026
a9efde0
fix(ui-perf): emit Cache-Control: no-store on ETag responses
harshach May 18, 2026
83085e4
fix(ui-perf): don't re-insert snapshot fallback into the ETag cache
harshach May 21, 2026
25d79b0
feat(ui-perf): defer Activity Feed activity-count fetch across all en…
harshach May 21, 2026
f09f040
feat(perf): cheap per-entity activity count via limit=0; re-eager Act…
harshach May 21, 2026
7f40077
feat(ui-perf): lazy-mount below-fold landing-page widgets (re-attempt)
harshach May 21, 2026
7577c56
perf(ui): dedup landing-page announcements fetch
harshach May 21, 2026
5de535d
feat(ui-perf): wire @tanstack/react-query at the app root
harshach May 21, 2026
a6b17a4
feat(ui-perf): migrate TableDetailsPageV1 to useQuery + optimistic fo…
harshach May 22, 2026
ff9cdbf
feat(ui-perf): prefetch Table details on hover from search result cards
harshach May 22, 2026
9a76d5c
feat(ui-perf): migrate DashboardDetailsPage to useQuery + optimistic …
harshach May 22, 2026
afbfd4b
feat(ui-perf): migrate PipelineDetailsPage to useQuery + optimistic f…
harshach May 22, 2026
6152d8f
feat(ui-perf): migrate TopicDetailsPage to useQuery + optimistic follow
harshach May 22, 2026
f5bac95
feat(ui-perf): prefetch Dashboard/Pipeline/Topic on hover too
harshach May 22, 2026
c9942c2
fix(ui-perf): break dashboardQuery/pipelineQuery → utils circular import
harshach May 22, 2026
5d26539
chore(ui): add preview-server proxy config so yarn preview works locally
harshach May 22, 2026
1c7db21
feat(perf): branch Cache-Control by path on the asset servlet
harshach May 22, 2026
1ed5c1f
feat(perf): emit strong ETag on the SPA shell and 304 on If-None-Match
harshach May 22, 2026
338a5e6
chore(ui): disable modulepreload polyfill for modern-browser targets
harshach May 22, 2026
6780722
feat(perf): inline a CSS-only loading shell in index.html
harshach May 22, 2026
7bb952a
feat(perf): make HTTP/2 listener available via SERVER_PROTOCOL=h2c
harshach May 22, 2026
325f494
feat(perf): cache-first for hashed assets in the existing service worker
harshach May 22, 2026
5e4d085
docs(perf): CDN deployment guide for per-customer isolated clusters
harshach May 22, 2026
8f6fb85
fix(perf): emit ETag based on CSP policy, not nonce attribute presence
harshach May 22, 2026
ab69e6e
docs(perf): CDN guide for per-customer release pinning, no extra data…
harshach May 22, 2026
c00a21e
Merge branch 'main' into harshach/perceived-latency-p1
harshach May 22, 2026
ca0d223
feat(ui-perf): split heavy specialist libs into their own chunks, tar…
harshach May 22, 2026
04262ac
docs(perf): bundle-size follow-up plan with measurements + per-PR ROI
harshach May 22, 2026
96e5f2f
feat(ui-perf): lazy-load FeedEditor (Quill) inside ActivityFeedEditor
harshach May 22, 2026
ce0a94b
chore(ui-perf): mark react-data-grid type-only imports as `import type`
harshach May 22, 2026
5efeaff
feat(ui-perf): per-scope vendor chunking for fine-grained cache inval…
harshach May 22, 2026
5813998
chore(ui): organize-imports order fix on ActivityFeedEditor
harshach May 22, 2026
916f198
test(ui): align entity-page jest mocks with deferred feed-count helpers
harshach May 22, 2026
04494c7
fix(ui-perf): render DeferredWidget eagerly under headless automation
harshach May 22, 2026
1131cb5
fix(ui-perf): re-bind Table state writers when cache key shifts on pe…
harshach May 22, 2026
d2e5fd0
fix(ui-perf): show ErrorPlaceHolder on Table 404; revert FeedEditor lazy
harshach May 22, 2026
ab45e76
chore(ui-checkstyle): prettier reflow on DirectoryDetails.test mock
harshach May 22, 2026
4d2532e
fix(sonar): exclude src/test/** from main-source indexing
harshach May 22, 2026
e0a0fa7
feat(ui-perf): variable Inter font + dark-mode splash restore
harshach May 22, 2026
77f1968
chore(ui): full Apache 2.0 header on inter-variable.css
harshach May 23, 2026
7bd1892
feat(ui-perf): idle route prefetch + lazy Tour + drop eager image pre…
harshach May 23, 2026
a76a1ea
feat(ui-perf): migrate 16 entity detail pages to React Query
harshach May 23, 2026
050d5f7
perf(ui): lazy-load TasksDAGView to split DAG chunk off entry path
harshach May 23, 2026
51288b8
fix(ui-perf): dedupe inflight permission fetches in PermissionProvider
harshach May 23, 2026
55be615
feat(ui-perf): bundle-size follow-up — 6 lazy-load PRs in one commit
harshach May 23, 2026
9834fad
chore(ui-checkstyle): prettier reflow on service-utility schema loaders
harshach May 23, 2026
9a1c80d
test(ui): await React.lazy() settle in AppTour test
harshach May 23, 2026
64901a4
fix(ui-perf): staleTime=0 (stale-while-revalidate) to restore Playwri…
harshach May 23, 2026
dd1aad6
chore(docs): drop bundle-size-followup.md — all PRs landed
harshach May 23, 2026
4cd8737
perf(ui-perf): lazy-fetch entity `extension` via Custom Properties tab
harshach May 23, 2026
1368c49
fix(ui-perf): observe LazyDataGrid mount before focusing first cell
harshach May 23, 2026
e44fd8a
fix(ui-perf): break Container column deep-link permission loop
harshach May 23, 2026
2297b0b
fix(ui-perf): walk up to parent container when column-FQN 404s
harshach May 23, 2026
98c9eb1
Revert "perf(ui-perf): lazy-fetch entity `extension` via Custom Prope…
harshach May 24, 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
19 changes: 18 additions & 1 deletion conf/openmetadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,24 @@ server:
applicationContextPath: ${BASE_PATH:-/}
rootPath: /api/*
applicationConnectors:
- type: http
#
# Connector protocol. Two supported values:
#
# - http (default): HTTP/1.1 only. Compatible with every load balancer and proxy
# ever shipped. Pick this if you front Jetty with an HTTP/2-terminating LB
# (AWS ALB / CloudFront / nginx / Caddy) — those speak HTTP/2 to the browser
# but downgrade to HTTP/1.1 on the upstream hop anyway, so HTTP/2 on Jetty
# buys nothing.
#
# - h2c HTTP/2 cleartext on the same TCP port. Jetty 12 accepts both h1 clients
# (via h1 protocol) and h2 clients (via h2c-upgrade or h2c-prior-knowledge),
# so this is backwards-compatible. Worth flipping on when browsers hit Jetty
# directly (Docker Compose, on-prem single-node, local dev) — multiplexing
# removes the h1 6-connections-per-origin cap, which materially helps cold
# first-paint on SPAs that load 30+ chunks.
#
# Requires the dropwizard-http2 module (already pulled in by openmetadata-service).
- type: ${SERVER_PROTOCOL:-http}
bindHost: ${SERVER_HOST:-0.0.0.0}
port: ${SERVER_PORT:-8585}

Expand Down
177 changes: 177 additions & 0 deletions docs/perf/bundle-size-followup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Bundle-size follow-up plan

Companion to the perceived-latency PR. The wins already shipped target *delivery*
(compression, caching, prefetch). The wins below target *bundle size* — the bytes that
ever reach the browser regardless of how well we cache them.

## Baseline measurements

Run on `harshach/perceived-latency-p1` at commit `ca0d22304d` (after chunk-splitting), via
`yarn build --mode analyze` — full artifact at `dist/bundle-stats.{html,json}`.

| Chunk (after split) | Raw | Brotli | Loaded eagerly? |
|---|---|---|---|
| `AsyncDeleteProvider` (shared) | 4.9 MB | 851 KB | Yes (via NavBar) |
| `vendor-antd` | 2.1 MB | 484 KB | Yes (modulepreload) |
| `vendor-elkjs` | 1.4 MB | 327 KB | No (lineage only) |
| `preset` (`@antv/g6` + `html2canvas`) | 1.4 MB | 320 KB | No (Ontology/KnowledgeGraph) |
| `vendor-recharts` | 398 KB | 85 KB | **Yes** (should be lazy) |
| `vendor-quill` | 400 KB | 90 KB | **Yes** (should be lazy) |
| `vendor-prosemirror` | 397 KB | 108 KB | No |
| `vendor-codemirror` | 277 KB | 80 KB | No |
| `vendor-reactflow` | ~130 KB | ~50 KB | **Yes** (should be lazy) |
| `vendor-datagrid` | ~80 KB | ~32 KB | **Yes** (should be lazy) |
| `vendor-latex` | 262 KB | 62 KB | No |
| `vendor-logviewer` | ~110 KB | ~40 KB | No |

The four marked **Yes / should be lazy** are the highest-ROI gaps. Vite emits a
`<link rel="modulepreload">` tag for each because there's a static-import path from the
entry chunk to the lib. Each one is the same shape of fix — find the static-import path,
move it behind a `React.lazy` or dynamic `import()`.

## Remaining contributors in the `AsyncDeleteProvider` chunk

| Module | Raw | Notes |
|---|---|---|
| `src/jsons/ingestionSchemas/testSuitePipeline.json` | 504 KB | Connection schema imported eagerly |
| `react-dom/server` (CJS + browser builds) | 421 KB | Legitimate — `renderToString` for diff/icon HTML. **Keep.** |
| `src/constants/mockTourData.constants.ts` | 113 KB | Product-tour mock data — only used when `isTourOpen` |
| `src/generated/antlr/EntityLinkLexer.js` | 69 KB | ANTLR-generated lexer for entity-link parsing |
| `src/components/DataContract/ODCSImportModal` | 67 KB | Modal — should be lazy on modal open |
| `src/jsons/connectionSchemas/.../airflowConnection.json` | 65 KB | One of N connection schemas |
| `cronstrue` | 54 KB | Cron-expression formatter — scheduler views only |
| `focus-trap` | 51 KB | Modal focus management |
| Multiple other connection-schema JSONs | ~500 KB total | One per data service |

## Follow-up PRs (ordered by ROI per effort)

### PR-1: Lazy-load Lineage + Workflow Builder (1 day)

Statically-imported files that pull `reactflow` into the entry:
- `src/utils/EntityLineageUtils.tsx`
- `src/utils/NodeUtils.ts`
- `src/utils/ViewportUtils.ts`
- `src/utils/EdgeStyleUtils.ts`
- `src/utils/WorkflowSerializer.ts`
- `src/utils/CanvasUtils.ts`
- `src/utils/EdgeMidpointUtils.ts`
- `src/hooks/useWorkflowLogic.ts`
- `src/hooks/useCanvasEdgeRenderer.ts`
- `src/hooks/useMapBasedNodesEdges.ts`
- `src/hooks/useWorkflowActions.ts`
- `src/context/LineageProvider/LineageProvider.tsx`

`EntityLineageTab.tsx` is *already* dynamic-imported in 10+ entity-utils files, so the
tab itself is lazy. The leak comes from these util/hook modules being statically imported
by code that runs at app boot.

**Fix shape**: convert the static `import` of `reactflow` types to `import type` (erased
at compile-time, zero runtime cost), and split runtime usage out into one file that's
only loaded when the lineage canvas mounts. Most of the reactflow imports in these utils
are actually `import { Edge, Node }` for *types* — those can become `import type` immediately
without behaviour change.

Expected win: `vendor-reactflow` drops out of modulepreload. Cold first paint loses ~50 KB
brotli + the chunk-discovery round-trip.

### PR-2: Lazy-load chart components (4 hours)

`recharts` is statically imported by ~10 chart components. The components themselves can
stay statically imported in their parent files, but each component should be wrapped in
`React.lazy` at its consumption site (the dashboard widget, the profiler page, etc.).

**Expected win**: `vendor-recharts` drops out of modulepreload. ~85 KB brotli saved on first
paint for users who don't immediately hit a chart view.

### PR-3: Lazy-load FeedEditor (Quill) (4 hours)

`FeedEditor.tsx` statically imports `quill` and is rendered in the activity-feed widget on
`/my-data`. Wrap `FeedEditor` in `React.lazy` at the widget level and gate the eager mount
behind user interaction (clicking the "comment" trigger).

**Expected win**: `vendor-quill` drops out of modulepreload. ~90 KB brotli saved.

### PR-4: Lazy-load mockTourData (4 hours)

Five consumers (`TableDetailsPageV1`, `DashboardDetailsPage`, etc.) statically import
`mockDatasetData` from `mockTourData.constants.ts`. Each use is gated on `isTourOpen`,
so the static import is pure waste in the 99% non-tour case.

Pattern: replace the static import with an async loader cached at module level. The
consumer becomes:

```ts
const [tourData, setTourData] = useState(null);
useEffect(() => {
if (!isTourOpen) return;
import('../../constants/mockTourData.constants').then(m => setTourData(m.mockDatasetData));
}, [isTourOpen]);
```

**Expected win**: 113 KB raw / ~25 KB brotli removed from the always-loaded
`AsyncDeleteProvider` chunk.

### PR-5: Lazy-load `react-data-grid` (2 hours)

Used by `BulkImportVersionSummary` and CSV utility code. Wrap the consuming components in
`React.lazy`.

**Expected win**: `vendor-datagrid` drops out of modulepreload. ~32 KB brotli saved.

### PR-6: Lazy connection schemas (1-2 days)

The connection-schema JSONs (`testSuitePipeline.json`, `airflowConnection.json`,
`hiveConnection.json`, ...) total ~620 KB raw in the AsyncDeleteProvider chunk. They're
imported via a registry that maps service-type → schema.

Refactor: change the registry from `{ snowflake: snowflakeSchema, ... }` (static imports)
to `{ snowflake: () => import('./snowflake.json'), ... }` (dynamic). Resolve the schema
only when the connection form mounts for that service type.

**Expected win**: ~150 KB brotli removed from `AsyncDeleteProvider`. Each service's schema
loads only when its form is opened — typical user touches 1-2 schemas per session.

### PR-7: Lazy-load `cronstrue` (2 hours)

Used in `DateTimeUtils` + 4 settings/scheduler views. Convert to async loader at the call
site.

**Expected win**: ~15 KB brotli removed.

## Other knobs worth measuring

### Rolldown

Vite 7 has experimental Rolldown support via the `rolldown-vite` package. Rolldown is
Rollup's Rust-based replacement; same config, faster builds, somewhat smaller output. Worth
swapping in once Rolldown reaches GA. Spec'd by Vite team as "drop-in" but worth a
dedicated soak.

### `vite-imagetools` for image conversion

Many of the in-repo PNGs (governance.png, data-collaboration.png, the various favicons)
could ship as `.webp` or `.avif` for 30-50% size reduction. The `vite-imagetools` plugin
applies the conversion at build time.

### Drop duplicate icon libraries

We use `@ant-design/icons`, `@untitledui/icons`, AND various inline SVGs. A consolidation
audit might find substantial duplication.

### Antd subset

Antd 5 supports per-component imports (e.g., `import { Button } from 'antd/es/button'`).
The current code does barrel imports (`import { Button, Modal } from 'antd'`) which tree-shake
*okay* in modern bundlers but not perfectly. Direct paths would shave ~10-20% off
`vendor-antd`.

## Measurement protocol for each follow-up PR

1. Baseline: `yarn build --mode analyze`, save `dist/bundle-stats.json` to `docs/perf/`.
2. Apply change.
3. Re-run analyzer, diff the two JSONs.
4. Commit message includes the numeric delta: before/after raw KB, before/after brotli KB,
chunks removed from modulepreload list.

This makes regressions detectable: a future PR that grows the bundle by 100 KB shows up as
a numeric delta in CI, not a vague "feels slower."
Loading
Loading