-
Notifications
You must be signed in to change notification settings - Fork 128
Fix #3673, instrument response times in shot creation flow #3727
Conversation
addon/bootstrap.js
Outdated
} else if (msg.funcName === "incrementUploadCount") { | ||
Services.telemetry.scalarAdd('screenshots.upload', 1); | ||
sendReply({type: "success", value: true}); |
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.
The changes in this file are the patch from #3726
a2b7d10
to
fc1853f
Compare
It occurred to me in the shower this morning that it might be nicer to keep the times inside the uicontrol state object, rather than overload sendEvent to turn it into a quasi-messaging broker with the internal-only events and separate time tracking. Might be something to fix in a refactoring bug later? |
This is important to land for our OKR :) |
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.
The matching approach seems reasonable, and I like that it uses our existing events for most moments in time. However, I don't think we should submit these as normal events. Google Analytics has a system specifically for these timings: https://github.com/peaksandpies/universal-analytics#user-timing-tracking
We'd have to reintroduce the server /timing
endpoint (it was removed in #3254 – it might have been slightly broken at the time), and then add a sendTiming
function instead of overloading sendEvent
.
@ianb cool, restored |
ecb9877
to
f6b29d6
Compare
let matched = false; | ||
filters.forEach(filter => { | ||
matched = matched || match(filter, action, label); | ||
}); |
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.
Nitpick: since we are only interested in the first matching filter, we can use array.find()
here.
userTimingLabel: bodyObj.timingLabel | ||
}; | ||
userAnalytics.timing(params).send(); | ||
simpleResponse(res, "OK", 200); |
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.
Previously /timing
allowed batching. That would make the code on the addon side a little more complicated though. Perhaps keep the completed timing data in one object and the in progress ones in another, so whenever teh in progress list is empty and there are completed timing measurements, send those to /timing
?
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 noticed that too. Since sendEvent
fires many more pings, and fires each one individually, it didn't seem worth bothering with the extra state management.
If there's a good reason to batch events in general, maybe we can adjust both endpoints in a followup bug?
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 made a followup in #3757
👍 |
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.
Several comments
let timingCategory = "addon"; | ||
let url = main.getBackend() + "/timing"; | ||
let req = new XMLHttpRequest(); | ||
req.open("POST", url); |
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 should probably start using fetch()
(though I also keep falling back to XHR 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.
sure, already filed as #2261
exc.status = req.status; | ||
exc.statusText = req.statusText; | ||
console.error(exc); | ||
// TODO: send the error to sentry, maybe? |
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 should reject a promise. Also it should create a promise! Same as sendEvent. I'm not sure how consistently we watch the sendEvent promise, arguably we should have a wrapped version of these functions for the not-uncommon case where we want to send-and-forget (with catcher.watchFunction(sendTiming, true)
)
@@ -446,6 +448,7 @@ this.ui = (function() { // eslint-disable-line no-unused-vars | |||
|
|||
unhide() { | |||
this.element.style.display = ""; | |||
catcher.watchPromise(callBackground("sendEvent", "internal", "unhide-preview-frame")); |
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 a little concerned that callBackground is going to confound the timing measurements a little. But... eh?
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, the time to send a message seems pretty small, <=10ms (for short messages), and some of these steps take several seconds, so it seemed fine for now.
docs/METRICS.md
Outdated
|
||
Internal-only events are used to measure the time from user input to user-visible UI response. | ||
|
||
*NOTE: Internal-only events are not submitted to GA.* |
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.
Probably these don't even need to be documented, but... well, no harm.
docs/METRICS.md
Outdated
|
||
##### First step: starting the shot | ||
|
||
1. [x] Time from clicking the page action (or toolbar button) to displaying the preselection iframe, `addon/perf-response-time/page-action` with `cd1: {ms response 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 these need to be adjusted to reflect they aren't events, but timings.
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.
One note, which you could resolve or not, eh. Otherwise 👍 – want to squash and merge?
let req = new XMLHttpRequest(); | ||
req.open("POST", url); | ||
req.setRequestHeader("content-type", "application/json"); | ||
req.onload = catcher.watchFunction(() => { |
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.
Technically I don't think this will reject the promise, if there's an exception in the function then the promise will just hang. But it will report something to Sentry.
The metrics docs changes cover the basic overview.
The iframe unhide events seem to fire slightly before the UI is updated, but given how horrifyingly slow some of the steps are (especially full-page previews & downloads), I suspect these start and end points will be good enough for a while.
Easiest way to test this is to filter on 'perf' in the browser console, then run through various shot creation + download/upload flows: