Performance issue in @tanstack/query-async-storage-persister (and possibly #updateStaleTimeout) #6489
Description
Describe the bug
It looks like delayFunc in the packages/query-async-storage-persister/src/asyncThrottle.ts
has some performance problem.
const delayFunc = async () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
if (running) {
delayFunc() // Will come here when 'func' execution time is greater than the interval.
} else {
execFunc()
}
}, interval)
}
this is the 4x slow-down profile, each refetch
call causes persistQueryClientSave
to be called, and then delayFunc
is the slowest function in the tree. All small yellow bars are setTimeout
or clearTimeout
calls.
My throttle timeout is the default (1e3), therefore it cannot be caused by execFunc
(all those delayFunc
calls happen within 1 second).
I try to patch my node_modules and replace the asyncThrottle
with this:
function asyncThrottle2(func, { interval = 1e3, onError = noop } = {}) {
if (typeof func !== 'function') throw new Error('argument is not function.')
let currentArgs = null
setInterval(() => {
if (!currentArgs) return
Promise.resolve(func(...currentArgs)).catch(onError)
currentArgs = null
}, interval)
return (...args) => {
currentArgs = args
}
}
export { asyncThrottle2 as asyncThrottle }
This implementation is incorrect but fast.
After the patch, persistQueryClientSave
now only costs 1 ms instead of 57 ms.
I think it looks like calling setTimeout
or clearTimeout
is costly. I think a better async throttle implementation can avoid calling them so often.
btw I also observed that #updateStaleTimeout
method has a similar behavior.
also, this method behaves like delayFunc
, simple but costs a lot of time (cancel & create timer).
Your minimal, reproducible example
n/a
Steps to reproduce
Sorry I didn't provide one, but my patch works so I think my analysis of the performance problem is correct.
I'm happy to try any new asyncThrottle implementation (by patch node_modules, so maintainers don't have to release a new version for this) in our project to see if that fixes the performance problem. Thanks!
Expected behavior
No performance problem
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Chrome 119
@tanstack/query-async-storage-persister 5.8.7
Tanstack Query adapter
None
TanStack Query version
5.8.7
TypeScript version
No response
Additional context
No response