Skip to content

Commit 9a89d49

Browse files
mountinycursoragent
andcommitted
Perf: Add diagnostic sub-spans to ManualOpenSearchRouter
Add 4 sub-spans and 2 attributes to the ManualOpenSearchRouter Sentry span to identify which phases of the search router opening are bottlenecks. Sub-spans: - SearchRouter.ModalCloseWait: Modal.close() callback latency - SearchRouter.OptionsInit: Cold-path createOptionList() cost (guarded) - SearchRouter.ComputeOptions: JS computation in SearchAutocompleteList (guarded) - SearchRouter.ListRender: FlashList rendering + native layout Attributes: - cold_start: whether options needed initialization - trigger: 'button' or 'keyboard' Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 43e46a7 commit 9a89d49

File tree

6 files changed

+56
-2
lines changed

6 files changed

+56
-2
lines changed

src/CONST/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,10 @@ const CONST = {
18111811
SPAN_OD_ND_TRANSITION: 'ManualOdNdTransition',
18121812
SPAN_OD_ND_TRANSITION_LOGGED_OUT: 'ManualOdNdTransitionLoggedOut',
18131813
SPAN_OPEN_SEARCH_ROUTER: 'ManualOpenSearchRouter',
1814+
SPAN_SEARCH_ROUTER_MODAL_CLOSE_WAIT: 'SearchRouter.ModalCloseWait',
1815+
SPAN_SEARCH_ROUTER_OPTIONS_INIT: 'SearchRouter.OptionsInit',
1816+
SPAN_SEARCH_ROUTER_COMPUTE_OPTIONS: 'SearchRouter.ComputeOptions',
1817+
SPAN_SEARCH_ROUTER_LIST_RENDER: 'SearchRouter.ListRender',
18141818
SPAN_OPEN_CREATE_EXPENSE: 'ManualOpenCreateExpense',
18151819
SPAN_SUBMIT_EXPENSE: 'ManualCreateExpenseSubmit',
18161820
SPAN_NAVIGATE_AFTER_EXPENSE_CREATE: 'ManualCreateExpenseNavigation',

src/components/OptionListContextProvider.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap';
77
import {createOptionFromReport, createOptionList, processReport, shallowOptionsListCompare} from '@libs/OptionsListUtils';
88
import type {OptionList, SearchOption} from '@libs/OptionsListUtils';
99
import {isSelfDM} from '@libs/ReportUtils';
10+
import {endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans';
11+
import CONST from '@src/CONST';
1012
import ONYXKEYS from '@src/ONYXKEYS';
1113
import type {PersonalDetails, Report} from '@src/types/onyx';
1214
import {usePersonalDetails} from './OnyxListItemProvider';
@@ -253,8 +255,18 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) {
253255
}, [personalDetails]);
254256

255257
const initializeOptions = useCallback(() => {
258+
const isSearchRouterSpanActive = !!getSpan(CONST.TELEMETRY.SPAN_OPEN_SEARCH_ROUTER);
259+
if (isSearchRouterSpanActive) {
260+
startSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_OPTIONS_INIT, {
261+
name: CONST.TELEMETRY.SPAN_SEARCH_ROUTER_OPTIONS_INIT,
262+
op: 'function',
263+
});
264+
}
256265
loadOptions();
257266
areOptionsInitialized.current = true;
267+
if (isSearchRouterSpanActive) {
268+
endSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_OPTIONS_INIT);
269+
}
258270
}, [loadOptions]);
259271

