Expensify / App

Welcome to New Expensify: a complete re-imagination of financial collaboration, centered around chat. Help us build the next generation of Expensify by sharing feedback and contributing to the code.
https://new.expensify.com
MIT License
3.52k stars 2.88k forks source link

[HOLD for payment 2024-03-22] [$1000] Settings - Performance and heat issues on the "Troubleshoot" option #36645

Closed m-natarajan closed 7 months ago

m-natarajan commented 8 months ago

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Issue was found when executing #35306 Version Number: 1.4.42.0 Reproducible in staging?: y Reproducible in production?: New feature If this was caught during regression testing, add the test name, ID and link from TestRail: Email or phone of affected tester (no customers): Logs: https://stackoverflow.com/c/expensify/questions/4856 Expensify/Expensify Issue URL: Issue reported by: Applause internal team Slack conversation:

Action Performed:

  1. Navigate to Account settings - About - Troubleshoot
  2. Background the app
  3. Foreground the app
  4. Swipe up and down

Expected Result:

Swiping should be smooth. The phone shouldn't heat up or crash.

Actual Result:

Low performance when I foreground the app. The phone heats up if I stay on the page. It even crashed once. The issue disappears if I navigate back and to the "Troubleshoot" again.

Workaround:

unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

Screenshots/Videos

Add any screenshot/video evidence

https://github.com/Expensify/App/assets/38435837/00fb81f1-f0b8-4114-890f-a6ca371e338d

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~01dd3351f4b07d85c0
  • Upwork Job ID: 1758255599540744192
  • Last Price Increase: 2024-02-27
melvin-bot[bot] commented 8 months ago

Job added to Upwork: https://www.upwork.com/jobs/~01dd3351f4b07d85c0

melvin-bot[bot] commented 8 months ago

Triggered auto assignment to Contributor-plus team member for initial proposal review - @hoangzinh (External)

melvin-bot[bot] commented 8 months ago

Triggered auto assignment to @laurenreidexpensify (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details.

github-actions[bot] commented 8 months ago

:wave: Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:

  1. Identify the pull request that introduced this issue and revert it.
  2. Find someone who can quickly fix the issue.
  3. Fix the issue yourself.
melvin-bot[bot] commented 8 months ago

Triggered auto assignment to @marcaaron (Engineering), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.

marcaaron commented 8 months ago

Trying to repro now.

marcaaron commented 8 months ago

It seems like a possible memory leak or something. I do notice things get slightly more janky. But doesn't seem like a blocker if the only way to reproduce it is to go to this particular page and background the app.

melvin-bot[bot] commented 8 months ago

@hoangzinh, @laurenreidexpensify Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

laurenreidexpensify commented 8 months ago

Asking here to get another repro of this

mallenexpensify commented 8 months ago

@laurenreidexpensify , snaggin' from you since I have an iPhone and am able to reproduce, at least the part where the scroll isn't smooth.

https://github.com/Expensify/App/assets/22508304/62851b28-8abf-4f93-93fe-848af93c40ba

mallenexpensify commented 8 months ago

Also... reporting back that I didn't experience any heat issues or noticeable battery drain.

mallenexpensify commented 8 months ago

Waiting for proposals

melvin-bot[bot] commented 8 months ago

@hoangzinh, @mallenexpensify Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

hoangzinh commented 8 months ago

Seeking proposals on Slack https://expensify.slack.com/archives/C01GTK53T8Q/p1709046872674669

melvin-bot[bot] commented 8 months ago

Upwork job price has been updated to $1000

mallenexpensify commented 8 months ago

Thanks for posting in #expensify-open-source @hoangzinh Doubling price to get eyes and proposals.

wustwg commented 8 months ago

Does only iOS have this problem?Does Android work smoothly?

melvin-bot[bot] commented 8 months ago

📣 @wustwg! 📣 Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:

  1. Make sure you've read and understood the contributing guidelines.
  2. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  3. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details. Screen Shot 2022-11-16 at 4 42 54 PM Format:
    Contributor details
    Your Expensify account email: <REPLACE EMAIL HERE>
    Upwork Profile Link: <REPLACE LINK HERE>
wustwg commented 8 months ago

Contributor details Your Expensify account email: wg940412478@gmail.com Upwork Profile Link: https://www.upwork.com/freelancers/~01eec1faf4fd4ceb3c?viewMode=1

melvin-bot[bot] commented 8 months ago

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

hoangzinh commented 8 months ago

I also feel a little laggy on my Android device. But @m-natarajan could you confirm if this issue also happens on Android devices? Thanks

tamachai commented 8 months ago

Contributor details Your Expensify account email: poppies.great_0r@icloud.com Upwork Profile Link: https://www.upwork.com/freelancers/~0107f7a8dc488440a0

melvin-bot[bot] commented 8 months ago

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

tamachai commented 8 months ago

Proposal

Please re-state the problem that we are trying to solve in this issue.

Performance degradation occurs when viewing the Troubleshoot screen on iOS.

What is the root cause of that problem?

The Troubleshoot screen is presented using transparentModal. This causes the About screen's animation to continue to play as the Troubleshoot screen overlays the About screen.

What changes do you think we should make in order to solve the problem?

Update the presentation type for the Troubleshoot screen to modal. This will prevent rendering the About screen while the Troubleshoot screen is displayed.

What alternative solutions did you explore? (Optional)

ikevin127 commented 8 months ago

Proposal

Please re-state the problem that we are trying to solve in this issue

Performance and heating issues on the Troubleshoot page if we put the app into background state then back to foreground.

What is the root cause of that problem?

[!NOTE] The issue is only happening on native devices because the navigation is stacking the screens (in memory). I wasn't able to reproduce on Android, only on iOS.

This happens because on Native when we navigate to a new screen, the previous screen remains in the navigation stack. Each new screen is added (pushed) onto the top of the stack, and when the user navigates back, the current screen is removed (popped), revealing the previous screen.

The problem in our case is that some of these pages contain Lottie animations and because the screens are kept in memory as we navigate (ex. from About to Troubleshoot), both pages animations never unmount when transitioning the app back from background to foreground - which increases the load on the UI thread significantly.

What changes do you think we should make in order to solve the problem?

We can handle this by creating a native platform specific hook called useAppState which we will use within the Lottie/index.tsx component.

useAppState hook code ```ts // src/hooks/useAppState/index.native.ts import React from 'react'; import type { AppStateStatus } from 'react-native'; import { AppState } from 'react-native'; type AppStateType = { isForeground: boolean; isInactive: boolean; isBackground: boolean; } function useAppState() { const [appState, setAppState] = React.useState({ isForeground: AppState.currentState === 'active', isInactive: AppState.currentState === 'inactive', isBackground: AppState.currentState === 'background', }); React.useEffect(() => { function handleAppStateChange(nextAppState: AppStateStatus) { setAppState({ isForeground: nextAppState === 'active', isInactive: nextAppState === 'inactive', isBackground: nextAppState === 'background', }); } const subscription = AppState.addEventListener('change', handleAppStateChange); return () => subscription.remove() }, []); return appState; } export default useAppState; ```

Which we will add to the Lottie component like so:

// ...existing logic
const appState = useAppState();

// If the image fails to load or app is in background state, we'll just render an empty view
// using the fallback in case of a Lottie error or to prevent memory leaks.
if (isError || appState.isBackground) {
    return <View style={aspectRatioStyle} />;
}
// ...existing logic

By doing this we unmount the animations when the app goes into background, and mount them again when the app is foregrounded - this way we take the load off the UI thread and get rid of both performance and heating issues.

Videos

iOS: Native | Before | After | | ------------- | ------------- | |
melvin-bot[bot] commented 8 months ago

@hoangzinh @mallenexpensify this issue was created 2 weeks ago. Are we close to a solution? Let's make sure we're treating this as a top priority. Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!

hoangzinh commented 8 months ago

@tamachai @ikevin127 Thanks for proposals everyone. My MAC has a problem when connect to an external device so I can't verify proposals on my ios device atm, please bear with me. I will sort it out soon.

hoangzinh commented 8 months ago

@tamachai @ikevin127 Thanks for proposals everyone. Both proposals look great, especially @ikevin127 with details root cause. I have a few concerns about solutions:

ikevin127 commented 8 months ago

@hoangzinh With the solution I proposed that is the trade-off since we're re-mounting the animation.

This load delay also happens first time you open the app and navigate to one of the screens where an animation exists - so it makes sense it would also exist when bringing the app back from background - which means all processes restart / re-mount.

I can look into more but for now just assume this is as good as it gets considering the RCA.

hoangzinh commented 8 months ago

@ikevin127 instead of based on app state, could you test & compare with useIsFocused hook? (hide/display based on isFocused state)

tamachai commented 8 months ago

@tamachai @ikevin127 Thanks for proposals everyone. Both proposals look great, especially @ikevin127 with details root cause. I have a few concerns about solutions:

  • @tamachai: Update the presentation type for the Troubleshoot screen to modal I'm worried about this approach potentially causing regression bugs. We must have reasons of using transparentModal. There is a PR change to use card presentation but has been reverted revert: native stack #37305. About your alternative solutions, could you elaborate more about playing/pausing animations based on useFocusEffect? Thanks
  • @ikevin127 When I watch your recordings, when the app is foregrounded, the animation doesn't load in a short period. Is there any way we can avoid it? Thanks

card uses OS defined animations. Whereas modal and transparentModel use the same animations as each other but have distinct settings to control the visibility of the header and previous screen. It's best to avoid transparent modal unless the app needs to render the screen below the current, such as when overlaying modals on iPad or using effects like blur. For reference, here's the view hierarchy with transparentModal vs modal.

transparentModal:

transparentModal

modal:

modal

When I experimented playing/pausing the animation via useFocusEffect, a stutter was introduced during view transition, as the animation begins to play.

mvtglobally commented 8 months ago

Issue not reproducible during KI retests. (First week)

hoangzinh commented 8 months ago

@tamachai if we use modal, will it keep the previous state if we navigate back? For example, previously we had an expectation here https://github.com/Expensify/App/issues/36077

playing/pausing the animation via useFocusEffect

Could you elaborate on how would you playing/pausing?

hoangzinh commented 8 months ago

Issue not reproducible during KI retests. (First week)

@mallenexpensify my iPhone is broken so I couldn't verify atm. Could you help to check if we can still reproduce this issue in the latest version? Thanks.

mallenexpensify commented 8 months ago

Reproducible for me on 1.4.47. It might not be clear in the vid but the scrolling seemed or felt jumpy

https://github.com/Expensify/App/assets/22508304/77e8a3a8-163c-4cac-ae5b-e708562017d4

tamachai commented 8 months ago

@tamachai if we use modal, will it keep the previous state if we navigate back? For example, previously we had an expectation here #36077

Yes, it should behave the same way. It doesn’t actually alter the view stack. An issue would occur if the modal wasn’t full screen, but this isn’t the case. Maybe we can look at only applying this to the settings and troubleshooting screens and then verify there are no regressions.

playing/pausing the animation via useFocusEffect

Could you elaborate on how would you playing/pausing?

LottieView exposes a ref prop that can be used to control the view via its imperative API. Using useFocusEffect we can call play/pause, depending on if the screen hosting the Lottie view is focused. The issue here is that starting playback introduces stutter. This stutter is especially noticeable when swiping back to the previous screen.

hoangzinh commented 8 months ago

@ikevin127 @tamachai could you guys try to use useFocusEffect to hide/show lotte view and share recordings results here? So we can know which approach gives better results. Thanks

ikevin127 commented 8 months ago

@hoangzinh I tried that but for my proposal it didn't work since (IMO) our issue has to be tackled based on the AppState change (background/foreground) and not whether the screen with animation is focused or not.

As explained by the other proposal's author, because the way our navigation works currently - there's no page focus change when the app goes into background and comes back to the foreground, hence we cannot use that to tackle the issue as you suggested.

hoangzinh commented 8 months ago

@ikevin127 Thanks for testing. I did think that, at least, we can avoid Lotte animation on hidden pages.

tamachai commented 8 months ago

@ikevin127 @tamachai could you guys try to use useFocusEffect to hide/show lotte view and share recordings results here? So we can know which approach gives better results. Thanks

Sure thing. I’ll try to put something together today.

mallenexpensify commented 8 months ago

Has anyone confirmed if there is battery drain or if performance is affected. Wonder if this purely aesthetic. Asking because I'm wanting to assign this too a VIP/Wave project.

ikevin127 commented 8 months ago

I can confirm that when the UI thread gets really heavy upon resuming app from background to foreground -> if waiting for 3+ minutes the phone heat increases gradually w/ every minute which also drains battery faster than usual.

mallenexpensify commented 8 months ago

Thanks @ikevin127 , added to #vip-vsb

melvin-bot[bot] commented 8 months ago

@hoangzinh @mallenexpensify this issue is now 3 weeks old. There is one more week left before this issue breaks WAQ. What needs to happen to get a PR in review this week? Please create a thread in #expensify-open-source to discuss. Thanks!

hoangzinh commented 8 months ago

if waiting for 3+ minutes the phone heat increases gradually w/ every minute which also drains battery faster than usual

Hi @ikevin127 your approach won't fix this case, will it? Because your approach is only applied when the app is changed state from background to foreground 🤔

ikevin127 commented 8 months ago

@hoangzinh It does fix the issue - both heating and heavy animation problems were gone when I tested my solution.

Again, as explained in my proposal's RCA - the lottie animations never stop when the app goes into the background, causing memory leak - the issue occurs because when the app reopens (from background state), we are presented with all that in-memory build-up - which causes our heavy UI / overheating issues.

What my solution does is un-mount the animation as soon as the app goes into the background and re-mounts it when the app is brought back to the foreground.

The only tradeoff of the solution being that there's a bit of delay until animation is rendered, depending on animation size - which we already have currently when first navigating to any screen with Lottie animation.

hoangzinh commented 8 months ago

@tamachai @ikevin127 Thanks for coming with me on this issue. I appreciate it. After playing around with solutions, @ikevin127 proposal looks good to me. The drawback of his solution is there Lottie view is not displayed a second when the app goes to forgeground. But I think it does not really matter because eventually it's displayed perfectly. Moreover, if we can combine useIsFocused and useAppState hooks, I think it would improve performance here. I.e here

if (isError || !isFocused || appState.isBackground) {
    return <View style={aspectRatioStyle} />;
}

Link to @ikevin127 proposal https://github.com/Expensify/App/issues/36645#issuecomment-1971281335 🎀👀🎀 C+ reviewed

melvin-bot[bot] commented 8 months ago

Triggered auto assignment to @hayata-suenaga, see https://stackoverflow.com/c/expensify/questions/7972 for more details.

hayata-suenaga commented 7 months ago

Thank you all for your help with this bug. I have a follow-up question regarding @ikevin127's analysis of the issue.

The Lottie animation doesn't unmount or stop when the app moves to the background or when the screen (i.e., the stack) is hidden behind another during navigation.

I have two questions:

ikevin127 commented 7 months ago

@hayata-suenaga Here's my take:

Are we certain that the Lottie library doesn't manage the animation state when the app moves to the background?

TLDR: The problem lies in the way our screens are stacked when transitioning the app to background state and back, not an issue with the Lottie library.

Lottie does manage animation in terms of app state by keeping record of the animation timeline and resuming, according to this lottie-react-native merged PR - but other than that there's no un-mount / re-mount functionality for edge cases like ours (navigation stack transparentModal + app state change with Lottie animation ongoing).

We use react-freeze. Do we know why this isn't resolving the performance issue when hidden stack screens have Lottie animations?

As mentioned in our docs here:

react-freeze allows us to increase performance by avoiding unnecessary re-renders of screens that aren’t visible to the user anyway.

This only prevents re-renders but since in our edge case there's no re-rendering happening, but only animation related behaviour (UI thread) - which does not help when bringing app back from background to the foreground and processes resume.

Also, in our case our react-freeze wrapper only applies to screens that are not focussed, but when the Troubleshoot page goes into the background - the screen remains focused, bringing the problem back to the app state transition.

This is why we need the proposed solution which un-mounts and re-mounts the animation component in order to actually fix this performance issue.