diff --git a/packages/core/src/js/profiling/cache.ts b/packages/core/src/js/profiling/cache.ts index 47126b4bd..e6496db43 100644 --- a/packages/core/src/js/profiling/cache.ts +++ b/packages/core/src/js/profiling/cache.ts @@ -1,5 +1,72 @@ -import { makeFifoCache } from '@sentry/core'; - import type { AndroidCombinedProfileEvent, CombinedProfileEvent } from './types'; export const PROFILE_QUEUE = makeFifoCache(20); + +/** + * Creates a cache that evicts keys in fifo order + * @param size {Number} + */ +function makeFifoCache( + size: number, +): { + get: (key: Key) => Value | undefined; + add: (key: Key, value: Value) => void; + delete: (key: Key) => boolean; + clear: () => void; + size: () => number; +} { + // Maintain a fifo queue of keys, we cannot rely on Object.keys as the browser may not support it. + let evictionOrder: Key[] = []; + let cache: Record = {}; + + return { + add(key: Key, value: Value) { + while (evictionOrder.length >= size) { + // shift is O(n) but this is small size and only happens if we are + // exceeding the cache size so it should be fine. + const evictCandidate = evictionOrder.shift(); + + if (evictCandidate !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete cache[evictCandidate]; + } + } + + // in case we have a collision, delete the old key. + if (cache[key]) { + this.delete(key); + } + + evictionOrder.push(key); + cache[key] = value; + }, + clear() { + cache = {}; + evictionOrder = []; + }, + get(key: Key): Value | undefined { + return cache[key]; + }, + size() { + return evictionOrder.length; + }, + // Delete cache key and return true if it existed, false otherwise. + delete(key: Key): boolean { + if (!cache[key]) { + return false; + } + + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete cache[key]; + + for (let i = 0; i < evictionOrder.length; i++) { + if (evictionOrder[i] === key) { + evictionOrder.splice(i, 1); + break; + } + } + + return true; + }, + }; +}