Skip to content

perf(voting): stop fetching proposal lists that are not on screen - #442

Open
yhabib wants to merge 1 commit into
perf/route-loaders-and-query-delayfrom
perf/gate-hidden-proposals-queries
Open

perf(voting): stop fetching proposal lists that are not on screen#442
yhabib wants to merge 1 commit into
perf/route-loaders-and-query-delayfrom
perf/gate-hidden-proposals-queries

Conversation

@yhabib

@yhabib yhabib commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Motivation

The voting page keeps its proposals list behind a "show proposals" toggle and only ever renders one filter, but it called useGovernanceProposals twice unconditionally. Both filters fetched on every visit, each as a query/update pair, so a default visit with the list collapsed spent four governance calls on a list nobody had asked to see.

This is the first piece of finding #3 from the perf audit. On nns.icp.net, /voting fired 8 concurrent certified update calls taking 4.2-4.9s each, while the same reads as plain queries came back in 26-379ms. Update latency gets worse fast with concurrency (three concurrent were ~1.4s each), so removing calls helps the ones left more than the count suggests.

Nothing loses certification here. The calls that remain are unchanged.

Changes

  • Added an enabled option to useGovernanceProposals.
  • Gated both proposals queries on the showProposals toggle and the active filter, so only the list actually on screen is fetched.
  • Updated the voting route loader to mirror those gates. A loader that warmed a list the component never asks for would put the calls straight back, so the two have to agree.
  • Added tests for both halves of that agreement: the loader leaves proposals alone while the list is collapsed, and warms exactly one filter when it is open.

Prev. #441

The voting page keeps its proposals list behind a "show proposals" toggle
and renders one filter at a time, but it called `useGovernanceProposals`
twice unconditionally. Both filters fetched on every visit, each as a
query/update pair, so a default visit — list collapsed — spent four
governance calls on a list nobody had asked to see.

Measured on nns.icp.net, /voting issued 8 concurrent update calls that
took 4.2-4.9s each; the same reads as plain queries came back in
26-379ms. Update-call latency degrades sharply with concurrency (three
concurrent were ~1.4s each), so dropping calls helps the ones that remain
more than the count alone suggests.

Both hooks now take an `enabled` gate keyed on the toggle and the active
filter, and the route loader mirrors it — a loader that warmed a list the
component never asks for would just put the calls straight back. Tests
cover both halves of that agreement.

Nothing loses certification here; the calls that remain are unchanged.
@zeropath-ai

zeropath-ai Bot commented Jul 31, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 37ba527.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► src/governance-app-frontend/src/common/hooks/governance/useGovernanceProposals.ts
    Extend hook to accept an enabled flag and gate by both readiness and enabled parameter
Enhancement ► src/governance-app-frontend/src/common/queries/routeLoaders.ts
    Add PrefetchedProposals type
    Update prefetchVotingRoute to use proposals param and gate loading when 'none' is requested
Enhancement ► src/governance-app-frontend/src/common/queries/routeLoaders.ts
    Modify prefetchVotingRoute logic to skip warming proposals when none is requested and to fetch only the appropriate open/all proposals
Enhancement ► src/governance-app-frontend/src/routes/_auth/voting/index.tsx
    Import PrefetchedProposals and prefetchVotingRoute with updated usage
    Add proposalsToPrefetch helper to determine prefetched state
    Update loaderDeps to include proposalFilter and showProposals
    Update loader to pass proposals instead of openProposalsOnly
    Preserve gating comments regarding prefetched data
Enhancement ► src/governance-app-frontend/src/routes/_auth/voting/index.tsx
    Refactor component to fetch only the needed proposals based on showProposals and proposalFilter (open/all)

@github-actions

Copy link
Copy Markdown

📊 Build Bundle Stats

The latest build generated the following assets:

