Skip to content

Commit 5c5d474

Browse files
stop using storage manager in OnyxUtils to prevent cycle dependecy, add perf tests
1 parent 0717c75 commit 5c5d474

4 files changed

Lines changed: 231 additions & 13 deletions

File tree

lib/Onyx.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function set<TKey extends OnyxKey>(key: TKey, value: OnyxSetInput<TKey>, options
208208
// If the change is null, we can just delete the key.
209209
// Therefore, we don't need to further broadcast and update the value so we can return early.
210210
if (value === null) {
211-
OnyxUtils.remove(key);
211+
OnyxUtils.remove(key).then(() => storageManager.trackKeyRemoval(key));
212212
OnyxUtils.logKeyRemoved(OnyxUtils.METHOD.SET, key);
213213
return Promise.resolve();
214214
}
@@ -364,7 +364,7 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
364364
// If the last change is null, we can just delete the key.
365365
// Therefore, we don't need to further broadcast and update the value so we can return early.
366366
if (validChanges.at(-1) === null) {
367-
OnyxUtils.remove(key);
367+
OnyxUtils.remove(key).then(() => storageManager.trackKeyRemoval(key));
368368
OnyxUtils.logKeyRemoved(OnyxUtils.METHOD.MERGE, key);
369369
return Promise.resolve();
370370
}

lib/OnyxStorageManager/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Storage from '../storage';
44
import * as Logger from '../Logger';
55
import type {StorageUsageConfig, StorageKeyInfo, StorageMetadata, StorageCleanupResult, CleanupExecutionResult} from './types';
66
import {DEFAULT_STORAGE_CONFIG} from './types';
7+
import OnyxUtils from '../OnyxUtils';
78

89
const MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
910
const METADATA_KEY_PREFIX = '__onyx_meta_';
@@ -214,12 +215,7 @@ class OnyxStorageManager {
214215
}
215216

216217
private isKeyEvictable(key: OnyxKey): boolean {
217-
return this.evictableKeys.some((pattern) => {
218-
if (pattern.endsWith('_')) {
219-
return key.startsWith(pattern);
220-
}
221-
return key === pattern;
222-
});
218+
return this.evictableKeys.some((pattern) => key === pattern || OnyxUtils.isCollectionMemberKey(pattern, key));
223219
}
224220

225221
private getEvictableKeysCount(): number {

lib/OnyxUtils.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import type {DeferredTask} from './createDeferredTask';
3939
import createDeferredTask from './createDeferredTask';
4040
import * as GlobalSettings from './GlobalSettings';
4141
import decorateWithMetrics from './metrics';
42-
import storageManager from './OnyxStorageManager';
4342
import type {StorageKeyValuePair} from './storage/providers/types';
4443
import logMessages from './logMessages';
4544

@@ -1110,10 +1109,7 @@ function remove<TKey extends OnyxKey>(key: TKey): Promise<void> {
11101109
const prevValue = cache.get(key, false) as OnyxValue<TKey>;
11111110
cache.drop(key);
11121111
scheduleSubscriberUpdate(key, undefined as OnyxValue<TKey>, prevValue);
1113-
return Storage.removeItem(key).then(() => {
1114-
storageManager.trackKeyRemoval(key);
1115-
return undefined;
1116-
});
1112+
return Storage.removeItem(key).then(() => undefined);
11171113
}
11181114

11191115
function reportStorageQuota(): Promise<void> {
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import {measureAsyncFunction, measureFunction} from 'reassure';
2+
import Onyx from '../../lib';
3+
import storageManager from '../../lib/OnyxStorageManager';
4+
import type {StorageUsageConfig} from '../../lib/OnyxStorageManager/types';
5+
import {getRandomReportActions} from '../utils/collections/reportActions';
6+
import type {OnyxKey} from '../../lib/types';
7+
8+
const ONYXKEYS = {
9+
TEST_KEY: 'test',
10+
TEST_KEY_2: 'test2',
11+
COLLECTION: {
12+
TEST_KEY: 'test_',
13+
TEST_NESTED_KEY: 'test_nested_',
14+
TEST_NESTED_NESTED_KEY: 'test_nested_nested_',
15+
TEST_KEY_2: 'test2_',
16+
TEST_KEY_3: 'test3_',
17+
TEST_KEY_4: 'test4_',
18+
TEST_KEY_5: 'test5_',
19+
EVICTABLE_TEST_KEY: 'evictable_test_',
20+
SNAPSHOT: 'snapshot_',
21+
},
22+
};
23+
24+
const collectionKey = ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY;
25+
const mockedReportActionsMap = getRandomReportActions(collectionKey);
26+
const mockedReportActionsKeys = Object.keys(mockedReportActionsMap);
27+
28+
const resetStorageManagerAfterEachMeasure = async () => {
29+
storageManager.config.enabled = false;
30+
storageManager.setEvictableKeys([]);
31+
await Onyx.clear();
32+
};
33+
34+
describe('OnyxStorageManager', () => {
35+
beforeAll(async () => {
36+
await Onyx.init({
37+
keys: ONYXKEYS,
38+
maxCachedKeysCount: 100000,
39+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
40+
});
41+
});
42+
43+
describe('initialize', () => {
44+
test('one call', async () => {
45+
await measureAsyncFunction(
46+
async () => {
47+
await storageManager.initialize();
48+
},
49+
{
50+
afterEach: resetStorageManagerAfterEachMeasure,
51+
},
52+
);
53+
});
54+
55+
test('one call with 1000 existing keys', async () => {
56+
await measureAsyncFunction(
57+
async () => {
58+
await storageManager.initialize({
59+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
60+
});
61+
},
62+
{
63+
beforeEach: async () => {
64+
const promises = [];
65+
for (let i = 0; i < 1000; i++) {
66+
promises.push(Onyx.set(`${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`, {id: i, data: `test-data-${i}`}));
67+
}
68+
await Promise.all(promises);
69+
},
70+
afterEach: resetStorageManagerAfterEachMeasure,
71+
},
72+
);
73+
});
74+
});
75+
76+
describe('trackKeySet', () => {
77+
test('one call', async () => {
78+
await measureFunction(
79+
() => {
80+
storageManager.trackKeySet(mockedReportActionsKeys[0]);
81+
},
82+
{
83+
beforeEach: async () => {
84+
await storageManager.initialize({
85+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
86+
});
87+
},
88+
afterEach: resetStorageManagerAfterEachMeasure,
89+
},
90+
);
91+
});
92+
93+
test('tracking 1000 key sets', async () => {
94+
await measureFunction(
95+
() => {
96+
for (let i = 0; i < 1000; i++) {
97+
storageManager.trackKeySet(`${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`);
98+
}
99+
},
100+
{
101+
beforeEach: async () => {
102+
await storageManager.initialize({
103+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
104+
});
105+
},
106+
afterEach: resetStorageManagerAfterEachMeasure,
107+
},
108+
);
109+
});
110+
});
111+
112+
describe('trackKeyRemoval', () => {
113+
test('one call', async () => {
114+
await measureFunction(
115+
() => {
116+
storageManager.trackKeyRemoval(mockedReportActionsKeys[0]);
117+
},
118+
{
119+
beforeEach: async () => {
120+
await storageManager.initialize({
121+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
122+
});
123+
storageManager.trackKeySet(mockedReportActionsKeys[0]);
124+
},
125+
afterEach: resetStorageManagerAfterEachMeasure,
126+
},
127+
);
128+
});
129+
130+
test('tracking 1000 key removals', async () => {
131+
await measureFunction(
132+
() => {
133+
for (let i = 0; i < 1000; i++) {
134+
storageManager.trackKeyRemoval(`${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`);
135+
}
136+
},
137+
{
138+
beforeEach: async () => {
139+
await storageManager.initialize({
140+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
141+
});
142+
for (let i = 0; i < 1000; i++) {
143+
storageManager.trackKeySet(`${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`);
144+
}
145+
},
146+
afterEach: resetStorageManagerAfterEachMeasure,
147+
},
148+
);
149+
});
150+
});
151+
152+
describe('setEvictableKeys', () => {
153+
test('one call with 100 patterns', async () => {
154+
const evictableKeys: OnyxKey[] = [];
155+
for (let i = 0; i < 100; i++) {
156+
evictableKeys.push(`pattern_${i}_`);
157+
}
158+
159+
await measureFunction(
160+
() => {
161+
storageManager.setEvictableKeys(evictableKeys);
162+
},
163+
{
164+
afterEach: resetStorageManagerAfterEachMeasure,
165+
},
166+
);
167+
});
168+
});
169+
170+
describe('shouldPerformCleanup', () => {
171+
test('one call with 1000 tracked keys', async () => {
172+
await measureFunction(
173+
() => {
174+
return storageManager.shouldPerformCleanup();
175+
},
176+
{
177+
beforeEach: async () => {
178+
await storageManager.initialize({
179+
enabled: true,
180+
maxIdleDays: 7,
181+
maxAgeDays: 30,
182+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
183+
});
184+
for (let i = 0; i < 1000; i++) {
185+
storageManager.trackKeySet(`${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`);
186+
}
187+
},
188+
afterEach: resetStorageManagerAfterEachMeasure,
189+
},
190+
);
191+
});
192+
});
193+
194+
describe('performCleanup', () => {
195+
test('cleanup with 1000 expired keys', async () => {
196+
await measureAsyncFunction(
197+
async () => {
198+
return storageManager.performCleanup();
199+
},
200+
{
201+
beforeEach: async () => {
202+
await storageManager.initialize({
203+
enabled: true,
204+
maxIdleDays: 0,
205+
maxAgeDays: 0,
206+
evictableKeys: [ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY],
207+
});
208+
209+
const promises = [];
210+
for (let i = 0; i < 1000; i++) {
211+
const key = `${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`;
212+
promises.push(Onyx.set(key, {id: i, data: `expired-data-${i}`}));
213+
}
214+
await Promise.all(promises);
215+
216+
for (let i = 0; i < 1000; i++) {
217+
const key = `${ONYXKEYS.COLLECTION.EVICTABLE_TEST_KEY}${i}`;
218+
storageManager.trackKeySet(key);
219+
}
220+
},
221+
afterEach: resetStorageManagerAfterEachMeasure,
222+
},
223+
);
224+
});
225+
});
226+
});

0 commit comments

Comments
 (0)