App
App copied to clipboard
[HOLD for payment 2022-11-16] [$500] Dev - Activating metrics on crashes the app on Android - reported by @parasharrajat
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
Expensify/Expensify Issue URL: Issue reported by: @parasharrajat Slack conversation: https://expensify.slack.com/archives/C01GTK53T8Q/p1660151993852989
Triggered auto assignment to @puneetlath (AutoAssignerTriage
), see https://stackoverflow.com/c/expensify/questions/4749 for more details.
Current assignee @puneetlath is eligible for the External assigner, not assigning anyone new.
External Upwork post: https://www.upwork.com/jobs/~01b6342fd8e2b2240a Internal Upwork job: https://www.upwork.com/ab/applicants/1564180886378299392/job-details
Triggered auto assignment to Contributor-plus team member for initial proposal review - @Santhosh-Sellavel (Exported
)
Current assignee @puneetlath is eligible for the Exported assigner, not assigning anyone new.
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});
});
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
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
Please let me know if you have any concerns regarding the above proposal Thanks!
@puneetlath, @Santhosh-Sellavel Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
Will review this today/tomorrow.
@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.
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?

Yes crash occurred for me, even after applying your suggestion,
@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?
thanks
Yeah getAllKeys was the issue, I checked it on the Latest main earlier.
@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)

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
@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
@Santhosh-Sellavel confirming with you, you are modifying it in the
node_modules/react-native-onyx/lib/metrics/index.web.js
after cloning repohttps://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) ofreact-native-onyx
innode_modules/react-native-onyx/dist/web.development.js
instead. You can try modifying the built code to test as follow (line 1786)![]()
This solution solves the crash on the web, need to ensure all platforms are working well before moving forward!
@puneetlath, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!
Not overdue.
Need to check on other platforms too!
@christianwen have you checked other platforms, does a crash occur there?
@Santhosh-Sellavel yeah I've checked, no more crash on any platform, screenshots attached below
iOS
mWeb
Mac Desktop app
For Android and web I've already attached screenshots in https://github.com/Expensify/App/issues/10622#issuecomment-1234389410
Thanks!
@puneetlath, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!
Updated to $500. @Santhosh-Sellavel mind providing an update?
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?
@puneetlath, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!
@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.
Commented on the other thread!
@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?
Well that PR was merged a while ago, so we will need to put up a fix.
Ah whoops, understood.
@christianwen feel free to update your proposal accordingly if you're still interested!