Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
Optimize report ordering in the LHN #10784
Optimize report ordering in the LHN #10784
Changes from all commits
fed705a
44a2ff6
57a4c2e
4b40492
00ebe57
103b126
bb57fb0
f3a7fa0
35cfd23
8e8ba1f
6a26a03
d973776
b770d0c
8a6ea06
9626972
6091608
56d6d34
853f8b8
44e258c
bd05cec
475f0ed
80615c9
de15223
8512aa0
ee56ec4
e7f7b33
8057f21
ff49948
7c51a86
fd60c45
9fad973
ceb2ea9
2ef3622
9ceb909
25825d0
6eb03cf
42ece5b
66d147d
d03e6fb
47aa0b8
99532c2
988e7c6
4036325
34a60c9
a41a30a
6b87f56
629c4c4
55bd67f
84b1be4
5ca3b5e
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait it is slower to push an item into an array than do this? 🤯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the general answer is "yes, it's faster", but there are differences between JS engines which make the testing somewhat unreliable. Generally,
push()
does some extra bits of processing from the base JS object that arrays extend from, whereas using an index approach bypassing that little bit of overhead.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should leave a note explaining why
_.each
is not hereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment about it in the method docs, because I think it should also explain why spread operators aren't being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment to clarify why we are using
Array.prototype.push.apply
so people don't move back to array.push?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about the explanation and +1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's hard for me to tell what is style and what is an improvement. Is there some benefit to declaring the object with all the properties it might have instead of assigning them later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My reason for this change is that before, 100% of the logic was running regardless if there was a report or not. There is logic for reports AND personal details but for the LHN, we only need the logic for the reports. By being more strategic with the
if
statements, it now means that it only runs the minimum necessary logic depending on if there is a report or not.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds intuitive to me. Mostly asking as I'm curious which of these changes had the biggest impact on performance (for my own understanding).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I could have taken better notes while doing this, but in the end, I don't think there are hugely practical takeaways. At large scale, ANY piece of code can become inefficient, and the same thing that fixes one bottleneck isn't going to fix all bottle necks and shouldn't be used everywhere just for the sake of performance (like
uniqFast()
).I think the better takeaway for anyone would be to learn how to do performance testing. This is something that I could do a LOU about because it seems like very few of our engineers are familiar with how to do this.
Just for a quick sample though, here were the notes that I did take:
The changes to getSearchText() were to stop using
.push()
and_.unique()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we initialize
isDefaultRoom
in results too?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! This came from me solving a merge conflict.
isDefaultRoom
didn't exist when I started refactoring so it got kind of left out.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB because I know you didn't introduce this variable name, but
shouldShowSubscript
is not very descriptive / the context for it is lost in this file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like it could maybe be renamed to
shouldShowSubscriptAvatar
, which is a small avatar that is below the main line of text?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When did reportID become strings and are all reportID now strings in JS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is being discussed in Slack to make sure we're getting to the right conclusion: https://expensify.slack.com/archives/C02HWMSMZEC/p1662382397049269
Regardless, we're not treating them consistently everywhere yet, but we want to!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we know for sure that
report
is not null or undefined?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
report is defined as the _.each iterator, so I think it should always be defined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my first thought, but what if you have
[{ // some real object }, null, { // some real object }]
? No idea if that can actually happen in this case though:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm that's a good point. I'm not sure either 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there are a few things here...
_.each
doesn't protect against null members in a collection, so it's possible forreport
to be null.This particular line of code (and several lines down below it) need to have null protection. There are two solutions around this typically...
_.compact
for arrays to clear out falsy values or use_.pluck
for objectsreport
is nullI think I'm going to do number
2
and then remove all the report null checksThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do this sooner on line 469?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably could. I'm honestly a bit perplexed by this and the comment. Since reports are indexed in Onyx by their ID, I don't think it should ever be possible for a report to not have an ID. If it is possible, I think it is a bug that we should track down and fix. So, I'd be more willing to either remove this check entirely, or at least add a log if we detect there is no reportID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm feeling cautious about removing that since it looks like we use the "report without a reportID" as a kind of "hook" to fetch the report. Which was the expected behavior at one point. But also looks like there's maybe some further cleanup to do here:
https://github.com/Expensify/App/blame/c9b41bff8b728073db85f38782b591a0fd4a9772/src/libs/actions/Report.js#L1135-L1137
We could solve this by sending the report and comment when a new message is added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wait scratch everything I just said haha that code looks for
report.reportID && !reportName
(but we should still clean that up)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still feeling cautious and agree we should try to figure out if/why a report object can be merged with no
reportID
(and a log would help)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About the best I can offer in that case then is to add a log somewhere to detect if a report ID is missing. I don't think this code is the right place for it though. I would add it somewhere in Onyx.update() probably? Something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but that doesn't really work because of doing something like
Onyx.merge('report_123', {reportName: 'blah'})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kind of just feel here like we need to trust that we would never have a report without a reportID. And if it happens, we will find out about it in buggy behavior and crashes.