-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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 performance metrics to the app #7953
Conversation
@@ -73,6 +74,11 @@ function PostDraft({ | |||
setCursorPosition(message.length); | |||
}, [channelId, rootId]); | |||
|
|||
useEffect(() => { |
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.
What is the impact if you use useLayoutEffect
instead?
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.
useLayoutEffect
will run before painting into the screen. I would prefer if the measure is done after painting. But 0/5.
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.
ok I understand.. now the next question is why do we consider the channel/thread switch complete when the post_draft renders ? out of curiosity my question, as I want to understand how did you come up with the fact that this is the last component being rendered on screen.
Also, if that is the case, is it still true on tablets ?
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.
Now that you mention it, I wanted to revisit this, you are right.
No, the post draft is not the last thing that renders in a channel. I thought it might be (and was convenient) but after some tests, it may not be.
Also, I completely forgot about the tablet scenario 🤦
I will revisit this.
app/screens/home/channel_list/categories_list/categories/categories.test.tsx
Outdated
Show resolved
Hide resolved
version: '0.1.0', | ||
labels: { | ||
platform: Platform.select({ios: 'ios', default: 'android'}), | ||
agent: 'rnapp', |
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.
Similar to my comment on the server PR, I don't think we need to set the agent for mobile reports
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.
not for this PR, but something we should consider is to come up with a way to identify the type of device (High end, Mid, low end) as that will give us more insight with the collected numbers.
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 seems like a good idea. I've wondered about similar on the web-side, but we're much more restricted in what metrics we can collect about the device there
app/managers/performance_metrics_manager/performance_metrics_batcher.ts
Outdated
Show resolved
Hide resolved
}; | ||
} | ||
|
||
private onAppStateChange(appState: AppStateStatus) { |
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 the PerformanceMetricsManager
perhaps be the one to listen for the app state to change because there'll be multiple batchers for multiple
Alternatively, maybe we would want to send out any pending metrics as soon as you switch away from a server to avoid sending out too many requests to different servers when the app goes to the background
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 didn't want to add more methods to the batcher interface (in this case, would be a flush method). But we could add it, yes.
We could also do as you say, and send the metrics on server switch, but that would have some impact (probably irrelevant though) to the server switch logic. I feel it is better to leave that performance impact to the "send app to the background" interaction.
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 would agree with both of you.
- leave the logic when the app is sent to the background.
- move the event listener to the manager and call the sendBatch on each batcher from there.
Important to mention that any timer is paused when the app state changes to background
this.ensureBatcher(serverUrl).addToBatch({ | ||
metric: metricName, | ||
value: (performanceNow - startTime) / 1000, | ||
timestamp: Math.round(startTime + performanceNowSkew), |
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.
Is performance.timeOrigin
not available for use here? I remember you mentioned that the performance API is only partially implemented on RN, but I don't know if that one is included
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.
Nope. No performance origin apparently. Only performance.now
.
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.
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 thought I did, and dismissed it because it wasn't maintained for a long time, but I see the last release was March, so I may have mixed different libraries in my mind.
I will re-assess whether this library makes sense for what we want to do.
@@ -73,6 +74,11 @@ function PostDraft({ | |||
setCursorPosition(message.length); | |||
}, [channelId, rootId]); | |||
|
|||
useEffect(() => { |
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.
ok I understand.. now the next question is why do we consider the channel/thread switch complete when the post_draft renders ? out of curiosity my question, as I want to understand how did you come up with the fact that this is the last component being rendered on screen.
Also, if that is the case, is it still true on tablets ?
if (!team) { | ||
return; | ||
} | ||
if (selected) { | ||
return; | ||
} |
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 could be simplified as
if (!team) { | |
return; | |
} | |
if (selected) { | |
return; | |
} | |
if (!team || selected) { | |
return; | |
} |
this.ensureBatcher(serverUrl).addToBatch({ | ||
metric: metricName, | ||
value: (performanceNow - startTime) / 1000, | ||
timestamp: Math.round(startTime + performanceNowSkew), |
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.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. | ||
// See LICENSE.txt for license information. | ||
|
||
import {getTimeSinceStartup} from 'react-native-startup-time'; |
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 you can use https://github.com/oblador/react-native-performance/blob/master/packages/react-native-performance instead of startup-time, it has all you need for perf metrics as well as start end time but also you can have values for when the JS bundle starts and ends loading and a bunch of other metrics for RN.
more importantly it does already support the new RN arch for when we migrate.
version: '0.1.0', | ||
labels: { | ||
platform: Platform.select({ios: 'ios', default: 'android'}), | ||
agent: 'rnapp', |
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.
not for this PR, but something we should consider is to come up with a way to identify the type of device (High end, Mid, low end) as that will give us more insight with the collected numbers.
}; | ||
} | ||
|
||
private onAppStateChange(appState: AppStateStatus) { |
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 would agree with both of you.
- leave the logic when the app is sent to the background.
- move the event listener to the manager and call the sendBatch on each batcher from there.
Important to mention that any timer is paused when the app state changes to background
package.json
Outdated
@@ -87,6 +87,7 @@ | |||
"react-native-section-list-get-item-layout": "2.2.3", | |||
"react-native-shadow-2": "7.0.8", | |||
"react-native-share": "10.1.0", | |||
"react-native-startup-time": "2.1.0", |
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.
consider switching the library as suggested
Hey @larkox you sure this is ok? There seems to be a lot of unrelated changes |
@enahum A bad merge for some reason... will fix it. |
f1bb84c
to
a2f771f
Compare
@enahum Bad merge fixed |
|
||
PerformanceMetricsManager.startMetric('mobile_team_switch'); | ||
handleTeamChange(serverUrl, team.id); | ||
}, [selected]); |
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.
Doesn't this callback also need to depend on the server URL and team ID?
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 ot depends on the team and the team.id but the serverUrl can be omitted as I believe the component is remounted when switching servers
} | ||
|
||
private onAppStateChange(appState: AppStateStatus) { | ||
const isAppStateActive = appState === 'active'; |
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.
Similar to my comments on your PR about the websocket, I don't think we actually need to store the previous value of this if this is only called when the app state changes
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.
There are several "non active states", and this is executed when the state become non active. Calling several times forceSend
shouldn't have any effect, but still, there is no need to do so. So I think there is not a big issue to store the previous value, to only call this the first time we go to an "inactive state".
this.ensureBatcher(serverUrl).addToBatch({ | ||
metric: 'mobile_load', | ||
value: measure.duration, | ||
timestamp: Date.now(), |
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.
Now that we have that library, could thse use performance.timeOrigin + measure.startTime
?
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.
Agree
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 got many issues to make this sum up. This is the values I get from the library (on a real android device):
{"dateNow": 1718095572718, "dur": 401.2168229999952, "origin": 553634.5101, "startTime": 623363.507261, "sum": 1176998.017361, "timestamp": 1718095642446.9973}
The timestamp
one is calculated as stated here: https://github.com/oblador/react-native-performance/blob/master/packages/react-native-performance/README.md
So... the only value that makes sense to use here is Date.now
. The timestamp is also not so important, since it is only used for bucketing, and we are not going so fine grain down to seconds.
If you have any other idea, I am happy to try 😄
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.
Huh, I feel like that doesn't match how the browser API works, but if that's how they say to do it, that's fine by me
@@ -60,6 +61,7 @@ type PostProps = { | |||
post: PostModel; | |||
rootId?: string; | |||
previousPost?: PostModel; | |||
nextPost?: PostModel; |
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.
Perhaps you can just pass the boolean isLastPost instead of the entire model?
|
||
PerformanceMetricsManager.startMetric('mobile_team_switch'); | ||
handleTeamChange(serverUrl, team.id); | ||
}, [selected]); |
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 ot depends on the team and the team.id but the serverUrl can be omitted as I believe the component is remounted when switching servers
this.ensureBatcher(serverUrl).addToBatch({ | ||
metric: 'mobile_load', | ||
value: measure.duration, | ||
timestamp: Date.now(), |
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.
Agree
package.json
Outdated
@@ -199,6 +200,9 @@ | |||
"updatesnapshot": "jest --updateSnapshot" | |||
}, | |||
"overrides": { | |||
"react-native-startup-time": { |
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.
No need for this
@yasserfaraazkhan For now, the test for this is just a quick smoke test that nothing is broken. Specially on load, channel switch and team switch. |
not able to build the app locally on windows machine. Will test it in release build. |
Cherry pick is scheduled. |
* Add performance metrics to the app * add batcher and improve handling * Add tests * Fix test * Address feedback * Address feedback * Address feedback * update podfile (cherry picked from commit 5f01f9e)
* Add performance metrics to the app * add batcher and improve handling * Add tests * Fix test * Address feedback * Address feedback * Address feedback * update podfile (cherry picked from commit 5f01f9e) Co-authored-by: Daniel Espino García <larkox@gmail.com>
Summary
Add performance metrics to the mobile app based on this document: https://mattermost.atlassian.net/wiki/spaces/ICU/pages/2806808578/Metrics+definition
Ticket Link
Fix https://mattermost.atlassian.net/browse/MM-58140
Related PRs
Server: mattermost/mattermost#27045
Release Note