From a72220c3d8e522c61918d59f78cf91d116f291d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Tue, 24 Mar 2026 13:30:17 +0100 Subject: [PATCH 1/4] chore: Remove unusued performance metrics system --- lib/GlobalSettings.ts | 32 ------------ lib/Onyx.ts | 28 ---------- lib/OnyxUtils.ts | 50 ------------------ lib/dependencies/ModuleProxy.ts | 39 -------------- .../PerformanceProxy/index.native.ts | 12 ----- lib/dependencies/PerformanceProxy/index.ts | 2 - lib/metrics.ts | 51 ------------------- lib/storage/index.ts | 22 -------- lib/types.ts | 6 --- lib/useOnyx.ts | 12 +---- package.json | 7 +-- 11 files changed, 2 insertions(+), 259 deletions(-) delete mode 100644 lib/GlobalSettings.ts delete mode 100644 lib/dependencies/ModuleProxy.ts delete mode 100644 lib/dependencies/PerformanceProxy/index.native.ts delete mode 100644 lib/dependencies/PerformanceProxy/index.ts delete mode 100644 lib/metrics.ts diff --git a/lib/GlobalSettings.ts b/lib/GlobalSettings.ts deleted file mode 100644 index 5b3aec1f2..000000000 --- a/lib/GlobalSettings.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Stores settings from Onyx.init globally so they can be made accessible by other parts of the library. - */ - -const globalSettings = { - enablePerformanceMetrics: false, -}; - -type GlobalSettings = typeof globalSettings; - -const listeners = new Set<(settings: GlobalSettings) => unknown>(); -function addGlobalSettingsChangeListener(listener: (settings: GlobalSettings) => unknown) { - listeners.add(listener); - return () => { - listeners.delete(listener); - }; -} - -function notifyListeners() { - for (const listener of listeners) listener(globalSettings); -} - -function setPerformanceMetricsEnabled(enabled: boolean) { - globalSettings.enablePerformanceMetrics = enabled; - notifyListeners(); -} - -function isPerformanceMetricsEnabled() { - return globalSettings.enablePerformanceMetrics; -} - -export {setPerformanceMetricsEnabled, isPerformanceMetricsEnabled, addGlobalSettingsChangeListener}; diff --git a/lib/Onyx.ts b/lib/Onyx.ts index 4a3f26317..439edeff8 100644 --- a/lib/Onyx.ts +++ b/lib/Onyx.ts @@ -26,8 +26,6 @@ import OnyxUtils from './OnyxUtils'; import logMessages from './logMessages'; import type {Connection} from './OnyxConnectionManager'; import connectionManager from './OnyxConnectionManager'; -import * as GlobalSettings from './GlobalSettings'; -import decorateWithMetrics from './metrics'; import OnyxMerge from './OnyxMerge'; /** Initialize the store with actions and listening for storage events */ @@ -37,17 +35,11 @@ function init({ evictableKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = !!global.localStorage, - enablePerformanceMetrics = false, enableDevTools = true, skippableCollectionMemberIDs = [], ramOnlyKeys = [], snapshotMergeKeys = [], }: InitOptions): void { - if (enablePerformanceMetrics) { - GlobalSettings.setPerformanceMetricsEnabled(true); - applyDecorators(); - } - initDevTools(enableDevTools); Storage.init(); @@ -604,25 +596,5 @@ const Onyx = { registerLogger: Logger.registerLogger, }; -function applyDecorators() { - // We are reassigning the functions directly so that internal function calls are also decorated - // @ts-expect-error Reassign - connect = decorateWithMetrics(connect, 'Onyx.connect'); - // @ts-expect-error Reassign - connectWithoutView = decorateWithMetrics(connectWithoutView, 'Onyx.connectWithoutView'); - // @ts-expect-error Reassign - set = decorateWithMetrics(set, 'Onyx.set'); - // @ts-expect-error Reassign - multiSet = decorateWithMetrics(multiSet, 'Onyx.multiSet'); - // @ts-expect-error Reassign - merge = decorateWithMetrics(merge, 'Onyx.merge'); - // @ts-expect-error Reassign - mergeCollection = decorateWithMetrics(mergeCollection, 'Onyx.mergeCollection'); - // @ts-expect-error Reassign - update = decorateWithMetrics(update, 'Onyx.update'); - // @ts-expect-error Reassign - clear = decorateWithMetrics(clear, 'Onyx.clear'); -} - export default Onyx; export type {OnyxUpdate, ConnectOptions, SetOptions}; diff --git a/lib/OnyxUtils.ts b/lib/OnyxUtils.ts index b160e1034..02e5b1ea4 100644 --- a/lib/OnyxUtils.ts +++ b/lib/OnyxUtils.ts @@ -36,8 +36,6 @@ import type {FastMergeOptions, FastMergeResult} from './utils'; import utils from './utils'; import type {DeferredTask} from './createDeferredTask'; import createDeferredTask from './createDeferredTask'; -import * as GlobalSettings from './GlobalSettings'; -import decorateWithMetrics from './metrics'; import type {StorageKeyValuePair} from './storage/providers/types'; import logMessages from './logMessages'; @@ -1906,54 +1904,6 @@ const OnyxUtils = { isRamOnlyKey, }; -GlobalSettings.addGlobalSettingsChangeListener(({enablePerformanceMetrics}) => { - if (!enablePerformanceMetrics) { - return; - } - // We are reassigning the functions directly so that internal function calls are also decorated - - // @ts-expect-error Reassign - initStoreValues = decorateWithMetrics(initStoreValues, 'OnyxUtils.initStoreValues'); - // @ts-expect-error Complex type signature - get = decorateWithMetrics(get, 'OnyxUtils.get'); - // @ts-expect-error Reassign - getAllKeys = decorateWithMetrics(getAllKeys, 'OnyxUtils.getAllKeys'); - // @ts-expect-error Reassign - getCollectionKeys = decorateWithMetrics(getCollectionKeys, 'OnyxUtils.getCollectionKeys'); - // @ts-expect-error Reassign - keysChanged = decorateWithMetrics(keysChanged, 'OnyxUtils.keysChanged'); - // @ts-expect-error Reassign - keyChanged = decorateWithMetrics(keyChanged, 'OnyxUtils.keyChanged'); - // @ts-expect-error Reassign - sendDataToConnection = decorateWithMetrics(sendDataToConnection, 'OnyxUtils.sendDataToConnection'); - // @ts-expect-error Reassign - scheduleSubscriberUpdate = decorateWithMetrics(scheduleSubscriberUpdate, 'OnyxUtils.scheduleSubscriberUpdate'); - // @ts-expect-error Reassign - scheduleNotifyCollectionSubscribers = decorateWithMetrics(scheduleNotifyCollectionSubscribers, 'OnyxUtils.scheduleNotifyCollectionSubscribers'); - // @ts-expect-error Reassign - remove = decorateWithMetrics(remove, 'OnyxUtils.remove'); - // @ts-expect-error Reassign - reportStorageQuota = decorateWithMetrics(reportStorageQuota, 'OnyxUtils.reportStorageQuota'); - // @ts-expect-error Complex type signature - retryOperation = decorateWithMetrics(retryOperation, 'OnyxUtils.retryOperation'); - // @ts-expect-error Reassign - broadcastUpdate = decorateWithMetrics(broadcastUpdate, 'OnyxUtils.broadcastUpdate'); - // @ts-expect-error Reassign - initializeWithDefaultKeyStates = decorateWithMetrics(initializeWithDefaultKeyStates, 'OnyxUtils.initializeWithDefaultKeyStates'); - // @ts-expect-error Complex type signature - multiGet = decorateWithMetrics(multiGet, 'OnyxUtils.multiGet'); - // @ts-expect-error Reassign - tupleGet = decorateWithMetrics(tupleGet, 'OnyxUtils.tupleGet'); - // @ts-expect-error Reassign - subscribeToKey = decorateWithMetrics(subscribeToKey, 'OnyxUtils.subscribeToKey'); - // @ts-expect-error Reassign - setWithRetry = decorateWithMetrics(setWithRetry, 'OnyxUtils.setWithRetry'); - // @ts-expect-error Reassign - multiSetWithRetry = decorateWithMetrics(multiSetWithRetry, 'OnyxUtils.multiSetWithRetry'); - // @ts-expect-error Reassign - setCollectionWithRetry = decorateWithMetrics(setCollectionWithRetry, 'OnyxUtils.setCollectionWithRetry'); -}); - export type {OnyxMethod}; export default OnyxUtils; export {clearOnyxUtilsInternals}; diff --git a/lib/dependencies/ModuleProxy.ts b/lib/dependencies/ModuleProxy.ts deleted file mode 100644 index 597f38a81..000000000 --- a/lib/dependencies/ModuleProxy.ts +++ /dev/null @@ -1,39 +0,0 @@ -type ImportType = ReturnType; - -/** - * Create a lazily-imported module proxy. - * This is useful for lazily requiring optional dependencies. - */ -const createModuleProxy = (getModule: () => ImportType): TModule => { - const holder: {module: TModule | undefined} = {module: undefined}; - - const proxy = new Proxy(holder, { - get: (target, property) => { - if (property === '$$typeof') { - // If inlineRequires is enabled, Metro will look up all imports - // with the $$typeof operator. In this case, this will throw the - // `OptionalDependencyNotInstalledError` error because we try to access the module - // even though we are not using it (Metro does it), so instead we return undefined - // to bail out of inlineRequires here. - return undefined; - } - - if (target.module == null) { - // lazy initialize module via require() - // caller needs to make sure the require() call is wrapped in a try/catch - // eslint-disable-next-line no-param-reassign - target.module = getModule() as TModule; - } - return target.module[property as keyof typeof holder.module]; - }, - }); - return proxy as unknown as TModule; -}; - -class OptionalDependencyNotInstalledError extends Error { - constructor(name: string) { - super(`${name} is not installed!`); - } -} - -export {createModuleProxy, OptionalDependencyNotInstalledError}; diff --git a/lib/dependencies/PerformanceProxy/index.native.ts b/lib/dependencies/PerformanceProxy/index.native.ts deleted file mode 100644 index ba96da650..000000000 --- a/lib/dependencies/PerformanceProxy/index.native.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type performance from 'react-native-performance'; -import {createModuleProxy, OptionalDependencyNotInstalledError} from '../ModuleProxy'; - -const PerformanceProxy = createModuleProxy(() => { - try { - return require('react-native-performance').default; - } catch { - throw new OptionalDependencyNotInstalledError('react-native-performance'); - } -}); - -export default PerformanceProxy; diff --git a/lib/dependencies/PerformanceProxy/index.ts b/lib/dependencies/PerformanceProxy/index.ts deleted file mode 100644 index 3aff0ad43..000000000 --- a/lib/dependencies/PerformanceProxy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Use the existing performance API on web -export default performance; diff --git a/lib/metrics.ts b/lib/metrics.ts deleted file mode 100644 index 5ae0c0cdc..000000000 --- a/lib/metrics.ts +++ /dev/null @@ -1,51 +0,0 @@ -import PerformanceProxy from './dependencies/PerformanceProxy'; - -/** - * Capture a measurement between the start mark and now - */ -function measureMarkToNow(startMark: PerformanceMark, detail: Record) { - PerformanceProxy.measure(`${startMark.name} [${startMark.detail.args.toString()}]`, { - start: startMark.startTime, - end: PerformanceProxy.now(), - detail: {...startMark.detail, ...detail}, - }); -} - -function isPromiseLike(value: unknown): value is Promise { - return value != null && typeof value === 'object' && 'then' in value; -} - -/** - * Wraps a function with metrics capturing logic - */ -function decorateWithMetrics(func: (...args: Args) => ReturnType, alias = func.name) { - function decorated(...args: Args) { - const mark = PerformanceProxy.mark(alias, {detail: {args, alias}}); - - const originalReturnValue = func(...args); - - if (isPromiseLike(originalReturnValue)) { - /* - * The handlers added here are not affecting the original promise - * They create a separate chain that's not exposed (returned) to the original caller - */ - originalReturnValue - .then((result) => { - measureMarkToNow(mark, {result}); - }) - .catch((error) => { - measureMarkToNow(mark, {error}); - }); - - return originalReturnValue; - } - - measureMarkToNow(mark, {result: originalReturnValue}); - return originalReturnValue; - } - decorated.name = `${alias}_DECORATED`; - - return decorated; -} - -export default decorateWithMetrics; diff --git a/lib/storage/index.ts b/lib/storage/index.ts index be45bc41a..d61dab273 100644 --- a/lib/storage/index.ts +++ b/lib/storage/index.ts @@ -4,9 +4,6 @@ import PlatformStorage from './platforms'; import InstanceSync from './InstanceSync'; import MemoryOnlyProvider from './providers/MemoryOnlyProvider'; import type StorageProvider from './providers/types'; -import * as GlobalSettings from '../GlobalSettings'; -import decorateWithMetrics from '../metrics'; - let provider = PlatformStorage as StorageProvider; let shouldKeepInstancesSync = false; let finishInitalization: (value?: unknown) => void; @@ -209,23 +206,4 @@ const storage: Storage = { }, }; -GlobalSettings.addGlobalSettingsChangeListener(({enablePerformanceMetrics}) => { - if (!enablePerformanceMetrics) { - return; - } - - // Apply decorators - storage.getItem = decorateWithMetrics(storage.getItem, 'Storage.getItem'); - storage.multiGet = decorateWithMetrics(storage.multiGet, 'Storage.multiGet'); - storage.setItem = decorateWithMetrics(storage.setItem, 'Storage.setItem'); - storage.multiSet = decorateWithMetrics(storage.multiSet, 'Storage.multiSet'); - storage.mergeItem = decorateWithMetrics(storage.mergeItem, 'Storage.mergeItem'); - storage.multiMerge = decorateWithMetrics(storage.multiMerge, 'Storage.multiMerge'); - storage.removeItem = decorateWithMetrics(storage.removeItem, 'Storage.removeItem'); - storage.removeItems = decorateWithMetrics(storage.removeItems, 'Storage.removeItems'); - storage.clear = decorateWithMetrics(storage.clear, 'Storage.clear'); - storage.getAllKeys = decorateWithMetrics(storage.getAllKeys, 'Storage.getAllKeys'); - storage.getAll = decorateWithMetrics(storage.getAll, 'Storage.getAll'); -}); - export default storage; diff --git a/lib/types.ts b/lib/types.ts index 18cfa1a34..0491b11b5 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -407,12 +407,6 @@ type InitOptions = { */ shouldSyncMultipleInstances?: boolean; - /** - * If enabled it will use the performance API to measure the time taken by Onyx operations. - * @default false - */ - enablePerformanceMetrics?: boolean; - /** * If enabled, it will connect to Redux DevTools Extension for debugging. * This allows you to see all Onyx state changes in the Redux DevTools. diff --git a/lib/useOnyx.ts b/lib/useOnyx.ts index 5808dcb2d..532b59f56 100644 --- a/lib/useOnyx.ts +++ b/lib/useOnyx.ts @@ -5,10 +5,8 @@ import OnyxCache, {TASK} from './OnyxCache'; import type {Connection} from './OnyxConnectionManager'; import connectionManager from './OnyxConnectionManager'; import OnyxUtils from './OnyxUtils'; -import * as GlobalSettings from './GlobalSettings'; import type {CollectionKeyBase, OnyxKey, OnyxValue} from './types'; import usePrevious from './usePrevious'; -import decorateWithMetrics from './metrics'; import onyxSnapshotCache from './OnyxSnapshotCache'; import useLiveRef from './useLiveRef'; @@ -329,19 +327,11 @@ function useOnyx>( [key, options?.initWithStoredValues, options?.reuseConnection, checkEvictableKey], ); - const getSnapshotDecorated = useMemo(() => { - if (!GlobalSettings.isPerformanceMetricsEnabled()) { - return getSnapshot; - } - - return decorateWithMetrics(getSnapshot, 'useOnyx.getSnapshot'); - }, [getSnapshot]); - useEffect(() => { checkEvictableKey(); }, [checkEvictableKey]); - const result = useSyncExternalStore>(subscribe, getSnapshotDecorated); + const result = useSyncExternalStore>(subscribe, getSnapshot); return result; } diff --git a/package.json b/package.json index 59afb7d93..7d7b4eb5f 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,6 @@ "react-native-device-info": "^10.3.0", "react-native-nitro-modules": "^0.27.2", "react-native-nitro-sqlite": "^9.2.0", - "react-native-performance": "^5.1.0", "react-test-renderer": "18.2.0", "reassure": "1.4.0", "ts-node": "^10.9.2", @@ -106,16 +105,12 @@ "react-native": ">=0.75.0", "react-native-device-info": "^10.3.0", "react-native-nitro-modules": ">=0.27.2", - "react-native-nitro-sqlite": "^9.2.0", - "react-native-performance": ">=5.1.0" + "react-native-nitro-sqlite": "^9.2.0" }, "peerDependenciesMeta": { "idb-keyval": { "optional": true }, - "react-native-performance": { - "optional": true - }, "react-native-nitro-modules": { "optional": true }, From 69ecb49bf7b2019aa8ead305ca35b48e3af8d058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Tue, 24 Mar 2026 13:36:47 +0100 Subject: [PATCH 2/4] fix: linter --- lib/storage/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/storage/index.ts b/lib/storage/index.ts index d61dab273..c143c8e85 100644 --- a/lib/storage/index.ts +++ b/lib/storage/index.ts @@ -4,6 +4,7 @@ import PlatformStorage from './platforms'; import InstanceSync from './InstanceSync'; import MemoryOnlyProvider from './providers/MemoryOnlyProvider'; import type StorageProvider from './providers/types'; + let provider = PlatformStorage as StorageProvider; let shouldKeepInstancesSync = false; let finishInitalization: (value?: unknown) => void; From 1229bc47314eea1856c56833866812db3ea9260c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Tue, 24 Mar 2026 13:44:07 +0100 Subject: [PATCH 3/4] fix: package-lock --- package-lock.json | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49f4f1d14..8bdb79245 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,6 @@ "react-native-device-info": "^10.3.0", "react-native-nitro-modules": "^0.27.2", "react-native-nitro-sqlite": "^9.2.0", - "react-native-performance": "^5.1.0", "react-test-renderer": "18.2.0", "reassure": "1.4.0", "ts-node": "^10.9.2", @@ -76,8 +75,7 @@ "react-native": ">=0.75.0", "react-native-device-info": "^10.3.0", "react-native-nitro-modules": ">=0.27.2", - "react-native-nitro-sqlite": "^9.2.0", - "react-native-performance": ">=5.1.0" + "react-native-nitro-sqlite": "^9.2.0" }, "peerDependenciesMeta": { "idb-keyval": { @@ -91,9 +89,6 @@ }, "react-native-nitro-sqlite": { "optional": true - }, - "react-native-performance": { - "optional": true } } }, @@ -13751,16 +13746,6 @@ "react-native-nitro-modules": ">=0.27.2" } }, - "node_modules/react-native-performance": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/react-native-performance/-/react-native-performance-5.1.4.tgz", - "integrity": "sha512-ydqDe8EFlA9iuhA/DBYAnt4Hs3lpmLKcaXPsDfQJwowT4dXPgLgUDqJUuDIYEVsEsjZybloSmUj0fvUuUogqpw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react-native": "*" - } - }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { "version": "0.76.3", "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.3.tgz", From ec1a8f030e5151bade65257e10c0eff4ed8d6bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Thu, 26 Mar 2026 15:11:28 +0100 Subject: [PATCH 4/4] chore: cleanup readme too --- README.md | 53 ----------------------------------------------------- 1 file changed, 53 deletions(-) diff --git a/README.md b/README.md index dd32a0e66..d85b032f8 100644 --- a/README.md +++ b/README.md @@ -351,59 +351,6 @@ const ReportActionsView = ({reportID, isActiveReport}) => { export default ReportActionsView; ``` -# Benchmarks - -Provide the `captureMetrics` boolean flag to `Onyx.init` to capture call statistics - -```js -Onyx.init({ - keys: ONYXKEYS, - evictableKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], - captureMetrics: Config.BENCHMARK_ONYX, -}); -``` - -At any point you can get the collected statistics using `Onyx.getMetrics()`. -This will return an object containing `totalTime`, `averageTime` and `summaries`. -`summaries` is a collection of statistics for each method it contains data about: - - method name - - total, max, min, average times for this method calls - - calls - a list of individual calls with each having: start time; end time; call duration; call arguments - - start/end times are relative to application launch time - 0.00 being exactly at launch - -If you wish to reset the metrics and start over use `Onyx.resetMetrics()` - -Finally, there's a `Onyx.printMetrics()` method which prints human statistics information on the dev console. You can use this method during debugging. For example add an `Onyx.printMetrics()` line somewhere in code or call it through the dev console. It supports 3 popular formats *MD* - human friendly markdown, *CSV* and *JSON*. The default is MD if you want to print another format call `Onyx.printMetrics({ format: 'csv' })` or -`Onyx.printMetrics({ format: 'json' })`. - -Sample output of `Onyx.printMetrics()` - -``` -### Onyx Benchmark - - Total: 1.5min - - Last call finished at: 12.55sec - -| method | total time spent | max | min | avg | time last call completed | calls made | -|-----------------|-----------------:|----------:|---------:|----------:|-------------------------:|-----------:| -| Onyx:getAllKeys | 1.2min | 2.16sec | 0.159ms | 782.230ms | 12.55sec | 90 | -| Onyx:merge | 4.73sec | 2.00sec | 74.412ms | 591.642ms | 10.24sec | 8 | -| Onyx:set | 3.90sec | 846.760ms | 43.663ms | 433.056ms | 7.47sec | 9 | -| Onyx:get | 8.87sec | 2.00sec | 0.063ms | 61.998ms | 10.24sec | 143 | - - -| Onyx:set | -|---------------------------------------------------------------| -| start time | end time | duration | args | -|-----------:|----------:|----------:|--------------------------| -| 291.042ms | 553.079ms | 262.037ms | session, [object Object] | -| 293.719ms | 553.316ms | 259.597ms | account, [object Object] | -| 294.541ms | 553.651ms | 259.109ms | network, [object Object] | -| 365.378ms | 554.246ms | 188.867ms | iou, [object Object] | -| 1.08sec | 2.20sec | 1.12sec | network, [object Object] | -| 1.08sec | 2.20sec | 1.12sec | iou, [object Object] | -| 1.17sec | 2.20sec | 1.03sec | currentURL, / | -``` - # Debug mode It can be useful to log why Onyx is calling `setState()` on a particular React component so that we can understand which key changed, what changed about the value, and the connected component that ultimately rendered as a result. When used correctly this can help isolate problem areas and unnecessary renders in the code. To enable this feature, pass `debugSetState: true` to the config and grep JS console logs for `[Onyx-Debug]`.