-
Notifications
You must be signed in to change notification settings - Fork 46
Share SharedPreferencesInMemoryCache across cache instances Fixes AB#3428107 #2813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
56cdb78
9c24275
757b178
9102d98
90223bc
280cffa
5a67472
d439457
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,7 +52,9 @@ | |
| import java.util.HashSet; | ||
| import java.util.Iterator; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
|
|
||
| import edu.umd.cs.findbugs.annotations.Nullable; | ||
| import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
|
|
@@ -104,6 +106,20 @@ public class BrokerOAuth2TokenCache | |
| private final MicrosoftFamilyOAuth2TokenCache mFociCache; | ||
| private final int mUid; | ||
| private ProcessUidCacheFactory mDelegate = null; | ||
| /** | ||
| * Shared, process-wide registry of in-memory augmented account/credential caches keyed by | ||
| * storage (SharedPreferences) name. | ||
| * <p> | ||
| * Populated lazily via computeIfAbsent in getCacheToBeUsed(...). Allows multiple | ||
| * BrokerOAuth2TokenCache instances to reuse the same in-memory layer for the same underlying | ||
| * encrypted name-value store, reducing disk I/O and serialization overhead. | ||
| * <p> | ||
| * Thread-safety: ConcurrentHashMap ensures safe concurrent access and publication. | ||
| * Lifecycle: Entries are never removed for the lifetime of the process. | ||
| */ | ||
| private static final Map<String, SharedPreferencesAccountCredentialCacheWithMemoryCache> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed offline, consider using ReadWriteLock in this layer for further optimization.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @siddhijain, is this comment addressed or you are going to address it in a future commit. @rpdome , @mohitc1 , please block PRs from merging in instances like this.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @moumighosh We synced offline. This will be picked up as a separate work item. |
||
| inMemoryCacheMapByStorage = new ConcurrentHashMap<>(); | ||
|
|
||
|
|
||
| /** | ||
| * Constructs a new BrokerOAuth2TokenCache. | ||
|
|
@@ -1337,6 +1353,7 @@ public void clearAll() { | |
|
|
||
| this.mFociCache.clearAll(); | ||
| this.mApplicationMetadataCache.clear(); | ||
| inMemoryCacheMapByStorage.clear(); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -1611,14 +1628,10 @@ private MsalOAuth2TokenCache initializeProcessUidCache(@NonNull final IPlatformC | |
| return mDelegate.getTokenCache(components, uid); | ||
| } | ||
|
|
||
| final INameValueStorage<String> sharedPreferencesFileManager = | ||
| components.getStorageSupplier().getEncryptedNameValueStore( | ||
| SharedPreferencesAccountCredentialCache | ||
| .getBrokerUidSequesteredFilename(uid), | ||
| String.class | ||
| ); | ||
| final String storeName = SharedPreferencesAccountCredentialCache | ||
| .getBrokerUidSequesteredFilename(uid); | ||
|
|
||
| return getTokenCache(components, sharedPreferencesFileManager, false); | ||
| return getTokenCache(components, storeName, false); | ||
| } | ||
|
|
||
| private static MicrosoftFamilyOAuth2TokenCache initializeFociCache(@NonNull final IPlatformComponents components) { | ||
|
|
@@ -1628,37 +1641,16 @@ private static MicrosoftFamilyOAuth2TokenCache initializeFociCache(@NonNull fina | |
| "Initializing foci cache" | ||
| ); | ||
|
|
||
| final INameValueStorage<String> sharedPreferencesFileManager = | ||
| components.getStorageSupplier().getEncryptedNameValueStore( | ||
| SharedPreferencesAccountCredentialCache.BROKER_FOCI_ACCOUNT_CREDENTIAL_SHARED_PREFERENCES, | ||
| String.class | ||
| ); | ||
| final String storeName = SharedPreferencesAccountCredentialCache.BROKER_FOCI_ACCOUNT_CREDENTIAL_SHARED_PREFERENCES; | ||
|
|
||
| return getTokenCache(components, sharedPreferencesFileManager, true); | ||
| return getTokenCache(components, storeName,true); | ||
| } | ||
|
|
||
| @SuppressWarnings(UNCHECKED) | ||
| private static <T extends MsalOAuth2TokenCache> T getTokenCache(@NonNull final IPlatformComponents components, | ||
| @NonNull final INameValueStorage<String> spfm, | ||
| String storeName, | ||
| boolean isFoci) { | ||
| final ICacheKeyValueDelegate cacheKeyValueDelegate = new CacheKeyValueDelegate(); | ||
| final boolean isFlightEnabled = CommonFlightsManager.INSTANCE | ||
| .getFlightsProvider() | ||
| .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS); | ||
| final IAccountCredentialCache accountCredentialCache; | ||
| if (isFlightEnabled) { | ||
| accountCredentialCache = new SharedPreferencesAccountCredentialCacheWithMemoryCache( | ||
| cacheKeyValueDelegate, | ||
| spfm | ||
| ); | ||
| SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), true); | ||
| } else { | ||
| accountCredentialCache = new SharedPreferencesAccountCredentialCache( | ||
| cacheKeyValueDelegate, | ||
| spfm | ||
| ); | ||
| SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), false); | ||
| } | ||
| final IAccountCredentialCache accountCredentialCache = getCacheToBeUsed(components, storeName); | ||
| final MicrosoftStsAccountCredentialAdapter accountCredentialAdapter = | ||
| new MicrosoftStsAccountCredentialAdapter(); | ||
|
|
||
|
|
@@ -1678,6 +1670,54 @@ private static <T extends MsalOAuth2TokenCache> T getTokenCache(@NonNull final I | |
| ); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Determines which cache implementation to use based on flighting. | ||
| * <p> | ||
| * When the flight {@code USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS} is enabled, | ||
| * returns a shared, cached instance of {@link SharedPreferencesAccountCredentialCacheWithMemoryCache} | ||
| * for the given storage, improving performance by reusing the in-memory cache layer across cache instances. | ||
| * When disabled, returns a new {@link SharedPreferencesAccountCredentialCache} instance. | ||
| * <p> | ||
| * Critical behavior: When flight is enabled, the same {@code SharedPreferencesAccountCredentialCacheWithMemoryCache} | ||
| * instance is returned for the same {@code spfm} reference, meaning the cache is shared across multiple | ||
| * {@code BrokerOAuth2TokenCache} instances. | ||
| * <p> | ||
| * Thread-safety: This method is thread-safe via {@code ConcurrentHashMap.computeIfAbsent}. | ||
| * <p> | ||
| * Lifecycle: Returned cached instances are never removed from the static map. | ||
| * | ||
| * @return A cached shared in-memory cache instance (flight enabled) or a new | ||
| * non-cached instance (flight disabled). | ||
| */ | ||
| public static IAccountCredentialCache getCacheToBeUsed(@NonNull final IPlatformComponents components, | ||
siddhijain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| final String storeName) { | ||
| final boolean isFlightEnabled = CommonFlightsManager.INSTANCE | ||
| .getFlightsProvider() | ||
| .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS); | ||
| SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), isFlightEnabled); | ||
| if (isFlightEnabled) { | ||
| return inMemoryCacheMapByStorage.computeIfAbsent(storeName, s -> | ||
| new SharedPreferencesAccountCredentialCacheWithMemoryCache( | ||
| new CacheKeyValueDelegate(), | ||
| components.getStorageSupplier().getEncryptedNameValueStore( | ||
| storeName, | ||
| String.class | ||
| )) | ||
siddhijain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ); | ||
| } else { | ||
| final INameValueStorage<String> sharedPreferencesFileManager = | ||
| components.getStorageSupplier().getEncryptedNameValueStore( | ||
| storeName, | ||
| String.class | ||
| ); | ||
| return new SharedPreferencesAccountCredentialCache( | ||
| new CacheKeyValueDelegate(), | ||
| sharedPreferencesFileManager | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| @Nullable | ||
| private MsalOAuth2TokenCache getTokenCacheForClient(@Nullable final BrokerApplicationMetadata metadata) { | ||
| final String methodName = ":getTokenCacheForClient(bam)"; | ||
|
|
@@ -1736,53 +1776,4 @@ private MsalOAuth2TokenCache getTokenCacheForClient(@NonNull final String client | |
|
|
||
| return getTokenCacheForClient(metadata); | ||
| } | ||
|
|
||
| /** | ||
| * Sets the SSO state for the supplied Account, relative to the provided uid. | ||
| * | ||
| * @param uidStr The uid of the app whose SSO token is being inserted. | ||
| * @param account The account for which the supplied token is being inserted. | ||
| * @param refreshToken The token to insert. | ||
| */ | ||
| public void setSingleSignOnState(@NonNull final String uidStr, | ||
| @NonNull final GenericAccount account, | ||
| @NonNull final GenericRefreshToken refreshToken) { | ||
| final String methodName = ":setSingleSignOnState"; | ||
|
|
||
| final boolean isFrt = refreshToken.getIsFamilyRefreshToken(); | ||
|
|
||
| MsalOAuth2TokenCache targetCache; | ||
|
|
||
| final int uid = Integer.parseInt(uidStr); | ||
|
|
||
| if (isFrt) { | ||
| Logger.verbose(TAG + methodName, "Saving tokens to foci cache."); | ||
|
|
||
| targetCache = mFociCache; | ||
| } else { | ||
| // If there is an existing cache for this client id, use it. Otherwise, create a new | ||
| // one based on the supplied uid. | ||
| targetCache = initializeProcessUidCache(getComponents(), uid); | ||
| } | ||
| try { | ||
| targetCacheSetSingleSignOnState(account, refreshToken, targetCache); | ||
| updateApplicationMetadataCache( | ||
| refreshToken.getClientId(), | ||
| refreshToken.getEnvironment(), | ||
| refreshToken.getFamilyId(), | ||
| uid | ||
| ); | ||
| } catch (ClientException e) { | ||
| Logger.warn( | ||
| TAG + methodName, | ||
| "Failed to save account/refresh token. Skipping." | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Suppressing unchecked warning as the generic type was not provided for targetCache | ||
| @SuppressWarnings(WarningType.unchecked_warning) | ||
| private void targetCacheSetSingleSignOnState(@NonNull GenericAccount account, @NonNull GenericRefreshToken refreshToken, MsalOAuth2TokenCache targetCache) throws ClientException { | ||
| targetCache.setSingleSignOnState(account, refreshToken); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.