Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more Firebase performance tracing. Improve Android boot time. #4247

Merged
merged 16 commits into from
Jul 28, 2021
Merged
1 change: 1 addition & 0 deletions __mocks__/@react-native-firebase/perf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => {};
1 change: 1 addition & 0 deletions src/CONST.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion src/Expensify.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Onyx.init({
[ONYXKEYS.IOU]: {
loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
},
[ONYXKEYS.IS_SIDEBAR_LOADED]: false,
},
registerStorageEventListener: (onStorageEvent) => {
listenToStorageEvents(onStorageEvent);
Expand Down Expand Up @@ -72,6 +73,9 @@ const propTypes = {
/** Whether the initial data needed to render the app is ready */
initialReportDataLoaded: PropTypes.bool,

/** Tells us if the sidebar has rendered */
isSidebarLoaded: PropTypes.bool,

/** List of betas */
betas: PropTypes.arrayOf(PropTypes.string),
};
Expand All @@ -84,6 +88,7 @@ const defaultProps = {
},
updateAvailable: false,
initialReportDataLoaded: false,
isSidebarLoaded: false,
betas: [],
};

Expand Down Expand Up @@ -144,7 +149,7 @@ class Expensify extends PureComponent {
Navigation.navigate(ROUTES.WORKSPACE_NEW);
}

if (this.getAuthToken() && this.props.initialReportDataLoaded) {
if (this.getAuthToken() && this.props.initialReportDataLoaded && this.props.isSidebarLoaded) {
BootSplash.getVisibilityStatus()
.then((value) => {
if (value !== 'visible') {
Expand Down Expand Up @@ -208,4 +213,7 @@ export default withOnyx({
initialReportDataLoaded: {
key: ONYXKEYS.INITIAL_REPORT_DATA_LOADED,
},
isSidebarLoaded: {
key: ONYXKEYS.IS_SIDEBAR_LOADED,
},
})(Expensify);
3 changes: 3 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export default {
// Boolean flag set whenever we are waiting for the reconnection callbacks to finish.
IS_LOADING_AFTER_RECONNECT: 'isLoadingAfterReconnect',

// Boolean flag set whenever the sidebar has loaded
IS_SIDEBAR_LOADED: 'isSidebarLoaded',

NETWORK_REQUEST_QUEUE: 'networkRequestQueue',

// What the active route is for our navigator. Global route that determines what views to display.
Expand Down
18 changes: 18 additions & 0 deletions src/components/OptionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ const propTypes = {

/** Whether to disable the interactivity of the list's option row(s) */
disableRowInteractivity: PropTypes.bool,

/** Callback to execute when the SectionList lays out */
onLayout: PropTypes.func,
};

const defaultProps = {
Expand All @@ -99,6 +102,7 @@ const defaultProps = {
showTitleTooltip: false,
optionMode: undefined,
disableRowInteractivity: false,
onLayout: undefined,
};

class OptionsList extends Component {
Expand All @@ -109,6 +113,8 @@ class OptionsList extends Component {
this.renderSectionHeader = this.renderSectionHeader.bind(this);
this.extractKey = this.extractKey.bind(this);
this.onScrollToIndexFailed = this.onScrollToIndexFailed.bind(this);
this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 95};
this.didLayout = false;
}

shouldComponentUpdate(nextProps) {
Expand Down Expand Up @@ -228,6 +234,18 @@ class OptionsList extends Component {
renderItem={this.renderItem}
renderSectionHeader={this.renderSectionHeader}
extraData={this.props.focusedIndex}
initialNumToRender={5}
maxToRenderPerBatch={5}
windowSize={5}
viewabilityConfig={this.viewabilityConfig}
onViewableItemsChanged={() => {
if (this.didLayout) {
return;
}

this.didLayout = true;
this.props.onLayout();
}}
/>
</View>
);
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Firebase/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** Web does not use Firebase for performance tracing */

export default {
startTrace() {},
stopTrace() {},
};
56 changes: 56 additions & 0 deletions src/libs/Firebase/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable no-unused-vars */
import perf from '@react-native-firebase/perf';
import {isDevelopment} from '../Environment/Environment';
import Log from '../Log';

const traceMap = {};

/**
* @param {String} customEventName
*/
function startTrace(customEventName) {
const start = global.performance.now();
if (isDevelopment()) {
return;
}

if (traceMap[customEventName]) {
return;
}

perf().startTrace(customEventName)
.then((trace) => {
traceMap[customEventName] = {
trace,
start,
};
});
}

/**
* @param {String} customEventName
*/
function stopTrace(customEventName) {
const stop = global.performance.now();

if (isDevelopment()) {
return;
}

const {trace, start} = traceMap[customEventName];
if (!trace) {
return;
}

trace.stop();

// Uncomment to inspect logs on release builds
// Log.info(`sidebar_loaded: ${stop - start} ms`, true);

delete traceMap[customEventName];
}

export default {
startTrace,
stopTrace,
};
20 changes: 20 additions & 0 deletions src/libs/actions/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';
import * as API from '../API';
import Firebase from '../Firebase';
import CONST from '../../CONST';

let isSidebarLoaded;

Onyx.connect({
key: ONYXKEYS.IS_SIDEBAR_LOADED,
callback: val => isSidebarLoaded = val,
initWithStoredValues: false,
});

/**
* @param {String} url
Expand All @@ -17,7 +27,17 @@ function setLocale(locale) {
Onyx.merge(ONYXKEYS.NVP_PREFERRED_LOCALE, locale);
}

function setSidebarLoaded() {
if (isSidebarLoaded) {
return;
}

Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true);
Firebase.stopTrace(CONST.TIMING.SIDEBAR_LOADED);
}

export {
setCurrentURL,
setLocale,
setSidebarLoaded,
};
10 changes: 8 additions & 2 deletions src/libs/actions/Timing.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import getPlatform from '../getPlatform';
import {Graphite_Timer} from '../API';
import {isDevelopment} from '../Environment/Environment';

let timestampData = {};

Expand All @@ -26,14 +27,19 @@ function end(eventName, secondaryName = '') {
: `expensify.cash.${eventName}`;

console.debug(`Timing:${grafanaEventName}`, eventTime);
delete timestampData[eventName];

// eslint-disable-next-line no-undef
if (isDevelopment()) {
// Don't create traces on dev as this will mess up the accuracy of data in release builds of the app
return;
}

Graphite_Timer({
name: grafanaEventName,
value: eventTime,
platform: `${getPlatform()}`,
});

delete timestampData[eventName];
}
}

Expand Down
21 changes: 20 additions & 1 deletion src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import styles from '../../styles/styles';
import ReportView from './report/ReportView';
Expand All @@ -8,6 +9,7 @@ import Navigation from '../../libs/Navigation/Navigation';
import ROUTES from '../../ROUTES';
import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator';
import {updateCurrentlyViewedReportID} from '../../libs/actions/Report';
import ONYXKEYS from '../../ONYXKEYS';

const propTypes = {
/** Navigation route context info provided by react navigation */
Expand All @@ -18,6 +20,13 @@ const propTypes = {
reportID: PropTypes.string,
}).isRequired,
}).isRequired,

/** Tells us if the sidebar has rendered */
isSidebarLoaded: PropTypes.bool,
};

const defaultProps = {
isSidebarLoaded: false,
};

class ReportScreen extends React.Component {
Expand Down Expand Up @@ -84,6 +93,10 @@ class ReportScreen extends React.Component {
}

render() {
if (!this.props.isSidebarLoaded) {
return null;
}

return (
<ScreenWrapper style={[styles.appContent, styles.flex1]}>
<HeaderView
Expand All @@ -100,4 +113,10 @@ class ReportScreen extends React.Component {
}

ReportScreen.propTypes = propTypes;
export default ReportScreen;
ReportScreen.defaultProps = defaultProps;

export default withOnyx({
isSidebarLoaded: {
key: ONYXKEYS.IS_SIDEBAR_LOADED,
},
})(ReportScreen);
3 changes: 2 additions & 1 deletion src/pages/home/sidebar/SidebarLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import CONST from '../../../CONST';
import {participantPropTypes} from './optionPropTypes';
import themeColors from '../../../styles/themes/default';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';

import * as App from '../../../libs/actions/App';

const propTypes = {
/** Toggles the navigation menu open and closed */
Expand Down Expand Up @@ -179,6 +179,7 @@ class SidebarLinks extends React.Component {
showTitleTooltip
disableFocusOptions={this.props.isSmallScreenWidth}
optionMode={this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 'compact' : 'default'}
onLayout={App.setSidebarLoaded}
/>
<KeyboardSpacer />
</View>
Expand Down
5 changes: 5 additions & 0 deletions src/pages/home/sidebar/SidebarScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '../../../components/Icon/Expensicons';
import Permissions from '../../../libs/Permissions';
import ONYXKEYS from '../../../ONYXKEYS';
import Firebase from '../../../libs/Firebase';

const propTypes = {
/** Beta features list */
Expand All @@ -47,6 +48,10 @@ class SidebarScreen extends Component {
};
}

componentDidMount() {
Firebase.startTrace(CONST.TIMING.SIDEBAR_LOADED);
}

/**
* Method called when a Create Menu item is selected.
*/
Expand Down