Performance metrics: Differentiate requests that are from page load (#24327)

* POC page_load_context header

* refactor to make this.pageLoadContext a private field, and relocate setTimeout code

* add pageLoadContext to `ObserveAPIEndpointDuration` method

* move to telemetry_actions

* move PageContext constant in webapp

* give server control of possible page_load_context values

* variable name change

---------

Co-authored-by: mickmister <mickmister>
Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Michael Kochell
2023-10-13 14:59:12 -04:00
коммит произвёл GitHub
родитель 72c75f01ce
Коммит e9da1ee8ca
8 изменённых файлов: 49 добавлений и 8 удалений

Просмотреть файл

@@ -407,8 +407,15 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != model.APIURLSuffix+"/websocket" {
elapsed := float64(time.Since(now)) / float64(time.Second)
pageLoadContext := r.Header.Get("X-Page-Load-Context")
if pageLoadContext != "page_load" && pageLoadContext != "reconnect" {
pageLoadContext = ""
}
originClient := string(originClient(r))
c.App.Metrics().ObserveAPIEndpointDuration(h.HandlerName, r.Method, statusCode, originClient, elapsed)
c.App.Metrics().ObserveAPIEndpointDuration(h.HandlerName, r.Method, statusCode, originClient, pageLoadContext, elapsed)
}
}
}

Просмотреть файл

@@ -58,7 +58,7 @@ type MetricsInterface interface {
IncrementFilesSearchCounter()
ObserveFilesSearchDuration(elapsed float64)
ObserveStoreMethodDuration(method, success string, elapsed float64)
ObserveAPIEndpointDuration(endpoint, method, statusCode, originClient string, elapsed float64)
ObserveAPIEndpointDuration(endpoint, method, statusCode, originClient, pageLoadContext string, elapsed float64)
IncrementPostIndexCounter()
IncrementFileIndexCounter()
IncrementUserIndexCounter()

Просмотреть файл

@@ -239,9 +239,9 @@ func (_m *MetricsInterface) IncrementWebsocketReconnectEvent(eventType string) {
_m.Called(eventType)
}
// ObserveAPIEndpointDuration provides a mock function with given fields: endpoint, method, statusCode, originClient, elapsed
func (_m *MetricsInterface) ObserveAPIEndpointDuration(endpoint string, method string, statusCode string, originClient string, elapsed float64) {
_m.Called(endpoint, method, statusCode, originClient, elapsed)
// ObserveAPIEndpointDuration provides a mock function with given fields: endpoint, method, statusCode, originClient, pageLoadContext, elapsed
func (_m *MetricsInterface) ObserveAPIEndpointDuration(endpoint string, method string, statusCode string, originClient string, pageLoadContext string, elapsed float64) {
_m.Called(endpoint, method, statusCode, originClient, pageLoadContext, elapsed)
}
// ObserveClusterRequestDuration provides a mock function with given fields: elapsed

Просмотреть файл

@@ -19,6 +19,8 @@ const SUPPORTS_MEASURE_METHODS = isSupported([
performance.clearMeasures,
]);
const HEADER_X_PAGE_LOAD_CONTEXT = 'X-Page-Load-Context';
export function isTelemetryEnabled(state) {
const config = getConfig(state);
return config.DiagnosticsEnabled === 'true';
@@ -262,3 +264,16 @@ function updateRequestCountAtMark(name) {
function getRequestCountAtMark(name) {
return requestCountAtMark[name] ?? 0;
}
/**
* This allows the server to know that a given HTTP request occurred during page load or reconnect.
* The server then uses this information to store metrics fields based on the request context.
* The setTimeout approach is a "best effort" approach that will produce false positives.
* A more accurate approach will result in more obtrusive code, which would add risk and maintenance cost.
*/
export const temporarilySetPageLoadContext = (pageLoadContext) => {
Client4.setHeader(HEADER_X_PAGE_LOAD_CONTEXT, pageLoadContext);
setTimeout(() => {
Client4.removeHeader(HEADER_X_PAGE_LOAD_CONTEXT);
}, 5000);
};

Просмотреть файл

@@ -118,9 +118,11 @@ import RemovedFromChannelModal from 'components/removed_from_channel_modal';
import WebSocketClient from 'client/web_websocket_client';
import {loadPlugin, loadPluginsIfNecessary, removePlugin} from 'plugins';
import {getHistory} from 'utils/browser_history';
import {ActionTypes, Constants, AnnouncementBarMessages, SocketEvents, UserStatuses, ModalIdentifiers, WarnMetricTypes} from 'utils/constants';
import {ActionTypes, Constants, AnnouncementBarMessages, SocketEvents, UserStatuses, ModalIdentifiers, WarnMetricTypes, PageLoadContext} from 'utils/constants';
import {getSiteURL} from 'utils/url';
import {temporarilySetPageLoadContext} from './telemetry_actions';
const dispatch = store.dispatch;
const getState = store.getState;
@@ -211,6 +213,8 @@ export function reconnect() {
// eslint-disable-next-line
console.log('Reconnecting WebSocket');
temporarilySetPageLoadContext(PageLoadContext.RECONNECT);
dispatch({
type: GeneralTypes.WEBSOCKET_SUCCESS,
timestamp: Date.now(),

Просмотреть файл

@@ -24,7 +24,7 @@ import type {ActionResult} from 'mattermost-redux/types/actions';
import {loadRecentlyUsedCustomEmojis} from 'actions/emoji_actions';
import * as GlobalActions from 'actions/global_actions';
import {measurePageLoadTelemetry, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx';
import {measurePageLoadTelemetry, temporarilySetPageLoadContext, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx';
import BrowserStore from 'stores/browser_store';
import store from 'stores/redux_store';
@@ -53,7 +53,7 @@ import webSocketClient from 'client/web_websocket_client';
import {initializePlugins} from 'plugins';
import Pluggable from 'plugins/pluggable';
import A11yController from 'utils/a11y_controller';
import {StoragePrefixes} from 'utils/constants';
import {PageLoadContext, StoragePrefixes} from 'utils/constants';
import {EmojiIndicesByAlias} from 'utils/emoji';
import {getSiteURL} from 'utils/url';
import * as UserAgent from 'utils/user_agent';
@@ -415,6 +415,8 @@ export default class Root extends React.PureComponent<Props, State> {
};
componentDidMount() {
temporarilySetPageLoadContext(PageLoadContext.PAGE_LOAD);
this.mounted = true;
this.initiateMeRequests();

Просмотреть файл

@@ -2162,4 +2162,9 @@ export const OverActiveUserLimits = {
MAX: 0.1,
} as const;
export const PageLoadContext = {
PAGE_LOAD: 'page_load',
RECONNECT: 'reconnect',
} as const;
export default Constants;

Просмотреть файл

@@ -216,6 +216,14 @@ export default class Client4 {
this.defaultHeaders['Accept-Language'] = locale;
}
setHeader(header: string, value: string) {
this.defaultHeaders[header] = value;
}
removeHeader(header: string) {
delete this.defaultHeaders[header];
}
setEnableLogging(enable: boolean) {
this.enableLogging = enable;
}