Skip to content

RUM-5843 Introduce the RumMonitor#addViewLoadingTime API#2243

Merged
mariusc83 merged 5 commits into
developfrom
mconstantin/rum-5843/implement-addviewloadingtime-api
Sep 13, 2024
Merged

RUM-5843 Introduce the RumMonitor#addViewLoadingTime API#2243
mariusc83 merged 5 commits into
developfrom
mconstantin/rum-5843/implement-addviewloadingtime-api

Conversation

@mariusc83

Copy link
Copy Markdown
Member

What does this PR do?

A brief description of the change being made with this pull request.

Motivation

What inspired you to submit this pull request?

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@mariusc83 mariusc83 self-assigned this Sep 5, 2024
@mariusc83
mariusc83 force-pushed the mconstantin/rum-5843/implement-addviewloadingtime-api branch 2 times, most recently from 8857cb3 to 51443c5 Compare September 5, 2024 15:18
@codecov-commenter

codecov-commenter commented Sep 5, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 70.09%. Comparing base (d059818) to head (02d3565).
Report is 7 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2243      +/-   ##
===========================================
+ Coverage    70.05%   70.09%   +0.04%     
===========================================
  Files          730      730              
  Lines        27189    27185       -4     
  Branches      4590     4594       +4     
===========================================
+ Hits         19045    19053       +8     
+ Misses        6842     6838       -4     
+ Partials      1302     1294       -8     
Files with missing lines Coverage Δ
.../main/kotlin/com/datadog/android/rum/RumMonitor.kt 28.57% <ø> (ø)
...g/android/rum/internal/domain/scope/RumRawEvent.kt 100.00% <100.00%> (ø)
.../android/rum/internal/domain/scope/RumViewScope.kt 94.63% <100.00%> (-0.65%) ⬇️
.../android/rum/internal/monitor/DatadogRumMonitor.kt 87.32% <100.00%> (-0.25%) ⬇️

... and 30 files with indirect coverage changes

@mariusc83
mariusc83 marked this pull request as ready for review September 6, 2024 06:57
@mariusc83
mariusc83 requested review from a team as code owners September 6, 2024 06:57