260272
const resetOptions = useCallback(() => {

src/components/Search/SearchAutocompleteList.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
import {buildSearchQueryJSON, buildUserReadableQueryString, getQueryWithoutFilters, getUserFriendlyKey, getUserFriendlyValue, shouldHighlight} from '@libs/SearchQueryUtils';
4646
import {getDatePresets, getHasOptions} from '@libs/SearchUIUtils';
4747
import StringUtils from '@libs/StringUtils';
48-
import {endSpan} from '@libs/telemetry/activeSpans';
48+
import {endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans';
4949
import CONST, {CONTINUATION_DETECTION_SEARCH_FILTER_KEYS} from '@src/CONST';
5050
import ONYXKEYS from '@src/ONYXKEYS';
5151
import type {CardFeeds, CardList, PersonalDetailsList, Policy, Report} from '@src/types/onyx';
@@ -184,6 +184,16 @@ function SearchAutocompleteList({
184184
const expensifyIcons = useMemoizedLazyExpensifyIcons(['History', 'MagnifyingGlass']);
185185

186186
const {options, areOptionsInitialized} = useOptionsList();
187+
188+
const computeSpanStarted = useRef(false);
189+
if (!computeSpanStarted.current && getSpan(CONST.TELEMETRY.SPAN_OPEN_SEARCH_ROUTER)) {
190+
startSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_COMPUTE_OPTIONS, {
191+
name: CONST.TELEMETRY.SPAN_SEARCH_ROUTER_COMPUTE_OPTIONS,
192+
op: 'function',
193+
});
194+
computeSpanStarted.current = true;
195+
}
196+
187197
const searchOptions = (() => {
188198
if (!areOptionsInitialized) {
189199
return defaultListOptions;
@@ -901,6 +911,14 @@ function SearchAutocompleteList({
901911
}
902912
}, [autocompleteQueryValue, onHighlightFirstItem, normalizedReferenceText]);
903913

914+
if (isInitialRender && computeSpanStarted.current) {
915+
endSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_COMPUTE_OPTIONS);
916+
startSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_LIST_RENDER, {
917+
name: CONST.TELEMETRY.SPAN_SEARCH_ROUTER_LIST_RENDER,
918+
op: 'ui.render',
919+
});
920+
}
921+
904922
return (
905923
<SelectionListWithSections<AutocompleteListItem>
906924
showLoadingPlaceholder
@@ -922,6 +940,7 @@ function SearchAutocompleteList({
922940
disableKeyboardShortcuts={!shouldSubscribeToArrowKeyEvents}
923941
addBottomSafeAreaPadding
924942
onLayout={() => {
943+
endSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_LIST_RENDER);
925944
setPerformanceTimersEnd();
926945
setIsInitialRender(false);
927946
innerListRef.current?.updateExternalTextInputFocus(textInputRef?.current?.isFocused() ?? false);

src/components/Search/SearchRouter/SearchButton.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ function SearchButton({style, shouldUseAutoHitSlop = false}: SearchButtonProps)
3434
startSpan(CONST.TELEMETRY.SPAN_OPEN_SEARCH_ROUTER, {
3535
name: CONST.TELEMETRY.SPAN_OPEN_SEARCH_ROUTER,
3636
op: CONST.TELEMETRY.SPAN_OPEN_SEARCH_ROUTER,
37+
attributes: {
38+
trigger: 'button',
39+
},
3740
});
3841

3942
openSearchRouter();

src/components/Search/SearchRouter/SearchRouter.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type {OptionData} from '@libs/ReportUtils';
3838
import {getAutocompleteQueryWithComma, getTrimmedUserSearchQueryPreservingComma} from '@libs/SearchAutocompleteUtils';
3939
import {getQueryWithUpdatedValues, sanitizeSearchValue} from '@libs/SearchQueryUtils';
4040
import StringUtils from '@libs/StringUtils';
41+
import {getSpan} from '@libs/telemetry/activeSpans';
4142
import Navigation from '@navigation/Navigation';
4243
import variables from '@styles/variables';
4344
import {navigateToAndOpenReport, searchInServer} from '@userActions/Report';
@@ -70,6 +71,16 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla
7071
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
7172
const isRecentSearchesDataLoaded = !isLoadingOnyxValue(recentSearchesMetadata);
7273
const shouldShowList = isRecentSearchesDataLoaded && areOptionsInitialized;
74+
75+
const coldStartAttributeSet = useRef(false);
76+
if (!coldStartAttributeSet.current) {
77+
const parentSpan = getSpan(CONST.TELEMETRY.SPAN_OPEN_SEARCH_ROUTER);
78+
if (parentSpan) {
79+
parentSpan.setAttribute('cold_start', !areOptionsInitialized);
80+
coldStartAttributeSet.current = true;
81+
}
82+
}
83+
7384
const personalDetails = usePersonalDetails();
7485
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
7586
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);

src/components/Search/SearchRouter/SearchRouterContext.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, {useContext, useEffect, useRef, useState} from 'react';
22
import type {AnimatedTextInputRef} from '@components/RNTextInput';
33
import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
44
import {navigationRef} from '@libs/Navigation/Navigation';
5-
import {startSpan} from '@libs/telemetry/activeSpans';
5+
import {endSpan, startSpan} from '@libs/telemetry/activeSpans';
66
import {close} from '@userActions/Modal';
77
import CONST from '@src/CONST';
88
import NAVIGATORS from '@src/NAVIGATORS';
@@ -77,8 +77,13 @@ function SearchRouterContextProvider({children}: ChildrenProps) {
7777
if (isBrowserWithHistory) {
7878
window.history.pushState({isSearchModalOpen: true} satisfies HistoryState, '');
7979
}
80+
startSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_MODAL_CLOSE_WAIT, {
81+
name: CONST.TELEMETRY.SPAN_SEARCH_ROUTER_MODAL_CLOSE_WAIT,
82+
op: 'ui.modal.wait',
83+
});
8084
close(
8185
() => {
86+
endSpan(CONST.TELEMETRY.SPAN_SEARCH_ROUTER_MODAL_CLOSE_WAIT);
8287
openSearch(setIsSearchRouterDisplayed);
8388
searchRouterDisplayedRef.current = true;
8489
},

0 commit comments

Comments
 (0)