-
Notifications
You must be signed in to change notification settings - Fork 128
Add gzip compression for URL metrics using Compression Streams API #1959
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 gzip compression for URL metrics using Compression Streams API #1959
Conversation
Co-authored-by: Felix Arntz <[email protected]>
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## trunk #1959 +/- ##
==========================================
+ Coverage 72.34% 72.51% +0.17%
==========================================
Files 85 85
Lines 6979 7023 +44
==========================================
+ Hits 5049 5093 +44
Misses 1930 1930
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
I would like to understand more about adding a However, we're currently still supporting the At the moment, I’ve added a flag called cc: @westonruter |
/** | ||
* Timeout ID for debouncing URL metric compression. | ||
* | ||
* @type {ReturnType<typeof setTimeout> | null} |
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 this any different than the following?
* @type {ReturnType<typeof setTimeout> | null} | |
* @type {number|null} |
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 its different TypeScript throws as error if we only put number
Type 'Timeout' is not assignable to type 'number'
recompressionTimeout = setTimeout( async () => {
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.
Ah, good to know.
@@ -660,6 +724,10 @@ export default async function detect( { | |||
initializingExtensionModuleUrls.push( extensionModuleUrl ); | |||
} | |||
} | |||
|
|||
if ( extension.finalize instanceof Function ) { | |||
compressionEnabled = false; |
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 better to have a separate variable like extensionHasFinalize
which is initially false
but then is set to true
here. Then after the loop finishes, if extensionHasFinalize
is true
, it can at that point set compressionEnabled
to false
and at the same time emit a warning to say that URL Metric compression is disabled due to the use of the deprecated finalize
function.
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.
Done in 7e8ec60.
recompressionTimeout = setTimeout( async () => { | ||
if ( typeof requestIdleCallback === 'function' ) { | ||
await new Promise( ( resolve ) => { | ||
requestIdleCallback( resolve ); |
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 requestIdleCallback()
returns a handle value which can be passed to cancelIdleCallback()
. I think this should be done as well in addition to clearing the debouncing timeout, as otherwise it could happen that two idle callbacks for re-compressing the URL Metric could happen right after each other.
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.
Done in aeda409.
@b1ink0 I think that comment I made is obsolete. We should prevent compression from happening if there is a |
Co-authored-by: Weston Ruter <[email protected]>
…tions Co-authored-by: Weston Ruter <[email protected]>
…alize` function Co-authored-by: Weston Ruter <[email protected]>
} ); | ||
idleCallbackHandle = null; | ||
} | ||
compressedPayload = await compress( JSON.stringify( urlMetric ) ); |
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.
Just in case this throws an error, perhaps there should be a try
/catch
block around this which when an error is caught the error is logged out to the console and then compressionEnabled
is set to false
.
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.
For logging the error, should console.log
be used since debouncedCompressUrlMetric
function is defined outside the detect
function and therefore doesn't have access to the custom logger? Also, if we want to use the custom logger, then we would need to pass it through multiple functions, as debouncedCompressUrlMetric
is called inside both extendElementData
and extendRootData
. Another option could be to define the logger
constant as a let
outside the detect
function which could let us use the custom logger.
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 function could also just create a new logger which is only used here for logging out the error.
Another option would be to re-throw the error after setting compressionEnabled
to false
. But better to log it.
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 function could also just create a new logger which is only used here for logging out the error.
So should the debugMode
param should be set to true
regardless of the isDebug
passed to detect
function? One other thing creating a logger for each call to debouncedCompressUrlMetric
does not seem correct to me, should there be a new variable outside function like compressionLogger
?
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 logger would only be created in the catch
block and then it would never be created again since upon an error compressionEnabled
would be set to false
and the function would short-circuit. So that seems fine.
And the debugMode
param shouldn't matter since the error
function logs out unconditionally.
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.
Ohh, It makes sense thank you!
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.
Done in 6c8c257
Co-authored-by: Weston Ruter <[email protected]>
Co-authored-by: Weston Ruter <[email protected]>
compressionEnabled = false; | ||
} | ||
recompressionTimeout = null; | ||
}, 1000 ); |
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.
Minor point: let's put this 1000
in a constant defined up near the top of this module so we can more easily understand what the magic number is. For example, compressionDebounceWaitDuration
.
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.
Done in dc4d5e9
Co-authored-by: Weston Ruter <[email protected]>
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
This comment was marked as spam.
This comment was marked as spam.
… add/reintroduce-url-metrics-gzip-compression
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.
Thanks so much for bringing this back to life!
@@ -718,12 +908,16 @@ export default async function detect( { | |||
urlMetric.elements.push( elementData ); | |||
elementsByXPath.set( elementData.xpath, elementData ); | |||
} | |||
breadcrumbedElementsMap.clear(); |
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.
Oops. This duplicate call (which I added) I've removed in #1968
Summary
Fixes #1893
Relevant technical choices
This PR implements the Compression Streams API to efficiently compress URL Metrics JSON before sending it. This significantly reduces the payload size, allowing more data to be sent without exceeding the 64 KiB limit.
Previously some of these changes where reverted due to the bug #1928.
This PR is able to avoid the issue of URL metrics not being sent on
pagehide
event by compressing the URL metrics beforepagehide
event and then recompressing it whenever URL metrics are updated by the extensions.