fix: race condition in ConfigurationStore cache loading #235
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Eppo Internal:
ποΈ Fixes race condition in offline mode initialization
π Design Doc: N/A
Motivation and Context
When initializing an
EppoClientwith a pre-seeded cache in offline mode, there's a race condition wheregetConfiguration()can be called before the async cache load completes. This causes the client to return an empty configuration and fall back to default values, even though valid configuration data exists in the cache.The race condition occurs because:
loadConfigFromCache()loads the cache file asynchronously viaCompletableFuture.supplyAsync()getAssignment()immediatelyBaseEppoClient.getConfiguration()is called synchronously to evaluate the assignmentConfigurationStore.getConfiguration()returns the internalconfigurationfield, which is stillemptyConfig()because the async load hasn't completedThis is particularly problematic for offline mode scenarios where the cache is the only source of configuration data.
Description
ConfigurationStore.java (lines 45-57)
Updated
loadConfigFromCache()to immediately update the internalconfigurationfield within the async task:Why This Works:
this.configurationis updated as soon as the file is readvolatilemodifier ensures visibility across threadsgetConfiguration()returns the loaded config even if called before the future completesEppoClientTest.java (lines 542-610)
Added
testOfflineInitWithPreSeededCache()which specifically exercises this race condition:getDoubleAssignment()after initializationWithout the fix, this test would fail because
getConfiguration()would return empty config.How has this been documented?
Code Comments:
ConfigurationStore.loadConfigFromCache()explaining whythis.configurationmust be updated (lines 51-53)No external documentation changes required as this is an internal bug fix.
How has this been tested?
New Test:
testOfflineInitWithPreSeededCache()(EppoClientTest.java:542-610)All Tests Passing: β 26/26 (100%)
Existing Tests Confirming No Regression:
testOfflineInit()- Offline init with initial configurationtestObfuscatedOfflineInit()- Offline init with obfuscated configtestCachedConfigurations()- Cache usage across reinitializationstestDifferentCacheFilesPerKey()- Multiple caches with different API keysBackwards Compatibility: β