aws / aws/graph-explorer

Schema view lockup from per-type Cytoscape style selectors in useGraphStyles

Open
#2,104 0 comments 0 reactions 1 assignee Claimed by @kmcginnes View on GitHub
internal performance schema
Dominant language
TypeScript
Stars
481
Forks
108
Avg merge
6d 8h
Merged PRs (30d)
5

Description

## Symptom

With very large label sets (repro: ~10k node labels + ~10k edge labels), the schema view pins the main thread and never renders — even after the icon fan-out fix (#2103). A performance trace attributes 87.6% self-time to Cytoscape's style engine (`getPropertiesDiff` + `getContextStyle`); the layout is never reached (0.3 ms).

## Root cause

`createGraphStyles` (`useGraphStyles.ts`) emits one selector per type — `node[type="…"]` and `edge[type="…"]` — so ~20k style contexts. Cytoscape resolves each element's style by scanning all contexts, making style application O(elements × contexts) ≈ 4×10⁸. Compounding it, `useManageStyles`'s effect is keyed on a `styles` object with fresh identity every render, so the multi-second recompute can re-fire and never settle.

## Evidence

Headless benchmark, per-type selectors vs. a single `data()` mapper rule, forcing per-element resolution:

| n (each of node + edge types) | contexts | resolve |
|---|---|---|
| 1,000 | 2,002 | 437 ms |
| 3,000 | 6,002 | 3,622 ms |
| 10,000 | 20,002 | ~100 min |
| 10,000 *(data-mapper)* | **2** | **324 ms** |

## Fix

Two independently shippable phases:

1. **Stabilize the effect** — memoize the `styles` object identity so `useManageStyles` applies once instead of re-firing the recompute.
2. **Data-mapper conversion** — precompute each element's resolved style values onto `ele.data()` (`ge_*`) at the element-enrichment seam and emit one `node` rule + one `edge` rule using `data(...)`, mirroring the existing `node[__iconUrl]` mapper. Contexts drop ~20k → ~2; `cy.style()` becomes O(elements). Extends #1725 (which validates the architecture for `background-color`) to all per-type properties.

Per-element logic that must move out of the style loop into precompute: the `isDark()` label text-color pick, the `line-dash-pattern`/`lineStyle` remap, and shape coercion (ADR `20260710-coerce-retired-round-polygon-shapes`).

Regression guard: assert style-context count stays O(1) regardless of type count.

## Related

Umbrella #2091 · caused-with #2103 · architecture #1725 · epic #1677 · spike #1797

> [!IMPORTANT]
> Internal only — this issue is maintained by the core team and is not accepting external contributions.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.