dist/index.html                                           2.47 kB │ gzip:   0.85 kB
dist/assets/index-DSwNl_yb.css                          131.57 kB │ gzip:  21.37 kB
dist/assets/externalServices-CK4eoRia.js                  0.19 kB │ gzip:   0.16 kB
dist/assets/address-C-4H442a.js                           0.20 kB │ gzip:   0.15 kB
dist/assets/icpLedger-C8IVtQi5.js                         0.23 kB │ gzip:   0.21 kB
dist/assets/Skeleton-zAxeOFaI.js                          0.46 kB │ gzip:   0.34 kB
dist/assets/useTvlValue-rk_xN_iO.js                       0.57 kB │ gzip:   0.39 kB
dist/assets/numbers-DJtvedQs.js                           0.58 kB │ gzip:   0.36 kB
dist/assets/service-CffYLUtU.js                           0.59 kB │ gzip:   0.37 kB
dist/assets/Spinner-BbBi8NCG.js                           0.59 kB │ gzip:   0.41 kB
dist/assets/PageHeader-Czh9Jiub.js                        0.77 kB │ gzip:   0.45 kB
dist/assets/CertifiedBadge-BRMhiDXr.js                    0.81 kB │ gzip:   0.48 kB
dist/assets/Separator-nlFj1fYk.js                         0.83 kB │ gzip:   0.49 kB
dist/assets/useIcpIndex-BCnvOxYX.js                       1.07 kB │ gzip:   0.63 kB
dist/assets/rolldown-runtime-CMxvf4Kt.js                  1.21 kB │ gzip:   0.67 kB
dist/assets/EmptyActionState-C8K6Bev4.js                  1.61 kB │ gzip:   0.83 kB
dist/assets/addressBook-C8D7WWeQ.js                       1.66 kB │ gzip:   0.99 kB
dist/assets/useCommandPaletteSettings-lLPvnkO6.js         1.73 kB │ gzip:   0.86 kB
dist/assets/useGovernanceAppCanister-l5i_Y-7e.js          1.91 kB │ gzip:   0.96 kB
dist/assets/AnimatedNumber-DX_QBNwy.js                    1.96 kB │ gzip:   1.11 kB
dist/assets/useIcpIndexTransactionsPolling-CRqfkL7l.js    2.03 kB │ gzip:   1.09 kB
dist/assets/ToggleGroup-CfD82vQ_.js                       3.09 kB │ gzip:   1.41 kB
dist/assets/tickers-CrcBnHJF.js                           3.10 kB │ gzip:   1.47 kB
dist/assets/Switch-6NFSPaMn.js                            3.22 kB │ gzip:   1.44 kB
dist/assets/SensitiveValue-B-A62htb.js                    7.33 kB │ gzip:   3.28 kB
dist/assets/routes-B_Hecgfe.js                            7.46 kB │ gzip:   2.46 kB
dist/assets/badge-BZWlXd0w.js                             8.49 kB │ gzip:   2.57 kB
dist/assets/spamFilter-sOpsjjV1.js                        8.54 kB │ gzip:   3.81 kB
dist/assets/MutationDialog-DE-Sw7Ot.js                    9.53 kB │ gzip:   3.42 kB
dist/assets/events-sqr4BseR.js                           10.38 kB │ gzip:   4.11 kB
dist/assets/_id-BnuCrDPq.js                              14.33 kB │ gzip:   4.30 kB
dist/assets/_auth-BlSqoTSv.js                            15.60 kB │ gzip:   5.74 kB
dist/assets/accounts-BvUydq_Y.js                         23.72 kB │ gzip:   8.18 kB
dist/assets/GetTokens-48YShXUe.js                        41.92 kB │ gzip:  14.90 kB
dist/assets/settings-RsyJi5rn.js                         42.51 kB │ gzip:  12.20 kB
dist/assets/dashboard-DKRZQvpt.js                        50.18 kB │ gzip:  15.02 kB
dist/assets/button-ClHF0ndL.js                           58.23 kB │ gzip:  20.00 kB
dist/assets/neurons-BBuePgai.js                          67.94 kB │ gzip:  20.23 kB
dist/assets/voting-B7Zh7aUL.js                           69.39 kB │ gzip:  21.24 kB
dist/assets/vendor-md-CLh1GvDS.js                        73.29 kB │ gzip:  19.98 kB
dist/assets/neuronDetail-BlZvN90-.js                     77.03 kB │ gzip:  22.88 kB
dist/assets/index-BnWuEAyM.js                            80.39 kB │ gzip:  25.38 kB
dist/assets/vendor-tanstack-BztRJxNp.js                 161.18 kB │ gzip:  49.35 kB
dist/assets/vendor-core-react-qLCIanUQ.js               189.81 kB │ gzip:  59.76 kB
dist/assets/vendor-recharts-CC-WrRXd.js                 223.45 kB │ gzip:  64.00 kB
dist/assets/vendor-icp-DP4ARddz.js                      411.37 kB │ gzip: 104.08 kB
dist/assets/vendor-libs-C_IwxdbG.js                     554.14 kB │ gzip: 180.78 kB

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reduces unnecessary governance traffic on the voting page by ensuring proposal lists are only fetched/prefetched when the proposals section is actually visible and only for the active filter, aligning loader prefetch behavior with the component’s query enabled gates.

Changes:

  • Added an enabled option to useGovernanceProposals so callers can prevent proposal fetching entirely.
  • Updated the voting route + component to only fetch proposals when showProposals is on and only for the currently selected filter.
  • Updated voting route loader prefetching to mirror the UI gating, and added tests to enforce that agreement.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/governance-app-frontend/src/routes/_auth/voting/index.tsx Adds proposalsToPrefetch, includes showProposals in loader deps, and gates proposal queries so only visible data is fetched.
src/governance-app-frontend/src/common/queries/routeLoaders.ts Adds PrefetchedProposals and updates prefetchVotingRoute to optionally skip prefetching proposals entirely or prefetch only one filter.
src/governance-app-frontend/src/common/queries/routeLoaders.test.tsx Adds tests asserting proposal prefetching is skipped when collapsed and that exactly one filter is warmed when expanded.
src/governance-app-frontend/src/common/hooks/governance/useGovernanceProposals.ts Extends the hook API with an enabled option and wires it into the underlying query options.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants