Repository navigation
Search Router open: restore option cache hits, skip empty-query navigation work, profile on native - #103982
Draft
sumo-slonik wants to merge 4 commits into
Draft
Search Router open: restore option cache hits, skip empty-query navigation work, profile on native#103982sumo-slonik wants to merge 4 commits into
sumo-slonik wants to merge 4 commits into
Conversation
…ns for empty query
…s navigation span
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Explanation of Change
Work toward the
ManualOpenSearchRoutertarget (#79353), aimed at the Android tail. This PR does not reach the 400 ms target on its own, and its production effect is not proven yet. It removes work that is wasted on every open, and adds the profiling needed to find what fills the rest of the tail.Who is in the Android tail (Sentry, production, week of 2026-10-05): small accounts on slow devices. The median Android opener has 2 workspaces, ~15 reports and ~7 contacts. Of the 46 Android opens above 900 ms, only 2 came from an account with more than 9 workspaces; the rest are
low/mediumdevices with 1-4 workspaces and under ~35 reports. Onlowdevices with at most 50 reports, the time to the skeleton (ManualOpenSearchRouterPageVisible) is p50 357 ms of a p50 720 ms open, so both halves matter. The tail is mostly a fixed cost per open, not data size.1. The option list cache missed on every open since 9.4.89 (#99270).
useFilteredOptionsfeedspendingDeleteMemberAccountIDsByReportIDinto the reference-keyed cache increateFilteredOptionList(#95378). Its selector built a new{}for every subscriber (useOnyxmemoizes selectors per hook instance), and the Search Router mounts fresh on every open, so this input changed reference each time and the list was rebuilt on every open, which also madeSearchRouterWarmupuseless. The selector now returns one shared frozen object when nothing is pending. The miss follows from the code for every account, but the saved work grows with the number of reports, so for the median Android account it is small; it matters more for larger accounts.2. Navigation suggestions are no longer built for the empty query every open starts with.
useNavigationSuggestionsbuilt all its sources andbuildNavigationSuggestionsthen returned[]. Now the hook returns early with the same condition, anduseSearchTypeMenuSectionsgets a flag to skipcreateTypeMenuSections, which parses each canned query. Two parts:3. Hermes profiling of the open on native, to find what fills the small-account tail. No 9.4/9.5 build has produced profiles:
hermesProfilingIntegrationneedsprofilesSampleRate, and_experiments.profilingOptionsnever reaches the native SDK because ofautoInitializeNativeSdk: false. A global rate would cut every profile at the next root span (everyManual*span is a root since #101877), run the synchronous stop inside the next measured span, and flood the quota.searchRouterProfilingSamplerkeeps the rate at 0 except for the navigation span dispatched during a Search Router open (Android 20%, iOS 5%, JS profiler only) and stampsprofiledon the open span, so metric comparisons can exclude profiled opens with!tags[profiled,boolean]:true. A test pinsprofilesSampleRateout ofSentry.init, because the SDK would then add its own profiler ahead of the sampler. Not run on a device yet.Emulator measurement. Limits: dev build (absolute numbers are inflated), one emulator, and an atypical account with ~255 workspaces and ~1565 reports, so this shows the mechanisms work and does not predict the production gain. Warm opens, base and fix alternated in blocks of 4; the first open after each reload (a cache rebuild) is excluded from the fix rows, and adding those rows back changes the medians to 1362 and 2182 ms.
ManualOpenSearchRoutermedianManualOpenSearchRouterPageVisiblemedianHow the effect will be checked after release: old and new native versions run side by side, so compare Android p50/p95 by
releaseanddevice.class, restricted totags[reports_count_raw,number]:<=50for the small-account tail, with profiled opens excluded.Fixed Issues
$ #79353
PROPOSAL:
Tests
go toand verify navigation suggestions (top-level destinations, Spend, workspace pages, Account) still appear.Offline tests
go toand verify navigation suggestions appear.QA Steps
Same as tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari