App icon indicating copy to clipboard operation
App copied to clipboard

[HOLD for payment 2022-11-16] [$500] Dev - Activating metrics on crashes the app on Android - reported by @parasharrajat

Open mvtglobally opened this issue 2 years ago • 57 comments

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Action Performed:

1.Set the env with following. ONYX_METRICS=true CAPTURE_METRICS=true 2. Build the app for android.

Expected Result:

App should build

Actual Result:

App crashes

Workaround:

unknown

Platform:

Where is this issue occurring?

  • Web
  • Android

Version Number: Reproducible in staging?: need repro Reproducible in production?: need repro Email or phone of affected tester (no customers): Logs: https://stackoverflow.com/c/expensify/questions/4856 Notes/Photos/Videos: Any additional supporting documentation

image - 2022-08-28T224657 651 image - 2022-08-28T224655 216

Expensify/Expensify Issue URL: Issue reported by: @parasharrajat Slack conversation: https://expensify.slack.com/archives/C01GTK53T8Q/p1660151993852989

View all open jobs on GitHub

mvtglobally avatar Aug 29 '22 02:08 mvtglobally

Triggered auto assignment to @puneetlath (AutoAssignerTriage), see https://stackoverflow.com/c/expensify/questions/4749 for more details.

melvin-bot[bot] avatar Aug 29 '22 02:08 melvin-bot[bot]

Current assignee @puneetlath is eligible for the External assigner, not assigning anyone new.

melvin-bot[bot] avatar Aug 29 '22 09:08 melvin-bot[bot]

External Upwork post: https://www.upwork.com/jobs/~01b6342fd8e2b2240a Internal Upwork job: https://www.upwork.com/ab/applicants/1564180886378299392/job-details

puneetlath avatar Aug 29 '22 09:08 puneetlath

Triggered auto assignment to Contributor-plus team member for initial proposal review - @Santhosh-Sellavel (Exported)

melvin-bot[bot] avatar Aug 29 '22 09:08 melvin-bot[bot]

Current assignee @puneetlath is eligible for the Exported assigner, not assigning anyone new.

melvin-bot[bot] avatar Aug 29 '22 09:08 melvin-bot[bot]

Is it OK to have originalPromise value undefined?

In node_modules/react-native-onyx/lib/metrics/index.native.js

the originalPromise is undefined when the args value is:

["isLoadingReportData", true]

If originalPromise might be undefined, then my proposal is to add undefined check

+        if(originalPromise)
            originalPromise
                .then((result) => {
                    measureMarkToNow(mark, {result});
                })
                .catch((error) => {
                    measureMarkToNow(mark, {error});
                });

wildan-m avatar Sep 01 '22 07:09 wildan-m

Proposal

Problem

