Skip to content

Commit b622701

Browse files
committed
perf: batch multiSet notifications per collection to avoid N redundant subscriber callbacks
1 parent 6926dc3 commit b622701

1 file changed

Lines changed: 33 additions & 4 deletions

File tree

lib/OnyxUtils.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,15 +1276,44 @@ function multiSetWithRetry(data: OnyxMultiSetInput, retryAttempt?: number): Prom
12761276

12771277
const keyValuePairsToSet = OnyxUtils.prepareKeyValuePairsForStorage(newData, true);
12781278

1279+
// Group keys by collection for batched notification, and track non-collection keys separately.
1280+
// This avoids firing N individual keyChanged() calls for N collection members — instead we
1281+
// update all members in cache first, then notify collection subscribers once per collection.
1282+
const collectionBatches = new Map<string, {partial: Record<string, OnyxValue<OnyxKey>>; previous: Record<string, OnyxValue<OnyxKey>>}>();
1283+
const nonCollectionPairs: Array<[string, OnyxValue<OnyxKey>]> = [];
1284+
12791285
for (const [key, value] of keyValuePairsToSet) {
1280-
// When we use multiSet to set a key we want to clear the current delta changes from Onyx.merge that were queued
1281-
// before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
1286+
// Clear any pending merge deltas for this key
12821287
if (OnyxUtils.hasPendingMergeForKey(key)) {
12831288
delete OnyxUtils.getMergeQueue()[key];
12841289
}
12851290

1286-
// Update cache and optimistically inform subscribers
1287-
cache.set(key, value);
1291+
const collectionKey = OnyxKeys.getCollectionKey(key);
1292+
if (collectionKey && OnyxKeys.isCollectionMemberKey(collectionKey, key)) {
1293+
// Capture the previous value before updating cache
1294+
const previousValue = cache.get(key);
1295+
cache.set(key, value);
1296+
1297+
let batch = collectionBatches.get(collectionKey);
1298+
if (!batch) {
1299+
batch = {partial: {}, previous: {}};
1300+
collectionBatches.set(collectionKey, batch);
1301+
}
1302+
batch.partial[key] = value;
1303+
batch.previous[key] = previousValue;
1304+
} else {
1305+
cache.set(key, value);
1306+
nonCollectionPairs.push([key, value]);
1307+
}
1308+
}
1309+
1310+
// Notify collection subscribers once per collection (batched)
1311+
for (const [collectionKey, batch] of collectionBatches) {
1312+
keysChanged(collectionKey as CollectionKeyBase, batch.partial, batch.previous);
1313+
}
1314+
1315+
// Notify non-collection key subscribers individually
1316+
for (const [key, value] of nonCollectionPairs) {
12881317
keyChanged(key, value);
12891318
}
12901319

0 commit comments

Comments
 (0)