/**
* Adds view loading time to current RUM view based on the time elapsed since the view was started.
* This method should be called only once per view.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we enforce it requirement on the client side? Cannot we just check in the implementation that if it was called already for the view, then ignore subsequent calls?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we follow same approach as on iOS and for now on their end they allow it to be called multiple times. @ganeshnj maybe you want to add some thoughts here ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

I agree with @0xnm , we can't ask the customer to know whether or not the method has already been called. They could have several code paths that would lead to calling this method, and it would be a hassle to ask them to track whether or not that method have been called for a given View.
We need to make our SDK resilient enough to handle "misuse".


// Then
argumentCaptor<ViewEvent> {
verify(mockWriter, times(viewLoadingTimeEvents.size))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why should we allow multiple calls?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same answer as above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Not sure what the RFC stated, but imo the first loading time is the one that should be kept, otherwise, if the same screen is updated/refreshed, we would report a longer loading time than what the end user experienced.

datadogBenchmark.stop()
}

@OptIn(ExperimentalRumApi::class)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it is not needed here? I don't see any addViewLoadingTime call here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup a left - over from previous tries :(

@OptIn(ExperimentalRumApi::class)
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_RESUME) {
GlobalRumMonitor.get().addViewLoadingTime()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't we have multiple calls for the same view here when application goes info background and then back into foreground?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the app goes into background there will be a new view...we stop the view on the onPause the way I remember.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, here view tracking relies on navigation - I don't think navigation events are fired when app changes the state (but I may be wrong). While addViewLoadingTime call here relies not on the navigation, but on the lifecycle events.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me double check this, you might be right, I was assuming we rely on the view lifecycle here

@OptIn(ExperimentalRumApi::class)
override fun onResume() {
super.onResume()
GlobalRumMonitor.get().addViewLoadingTime()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same: I guess we can have multiple calls for the same view?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

@OptIn(ExperimentalRumApi::class)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
GlobalRumMonitor.get().addViewLoadingTime()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onResume will be called right after onViewCreated effectively discarding this call

object : ImageLoadedCallback {
override fun onImageLoaded(resource: Drawable) {
imageViewRemote.setImageDrawable(resource)
if (imageLoadedCounter.decrementAndGet() == 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we extract this into a common private method? same snippet is used 4 times.

)

@Volatile
private var pageWasLoaded: Boolean = false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private var pageWasLoaded: Boolean = false
private var pageIsLoaded: Boolean = false

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that the WebView callbacks are being executed on a different thread this is the reason why I want to keep it as @volatile

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my suggestion is not about volatile, but about naming

private lateinit var webView: WebView

@Volatile
private var pageWasLoaded: Boolean = false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private var pageWasLoaded: Boolean = false
private var pageIsLoaded: Boolean = false

)

@Volatile
private var pageWasLoaded: Boolean = false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private var pageWasLoaded: Boolean = false
private var pageIsLoaded: Boolean = false

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

@mariusc83
mariusc83 force-pushed the mconstantin/rum-5843/implement-addviewloadingtime-api branch from 51443c5 to fe279fd Compare September 6, 2024 08:54
Comment on lines +329 to +330
* If the view is not started, this method does nothing.
* If the view is not active(stopped), this method does nothing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Since the loading time is applied to the current RUM view (maybe should be the current active RUM View), those two lines are misleading IMO. It feels like we make the assumption that a view is necessarily an Activity/Fragment with a lifecycle (start, resume, …), which is not always the case.

Suggested change
* If the view is not started, this method does nothing.
* If the view is not active(stopped), this method does nothing.
* If no view is started or active, this method does nothing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am following the same docs as in iOS


/**
* Adds view loading time to current RUM view based on the time elapsed since the view was started.
* This method should be called only once per view.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

I agree with @0xnm , we can't ask the customer to know whether or not the method has already been called. They could have several code paths that would lead to calling this method, and it would be a hassle to ask them to track whether or not that method have been called for a given View.
We need to make our SDK resilient enough to handle "misuse".


// Then
argumentCaptor<ViewEvent> {
verify(mockWriter, times(viewLoadingTimeEvents.size))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Not sure what the RFC stated, but imo the first loading time is the one that should be kept, otherwise, if the same screen is updated/refreshed, we would report a longer loading time than what the end user experienced.


@OptIn(ExperimentalRumApi::class)
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_RESUME && !viewIsLoaded) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Is this sample the kind of use case we want to showcase ? Tracking the onResume time is not relevant most of the time. We should probably instead only do what you added in the DataListViewModel where we actually load something from the network, which would be a better example of how to use this new API.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason I cannot reply to your comment one by one @xgouchet. Regarding the addViewLoadingTime being called multiple times the whole approach was discussed and agreed in the RFC.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding your last comment, that is a nice to have in the Sample app, my intention was to add the viewLoadingTime for most of the views there. In that case I will remove the call for this view and maybe add a task to enrich the sample app. This is not the scope for this PR. @0xnm what's your opinion on that ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the nature of the screen, some screens may not load anything from the network/disk. But maybe to highlight that this method shouldn't be blindly used in the onResume we may want indeed to have it called only in the data stream.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the same time we could agree that there will be use cases on the customer's end where simple static views like these ones would have to be measured. In that case we will remove these use - cases from the sample app and only maintain the ones that imply an async data ?

@mariusc83
mariusc83 force-pushed the mconstantin/rum-5843/implement-addviewloadingtime-api branch 2 times, most recently from 2b818a3 to 13199cd Compare September 9, 2024 13:20
@mariusc83
mariusc83 requested review from 0xnm and xgouchet September 9, 2024 14:22
@mariusc83
mariusc83 force-pushed the mconstantin/rum-5843/implement-addviewloadingtime-api branch from 13199cd to 61ca355 Compare September 10, 2024 08:06
startCustomRumViewButton.setOnClickListener {
GlobalRumMonitor.get().startView(this, "Custom RUM View")
}
// add webview on load listener

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Is this comment needed here ? Feels like its a TODO that didn't get removed

@mariusc83
mariusc83 force-pushed the mconstantin/rum-5843/implement-addviewloadingtime-api branch from 61ca355 to 02d3565 Compare September 13, 2024 08:53
@mariusc83
mariusc83 merged commit febadea into develop Sep 13, 2024
@mariusc83
mariusc83 deleted the mconstantin/rum-5843/implement-addviewloadingtime-api branch September 13, 2024 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants