[search] Add Vue search performance instrumentation

This is almost identical to the instrumentation currently used in
production for mediawiki.searchSuggest -- the only differences being in
the nomenclature of variables, etc.

As part of comparing Vue search with legacy search, we need to track how
long it takes a keypress to load and render search results for Vue
search. This will only be used only in synthetic testing at this time
(Real user monitoring (RUM) is not in scope for this ticket).

To test locally, first enter characters in input. Then to see the
metrics recorded:

```
// View all marks
performance.getEntriesByType('mark');

// View all measures
performance.getEntriesByType('measure');
```

This commit adds the following metrics which will only be used in our
synthetic tests. We are not collecting RUM metrics at this time.

Measures:

* mwVectorVueSearchLoadStartToFirstRender: Measures the time it takes
from the start of loading the search module to the first render of results.

* mwVectorVueSearchQueryToRender: Measures the time it takes from
the start of the fetch to the render of search results.

Bug: T251544
Change-Id: I39200648a3a0a4079a132134d142ad8997c8962a
This commit is contained in:
Nicholas Ray 2020-12-01 15:41:44 -07:00 committed by Jdlrobson
parent aa10668e6d
commit 3aaedcfe3c
2 changed files with 63 additions and 1 deletions

View file

@ -16,6 +16,7 @@
:search-language="language" :search-language="language"
:show-thumbnail="showThumbnail" :show-thumbnail="showThumbnail"
:show-description="showDescription" :show-description="showDescription"
@fetch-start="instrumentation.onFetchStart"
@fetch-end="instrumentation.onFetchEnd" @fetch-end="instrumentation.onFetchEnd"
@suggestion-click="instrumentation.onSuggestionClick" @suggestion-click="instrumentation.onSuggestionClick"
> >

View file

@ -8,7 +8,37 @@
* @see https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/skins/Vector/+/refs/heads/master/includes/Constants.php * @see https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/skins/Vector/+/refs/heads/master/includes/Constants.php
*/ */
var INPUT_LOCATION_MOVED = 'header-moved', var INPUT_LOCATION_MOVED = 'header-moved',
wgScript = mw.config.get( 'wgScript' ); wgScript = mw.config.get( 'wgScript' ),
// T251544: Collect search performance metrics to compare Vue search with
// mediawiki.searchSuggest performance. Marks and Measures will only be
// recorded on the Vector skin and only if browser supported.
shouldTestSearchPerformance = !!( window.performance &&
window.requestAnimationFrame &&
performance.mark &&
performance.measure &&
performance.getEntriesByName &&
performance.clearMarks ),
loadStartMark = 'mwVectorVueSearchLoadStart',
queryMark = 'mwVectorVueSearchQuery',
renderMark = 'mwVectorVueSearchRender',
queryToRenderMeasure = 'mwVectorVueSearchQueryToRender',
loadStartToFirstRenderMeasure = 'mwVectorVueSearchLoadStartToFirstRender';
function onFetchStart() {
if ( !shouldTestSearchPerformance ) {
return;
}
// Clear past marks that are no longer relevant. This likely means that the
// search request failed or was cancelled. Whatever the reason, the mark
// is no longer needed since we are only interested in collecting the time
// from query to render.
if ( performance.getEntriesByName( queryMark ).length ) {
performance.clearMarks( queryMark );
}
performance.mark( queryMark );
}
/** /**
* @param {FetchEndEvent} event * @param {FetchEndEvent} event
@ -22,6 +52,36 @@ function onFetchEnd( event ) {
query: event.query, query: event.query,
inputLocation: INPUT_LOCATION_MOVED inputLocation: INPUT_LOCATION_MOVED
} ); } );
if ( shouldTestSearchPerformance ) {
// Schedule the mark after the search results have rendered and are
// visible to the user. Two rAF's are needed for this since rAF will
// execute before the rendering steps happen (e.g. layout and paint). A
// nested rAF will execute after these rendering steps have completed
// and ensure the search results are visible to the user.
requestAnimationFrame( function () {
requestAnimationFrame( function () {
if ( !performance.getEntriesByName( queryMark ).length ) {
return;
}
performance.mark( renderMark );
performance.measure( queryToRenderMeasure, queryMark, renderMark );
// Measure from the start of the lazy load to the first render if we
// haven't already captured that info.
if ( performance.getEntriesByName( loadStartMark ).length &&
!performance.getEntriesByName( loadStartToFirstRenderMeasure ).length ) {
performance.measure( loadStartToFirstRenderMeasure, loadStartMark, renderMark );
}
// The measures are the most meaningful info so we remove the marks
// after we have the measure.
performance.clearMarks( queryMark );
performance.clearMarks( renderMark );
} );
} );
}
} }
/** /**
@ -91,6 +151,7 @@ function generateUrl( suggestion, meta ) {
module.exports = { module.exports = {
listeners: { listeners: {
onFetchStart: onFetchStart,
onFetchEnd: onFetchEnd, onFetchEnd: onFetchEnd,
onSuggestionClick: onSuggestionClick, onSuggestionClick: onSuggestionClick,