@@ -4,7 +4,7 @@ import _ from 'underscore';
44import DevTools from './DevTools' ;
55import * as Logger from './Logger' ;
66import type Onyx from './Onyx' ;
7- import cache , { TASK } from './OnyxCache' ;
7+ import cache from './OnyxCache' ;
88import * as Str from './Str' ;
99import Storage from './storage' ;
1010import type {
@@ -262,143 +262,22 @@ function get<TKey extends OnyxKey, TValue extends OnyxValue<TKey>>(key: TKey): P
262262 return Promise . resolve ( cache . get ( key ) as TValue ) ;
263263 }
264264
265- // RAM-only keys should never read from storage (they may have stale persisted data
266- // from before the key was migrated to RAM-only). Mark as nullish so future get() calls
267- // short-circuit via hasCacheForKey and avoid re-running this branch.
268- if ( isRamOnlyKey ( key ) ) {
269- cache . addNullishStorageKey ( key ) ;
270- return Promise . resolve ( undefined as TValue ) ;
271- }
272-
273- const taskName = `${ TASK . GET } :${ key } ` as const ;
274-
275- // When a value retrieving task for this key is still running hook to it
276- if ( cache . hasPendingTask ( taskName ) ) {
277- return cache . getTaskPromise ( taskName ) as Promise < TValue > ;
278- }
279-
280- // Otherwise retrieve the value from storage and capture a promise to aid concurrent usages
281- const promise = Storage . getItem ( key )
282- . then ( ( val ) => {
283- if ( skippableCollectionMemberIDs . size ) {
284- try {
285- const [ , collectionMemberID ] = splitCollectionMemberKey ( key ) ;
286- if ( skippableCollectionMemberIDs . has ( collectionMemberID ) ) {
287- // The key is a skippable one, so we set the value to undefined.
288- // eslint-disable-next-line no-param-reassign
289- val = undefined as OnyxValue < TKey > ;
290- }
291- } catch ( e ) {
292- // The key is not a collection one or something went wrong during split, so we proceed with the function's logic.
293- }
294- }
295-
296- if ( val === undefined ) {
297- cache . addNullishStorageKey ( key ) ;
298- return undefined ;
299- }
300-
301- cache . set ( key , val ) ;
302- return val ;
303- } )
304- . catch ( ( err ) => Logger . logInfo ( `Unable to get item from persistent storage. Key: ${ key } Error: ${ err } ` ) ) ;
305-
306- return cache . captureTask ( taskName , promise ) as Promise < TValue > ;
265+ return Promise . resolve ( undefined as TValue ) ;
307266}
308267
309268// multiGet the data first from the cache and then from the storage for the missing keys.
310269function multiGet < TKey extends OnyxKey > ( keys : CollectionKeyBase [ ] ) : Promise < Map < OnyxKey , OnyxValue < TKey > > > {
311- // Keys that are not in the cache
312- const missingKeys : OnyxKey [ ] = [ ] ;
313-
314- // Tasks that are pending
315- const pendingTasks : Array < Promise < OnyxValue < TKey > > > = [ ] ;
316-
317- // Keys for the tasks that are pending
318- const pendingKeys : OnyxKey [ ] = [ ] ;
319-
320270 // Data to be sent back to the invoker
321271 const dataMap = new Map < OnyxKey , OnyxValue < TKey > > ( ) ;
322272
323- /**
324- * We are going to iterate over all the matching keys and check if we have the data in the cache.
325- * If we do then we add it to the data object. If we do not have them, then we check if there is a pending task
326- * for the key. If there is such task, then we add the promise to the pendingTasks array and the key to the pendingKeys
327- * array. If there is no pending task then we add the key to the missingKeys array.
328- *
329- * These missingKeys will be later used to multiGet the data from the storage.
330- */
331273 for ( const key of keys ) {
332- // RAM-only keys should never read from storage as they may have stale persisted data
333- // from before the key was migrated to RAM-only.
334- if ( isRamOnlyKey ( key ) ) {
335- if ( cache . hasCacheForKey ( key ) ) {
336- dataMap . set ( key , cache . get ( key ) as OnyxValue < TKey > ) ;
337- }
338- continue ;
339- }
340-
341274 const cacheValue = cache . get ( key ) as OnyxValue < TKey > ;
342275 if ( cacheValue ) {
343276 dataMap . set ( key , cacheValue ) ;
344- continue ;
345- }
346-
347- const pendingKey = `${ TASK . GET } :${ key } ` as const ;
348- if ( cache . hasPendingTask ( pendingKey ) ) {
349- pendingTasks . push ( cache . getTaskPromise ( pendingKey ) as Promise < OnyxValue < TKey > > ) ;
350- pendingKeys . push ( key ) ;
351- } else {
352- missingKeys . push ( key ) ;
353277 }
354278 }
355279
356- return (
357- Promise . all ( pendingTasks )
358- // Wait for all the pending tasks to resolve and then add the data to the data map.
359- . then ( ( values ) => {
360- for ( const [ index , value ] of values . entries ( ) ) {
361- dataMap . set ( pendingKeys [ index ] , value ) ;
362- }
363-
364- return Promise . resolve ( ) ;
365- } )
366- // Get the missing keys using multiGet from the storage.
367- . then ( ( ) => {
368- if ( missingKeys . length === 0 ) {
369- return Promise . resolve ( undefined ) ;
370- }
371-
372- return Storage . multiGet ( missingKeys ) ;
373- } )
374- // Add the data from the missing keys to the data map and also merge it to the cache.
375- . then ( ( values ) => {
376- if ( ! values || values . length === 0 ) {
377- return dataMap ;
378- }
379-
380- // temp object is used to merge the missing data into the cache
381- const temp : OnyxCollection < KeyValueMapping [ TKey ] > = { } ;
382- for ( const [ key , value ] of values ) {
383- if ( skippableCollectionMemberIDs . size ) {
384- try {
385- const [ , collectionMemberID ] = splitCollectionMemberKey ( key ) ;
386- if ( skippableCollectionMemberIDs . has ( collectionMemberID ) ) {
387- // The key is a skippable one, so we skip this iteration.
388- continue ;
389- }
390- } catch ( e ) {
391- // The key is not a collection one or something went wrong during split, so we proceed with the function's logic.
392- }
393- }
394-
395- dataMap . set ( key , value as OnyxValue < TKey > ) ;
396- temp [ key ] = value as OnyxValue < TKey > ;
397- }
398- cache . merge ( temp ) ;
399- return dataMap ;
400- } )
401- ) ;
280+ return Promise . resolve ( dataMap ) ;
402281}
403282
404283/**
@@ -442,29 +321,7 @@ function deleteKeyBySubscriptions(subscriptionID: number) {
442321
443322/** Returns current key names stored in persisted storage */
444323function getAllKeys ( ) : Promise < Set < OnyxKey > > {
445- // When we've already read stored keys, resolve right away
446- const cachedKeys = cache . getAllKeys ( ) ;
447- if ( cachedKeys . size > 0 ) {
448- return Promise . resolve ( cachedKeys ) ;
449- }
450-
451- // When a value retrieving task for all keys is still running hook to it
452- if ( cache . hasPendingTask ( TASK . GET_ALL_KEYS ) ) {
453- return cache . getTaskPromise ( TASK . GET_ALL_KEYS ) as Promise < Set < OnyxKey > > ;
454- }
455-
456- // Otherwise retrieve the keys from storage and capture a promise to aid concurrent usages
457- const promise = Storage . getAllKeys ( ) . then ( ( keys ) => {
458- // Filter out RAM-only keys from storage results as they may be stale entries
459- // from before the key was migrated to RAM-only.
460- const filteredKeys = keys . filter ( ( key ) => ! isRamOnlyKey ( key ) ) ;
461- cache . setAllKeys ( filteredKeys ) ;
462-
463- // return the updated set of keys
464- return cache . getAllKeys ( ) ;
465- } ) ;
466-
467- return cache . captureTask ( TASK . GET_ALL_KEYS , promise ) as Promise < Set < OnyxKey > > ;
324+ return Promise . resolve ( cache . getAllKeys ( ) ) ;
468325}
469326
470327/**
@@ -1075,18 +932,44 @@ function mergeInternal<TValue extends OnyxInput<OnyxKey> | undefined, TChange ex
1075932 * Merge user provided default key value pairs.
1076933 */
1077934function initializeWithDefaultKeyStates ( ) : Promise < void > {
1078- // Filter out RAM-only keys from storage reads as they may have stale persisted data
1079- // from before the key was migrated to RAM-only.
1080- const keysToFetch = Object . keys ( defaultKeyStates ) . filter ( ( key ) => ! isRamOnlyKey ( key ) ) ;
1081- return Storage . multiGet ( keysToFetch ) . then ( ( pairs ) => {
1082- const existingDataAsObject = Object . fromEntries ( pairs ) as Record < string , unknown > ;
935+ // Eagerly load the entire database into cache in a single batch read.
936+ // This is faster than lazy-loading individual keys because:
937+ // 1. One DB transaction instead of hundreds
938+ // 2. All subsequent reads are synchronous cache hits
939+ return Storage . getAll ( ) . then ( ( pairs ) => {
940+ const allDataFromStorage : Record < string , unknown > = { } ;
941+
942+ for ( const [ key , value ] of pairs ) {
943+ // RAM-only keys should never be loaded from storage as they may have stale persisted data
944+ // from before the key was migrated to RAM-only.
945+ if ( isRamOnlyKey ( key ) ) {
946+ continue ;
947+ }
948+ allDataFromStorage [ key ] = value ;
949+ }
950+
951+ // Load all storage data into cache silently (no subscriber notifications)
952+ cache . setAllKeys ( Object . keys ( allDataFromStorage ) ) ;
953+ cache . merge ( allDataFromStorage ) ;
954+
955+ // Extract only the default key states from storage and merge with defaults
956+ const defaultKeysFromStorage = Object . keys ( defaultKeyStates ) . reduce ( ( obj : Record < string , unknown > , key ) => {
957+ if ( key in allDataFromStorage ) {
958+ // eslint-disable-next-line no-param-reassign
959+ obj [ key ] = allDataFromStorage [ key ] ;
960+ }
961+ return obj ;
962+ } , { } ) ;
1083963
1084- const merged = utils . fastMerge ( existingDataAsObject , defaultKeyStates , {
964+ const merged = utils . fastMerge ( defaultKeysFromStorage , defaultKeyStates , {
1085965 shouldRemoveNestedNulls : true ,
1086966 } ) . result ;
1087967 cache . merge ( merged ?? { } ) ;
1088968
1089- for ( const [ key , value ] of Object . entries ( merged ?? { } ) ) keyChanged ( key , value ) ;
969+ // Only notify subscribers for default key states — same as before.
970+ // Other keys will be picked up by subscribers when they connect.
971+ // TODO: Maybe we dont need this.
972+ // for (const [key, value] of Object.entries(merged ?? {})) keyChanged(key, value);
1090973 } ) ;
1091974}
1092975
0 commit comments