T
TanStack12mo ago
sunny-green

Measuring the total time of all queries

I'm looking to profile an app that uses react query. I want to send a report to our server whenever the app is closed, which will record the time the app was open and the time that was taken waiting for requests to finish. Is there an easy way of going about this with react query? Note that if requests run concurrently I do not want the fetch times to be added up Thanks
3 Replies
sunny-green
sunny-greenOP12mo ago
This seems tough to do with the tools tanstack query provides. I'm using a wrapper around fetch that adds auth headers, so I think I will just record spans there, store them in a react context, merge overlapping spans and then report those Wrapping around fetch isn't super nice, so still open to suggestions on how to do this through react query Current approach is to transparently wrap query/mutation options:
export function useTrackedSessionInstrumentation(name: string, metadata: Record<string, string>) {
const trackedSession = useTrackedSession();
return useMemo(
() => ({
instrumentQueryOptions: <TQueryFnData, TError, TData, TQueryKey extends QueryKey, TPageParam>(
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
): QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> => {
const { queryFn } = options;

if (typeof queryFn !== 'function') {
return options;
}

return {
...options,
queryFn: async (...args) => {
const span: TrackedSessionSpan = {
name,
start: new Date(),
end: null,
metadata,
};

try {
return await queryFn(...args);
} finally {
span.end = new Date();
trackedSession?.spans.push(span);
}
},
};
},
instrumentMutationOptions: <TData, TError, TVariables, TContext>(
options: MutationOptions<TData, TError, TVariables, TContext>,
): MutationOptions<TData, TError, TVariables, TContext> => {
const { mutationFn } = options;

if (typeof mutationFn !== 'function') {
return options;
}

return {
...options,
mutationFn: async (...args) => {
const span: TrackedSessionSpan = {
name,
start: new Date(),
end: null,
metadata,
};

try {
return await mutationFn(...args);
} finally {
span.end = new Date();
trackedSession?.spans.push(span);
}
},
};
},
}),
[trackedSession, name, metadata],
);
}
export function useTrackedSessionInstrumentation(name: string, metadata: Record<string, string>) {
const trackedSession = useTrackedSession();
return useMemo(
() => ({
instrumentQueryOptions: <TQueryFnData, TError, TData, TQueryKey extends QueryKey, TPageParam>(
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
): QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> => {
const { queryFn } = options;

if (typeof queryFn !== 'function') {
return options;
}

return {
...options,
queryFn: async (...args) => {
const span: TrackedSessionSpan = {
name,
start: new Date(),
end: null,
metadata,
};

try {
return await queryFn(...args);
} finally {
span.end = new Date();
trackedSession?.spans.push(span);
}
},
};
},
instrumentMutationOptions: <TData, TError, TVariables, TContext>(
options: MutationOptions<TData, TError, TVariables, TContext>,
): MutationOptions<TData, TError, TVariables, TContext> => {
const { mutationFn } = options;

if (typeof mutationFn !== 'function') {
return options;
}

return {
...options,
mutationFn: async (...args) => {
const span: TrackedSessionSpan = {
name,
start: new Date(),
end: null,
metadata,
};

try {
return await mutationFn(...args);
} finally {
span.end = new Date();
trackedSession?.spans.push(span);
}
},
};
},
}),
[trackedSession, name, metadata],
);
}
metropolitan-bronze
metropolitan-bronze12mo ago
Query isn't necessarily just network calls. So I wouldn't think this is it's responsibility. You could use a service worker for this. Intercept all requests, measure them and return them. Seems like a good use case.
sunny-green
sunny-greenOP12mo ago
In my case it is just network calls. Service worker isn't possible because my code is running inside of a sandbox/very restricted runtime

Did you find this page helpful?