For the issue in Android, it's due to the code in react-native-onyx -> lib/metrics/index.native.js -> function decorateWithMetrics is expecting that the func passed in is a function that returns a promise, which it's not always the case, for example the lib/Onyx.js -> function update(data) { does not return anything. So the attempt to use the decorateWithMetrics with that update function will result in that error because in decorateWithMetrics we're using .then with an undefined variable.

Solution

In function decorateWithMetrics, we should check that the returned value of the function is a promise first before attempting to resolve that promise. If we only check if the originalPromise is truthy like in the above proposal, it will still crash if the originalPromise returns a value that is not a promise (thus does not have the .then method). I suggest that the code can be changed to below:

+function isPromise(value){
+    return typeof value?.then === 'function';
+}

/**
 * Wraps a function with metrics capturing logic
 * @param {function} func
 * @param {String} [alias]
 * @returns {function} The wrapped function
 */
function decorateWithMetrics(func, alias = func.name) {
    if (decoratedAliases.has(alias)) {
        throw new Error(`"${alias}" is already decorated`);
    }

    decoratedAliases.add(alias);

    function decorated(...args) {
        const mark = addMark(alias, args);

+       const returnedValue = func.apply(this, args);

+       if (isPromise(returnedValue)) {
            /*
            * Then handlers added here are not affecting the original promise
            * They create a separate chain that's not exposed (returned) to the original caller
            * */
+           returnedValue
                .then((result) => {
                    measureMarkToNow(mark, {result});
                })
                .catch((error) => {
                    measureMarkToNow(mark, {error});
                });
+       }

+       return returnedValue;
    }

    return decorated;
}

The way I check if a value is a promise is also how graphql-js does it, you can refer here https://github.com/graphql/graphql-js/blob/main/src/jsutils/isPromise.ts

Android after the fix, no more crashes Screen Shot 2022-09-01 at 21 39 30

About the crash in web

I'm not sure if the web crash is in the scope of this issue, but for the issue in web, it's due to the decorateWithMetrics noop in react-native-onyx -> lib/metrics/index.web.js is not returning anything, thus when we're using getAllKeys = decorate.decorateWithMetrics(getAllKeys, 'Onyx:getAllKeys');, the getAllKeys becomes undefined. So to fix that we need to modify the noop so that it returns the original function that is passed in

-function decorateWithMetrics() {}
+function decorateWithMetrics(func) { return func }

No more crash in web after the fix Screen Shot 2022-09-01 at 21 47 13

Please let me know if you have any concerns regarding the above proposal Thanks!

christianwen avatar Sep 01 '22 14:09 christianwen

@puneetlath, @Santhosh-Sellavel Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

melvin-bot[bot] avatar Sep 05 '22 07:09 melvin-bot[bot]

Will review this today/tomorrow.

Santhosh-Sellavel avatar Sep 06 '22 15:09 Santhosh-Sellavel

@puneetlath Actually, @wildan-m suggestion solves the crash that occurs on android devices. I do prefer their solution to address the same.

From @christianwen proposal,

I'm not sure if the web crash is in the scope of this issue, but for the issue in web, it's due to the decorateWithMetrics noop in react-native-onyx -> lib/metrics/index.web.js is not returning anything, thus when we're using getAllKeys = decorate.decorateWithMetrics(getAllKeys, 'Onyx:getAllKeys');, the getAllKeys becomes undefined. So to fix that we need to modify the noop so that it returns the original function that is passed in

There is a different issue that occurs on the web, I can confirm that. We need a proposal to address that issue, but their solution doesn't address the issue.

TLD

So we are looking proposal to address the crash that occurs on the web also.

Santhosh-Sellavel avatar Sep 07 '22 19:09 Santhosh-Sellavel

Hi, @Santhosh-Sellavel in my proposal I've also put the solution to address the crash on the web by fixing the decorateWithMetrics noop. By their solution doesn't address the issue, do you mean the crash still happens for you after trying the solution?

Screen Shot 2022-09-08 at 07 39 39

christianwen avatar Sep 08 '22 00:09 christianwen

Yes crash occurred for me, even after applying your suggestion,

Santhosh-Sellavel avatar Sep 08 '22 01:09 Santhosh-Sellavel

@Santhosh-Sellavel can you help attach the log of the crash after you apply the suggestion is it the same log as in the original description? Screen Shot 2022-09-08 at 09 34 03 thanks

christianwen avatar Sep 08 '22 02:09 christianwen

Yeah getAllKeys was the issue, I checked it on the Latest main earlier.

Santhosh-Sellavel avatar Sep 08 '22 03:09 Santhosh-Sellavel

@Santhosh-Sellavel confirming with you, you are modifying it in the node_modules/react-native-onyx/lib/metrics/index.web.js after cloning repo https://github.com/Expensify/App locally to try right?

if that's the case then it's not working because in node_modules, it is using the built code (not the source code) of react-native-onyx in node_modules/react-native-onyx/dist/web.development.js instead. You can try modifying the built code to test as follow (line 1786)

Screen Shot 2022-09-08 at 10 50 23

later after the suggested change in node_modules/react-native-onyx/lib/metrics/index.web.js is merged into react-native-onyx it will work after updating the react-native-onyx version

christianwen avatar Sep 08 '22 03:09 christianwen

@Santhosh-Sellavel Forgot to investigate the web version My updated proposal For android:

+         originalPromise && originalPromise
                .then((result) => {
                    measureMarkToNow(mark, {result});
                })
                .catch((error) => {
                    measureMarkToNow(mark, {error});
                });

For web:

function applyDecorators() {
    // We're requiring the script dynamically here so that it's only evaluated when decorators are used
    const decorate = require('./metrics');

    // Re-assign with decorated functions
    /* eslint-disable no-func-assign */
+    get = decorate.decorateWithMetrics(get, 'Onyx:get') || get;
+   set = decorate.decorateWithMetrics(set, 'Onyx:set') || set;
+    multiSet = decorate.decorateWithMetrics(multiSet, 'Onyx:multiSet') || multiSet;
+   clear = decorate.decorateWithMetrics(clear, 'Onyx:clear') || clear;
+   merge = decorate.decorateWithMetrics(merge, 'Onyx:merge') || merge;
+   mergeCollection = decorate.decorateWithMetrics(mergeCollection, 'Onyx:mergeCollection') || mergeCollection;
+   getAllKeys = decorate.decorateWithMetrics(getAllKeys, 'Onyx:getAllKeys') || getAllKeys;
+   initializeWithDefaultKeyStates = decorate.decorateWithMetrics(initializeWithDefaultKeyStates, 'Onyx:defaults') || initializeWithDefaultKeyStates;
+    update = decorate.decorateWithMetrics(update, 'Onyx:update') || update;
    /* eslint-enable */

The @christianwen wen solution for web version might be cleaner and not repeated

wildan-m avatar Sep 08 '22 04:09 wildan-m

@Santhosh-Sellavel confirming with you, you are modifying it in the node_modules/react-native-onyx/lib/metrics/index.web.js after cloning repo https://github.com/Expensify/App locally to try right?

Ya right!

if that's the case then it's not working because in node_modules, it is using the built code (not the source code) of react-native-onyx in node_modules/react-native-onyx/dist/web.development.js instead. You can try modifying the built code to test as follow (line 1786)

Screen Shot 2022-09-08 at 10 50 23

This solution solves the crash on the web, need to ensure all platforms are working well before moving forward!

Santhosh-Sellavel avatar Sep 08 '22 21:09 Santhosh-Sellavel

@puneetlath, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!

melvin-bot[bot] avatar Sep 12 '22 06:09 melvin-bot[bot]

Not overdue.

Need to check on other platforms too!

Santhosh-Sellavel avatar Sep 12 '22 18:09 Santhosh-Sellavel

@christianwen have you checked other platforms, does a crash occur there?

Santhosh-Sellavel avatar Sep 12 '22 18:09 Santhosh-Sellavel

@Santhosh-Sellavel yeah I've checked, no more crash on any platform, screenshots attached below

iOS simulator_screenshot_C9FE8B23-06B8-4EAB-B613-188E3EA78691

mWeb Screen Shot 2022-09-13 at 07 49 59

Mac Desktop app Screen Shot 2022-09-13 at 07 50 40

For Android and web I've already attached screenshots in https://github.com/Expensify/App/issues/10622#issuecomment-1234389410

Thanks!

christianwen avatar Sep 13 '22 00:09 christianwen

@puneetlath, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!

melvin-bot[bot] avatar Sep 16 '22 06:09 melvin-bot[bot]

Updated to $500. @Santhosh-Sellavel mind providing an update?

puneetlath avatar Sep 16 '22 17:09 puneetlath

Sorry for the delay here! @puneetlath Actually, @christianwen proposal solves the issue, but I am not sure this is the right way and am afraid of missing something else in the context here. So can we have a look from internal who is familiar & involved in implementing this moving forward?

Santhosh-Sellavel avatar Sep 16 '22 20:09 Santhosh-Sellavel

@puneetlath, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!

melvin-bot[bot] avatar Sep 20 '22 06:09 melvin-bot[bot]

@Santhosh-Sellavel @christianwen proposal looks good to me if we want to go with that path.

IMO, the correct fix will be https://github.com/Expensify/react-native-onyx/pull/133/files#r975658158.

Other than this,

  • it requires fixing the signature of noop methods in metrics/index.web.js.
  • Add/fix tests for the changes. (no proposal is valid without unit tests).

cc: @luacmartins Since you are the author of Onyx changes.

parasharrajat avatar Sep 20 '22 17:09 parasharrajat

Commented on the other thread!

luacmartins avatar Sep 20 '22 18:09 luacmartins

@parasharrajat @luacmartins just to make sure I understand, you are both saying that this Onyx PR should solve this issue. And no further action should be needed, is that correct?

puneetlath avatar Sep 20 '22 20:09 puneetlath

Well that PR was merged a while ago, so we will need to put up a fix.

luacmartins avatar Sep 20 '22 21:09 luacmartins

Ah whoops, understood.

@christianwen feel free to update your proposal accordingly if you're still interested!

puneetlath avatar Sep 20 '22 22:09 puneetlath