-
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
Conversation
|
✅ Work item link check complete. Description contains link AB#3428107 to an Azure Boards work item. |
|
❌ Invalid work item number: AB#3428107 Click here to learn more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a performance optimization by sharing SharedPreferencesAccountCredentialCacheWithMemoryCache instances across multiple BrokerOAuth2TokenCache instances. Instead of creating a new in-memory cache wrapper for each token cache, the code now maintains a static ConcurrentHashMap that maps INameValueStorage instances to their corresponding cached SharedPreferencesAccountCredentialCacheWithMemoryCache objects. This avoids redundant memory allocation and duplicate cache loading operations when the in-memory cache flight is enabled.
Key Changes
- Extracted cache initialization logic into a new
getCacheToBeUsed()helper method that implements the caching strategy - Added a static
ConcurrentHashMap(inMemoryCacheMapByStorage) to store and reuseSharedPreferencesAccountCredentialCacheWithMemoryCacheinstances keyed by their underlying storage - Removed unused
setSingleSignOnStateandtargetCacheSetSingleSignOnStatemethods fromBrokerOAuth2TokenCache(these remain available through the parentMsalOAuth2TokenCacheclass)
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java | Refactored cache creation to use a static map for sharing in-memory cache instances; added getCacheToBeUsed() method; removed unused SSO state methods |
| changelog.txt | Added changelog entry documenting this minor performance improvement |
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Outdated
Show resolved
Hide resolved
| final ICacheKeyValueDelegate cacheKeyValueDelegate = new CacheKeyValueDelegate(); | ||
| SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), isFlightEnabled); | ||
| if (isFlightEnabled) { | ||
| return inMemoryCacheMapByStorage.computeIfAbsent(spfm, s -> | ||
| new SharedPreferencesAccountCredentialCacheWithMemoryCache( | ||
| cacheKeyValueDelegate, | ||
| spfm | ||
| ) | ||
| ); |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Severity: Medium – CacheKeyValueDelegate instantiated per call instead of being reused
Issue: A new CacheKeyValueDelegate instance is created on every call to getCacheToBeUsed(), even when returning a cached SharedPreferencesAccountCredentialCacheWithMemoryCache instance. However, the delegate is only used during the initial construction (inside computeIfAbsent) when the cache is first created.
Impact:
- Unnecessary object allocation on every cache lookup (minor performance overhead)
- Inconsistency: when the flight is disabled, each call creates a new
SharedPreferencesAccountCredentialCachewith a new delegate (expected), but when enabled, the returned cached instance uses the delegate from the first creation, not the current one - This could cause confusion during debugging if delegate behavior needs to be analyzed
Recommendation: Move the cacheKeyValueDelegate instantiation inside the lambda passed to computeIfAbsent so it's only created when a new cache instance is actually constructed. This makes the code more efficient and clearer:
private static IAccountCredentialCache getCacheToBeUsed(@NonNull final INameValueStorage<String> spfm) {
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(spfm, s ->
new SharedPreferencesAccountCredentialCacheWithMemoryCache(
new CacheKeyValueDelegate(),
spfm
)
);
} else {
return new SharedPreferencesAccountCredentialCache(
new CacheKeyValueDelegate(),
spfm
);
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changes in the next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siddhijain , let's not do this in the future, all comments must be addressed in the same PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Outdated
Show resolved
Hide resolved
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Outdated
Show resolved
Hide resolved
| private static final Map<INameValueStorage<String>, SharedPreferencesAccountCredentialCacheWithMemoryCache> | ||
| inMemoryCacheMapByStorage = new ConcurrentHashMap<>(); |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Severity: Medium – Static cache map not cleared during clearAll() operations
Issue: The static inMemoryCacheMapByStorage map is never cleared, even when BrokerOAuth2TokenCache.clearAll() is called (line 1326). The clearAll() method clears individual token caches and application metadata, but the cached SharedPreferencesAccountCredentialCacheWithMemoryCache instances remain in the static map.
Impact:
- After
clearAll(), subsequent cache operations may still use the old cached instances which have already been cleared, leading to inconsistent state - The in-memory cache layer within
SharedPreferencesAccountCredentialCacheWithMemoryCachewill be out of sync with the underlying storage after a clear operation - In testing scenarios, this can cause test pollution where one test's data affects another test even after explicit cache clearing
Recommendation:
- Add a static method to clear the
inMemoryCacheMapByStoragemap (e.g.,clearCacheMap()) and call it fromclearAll() - Alternatively, call
clear()on the map itself withinclearAll()to ensure the in-memory cache references are released - Consider whether clearing should trigger reloading of the in-memory cache or if the instances should be invalidated entirely
Example fix:
@Override
public void clearAll() {
final List<BrokerApplicationMetadata> allClientsMetadata = mApplicationMetadataCache.getAll();
for (final BrokerApplicationMetadata clientMetadata : allClientsMetadata) {
final OAuth2TokenCache clientTokenCache = getTokenCacheForClient(
clientMetadata.getClientId(),
clientMetadata.getEnvironment(),
clientMetadata.getUid()
);
if (clientTokenCache != null) {
clientTokenCache.clearAll();
}
}
this.mFociCache.clearAll();
this.mApplicationMetadataCache.clear();
inMemoryCacheMapByStorage.clear(); // Clear the static cache map
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in the next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siddhijain , let's discuss this offline, we should not be auto-resolving CoPilot comments when it is flagging high severity/Medium severity issues. All issues must be addressed in the same PR
…kerOAuth2TokenCache.java Co-authored-by: Copilot <[email protected]>
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Outdated
Show resolved
Hide resolved
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Outdated
Show resolved
Hide resolved
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Show resolved
Hide resolved
common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheTest.java
Show resolved
Hide resolved
common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheTest.java
Outdated
Show resolved
Hide resolved
common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java
Show resolved
Hide resolved
common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheTest.java
Outdated
Show resolved
Hide resolved
mohitc1
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
![]()
| * 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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, consider using ReadWriteLock in this layer for further optimization.
(maybe consider benchmarking if it's worth it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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.
(It's not a bug/regression in this PR - more like "what you can look into improving next.")

Fixes AB#3428107
Proposing to use static instance of in-memory cache layer for performance benefits.