Draft
Conversation
Add EppoPrecomputedClient for precomputed flag assignments computed server-side at the edge endpoint. Key features: - Subject-specific initialization (client bound to user at init time) - HTTP POST to edge endpoint: https://fs-edge-assignment.eppo.cloud/assignments - Obfuscation support with MD5 hashing and Base64 decoding - All assignment methods: string, boolean, integer, numeric, JSON, bandit - Offline mode with initialConfiguration for sync without network - Polling support for configuration updates - Assignment logging with deduplication via IAssignmentCache - Graceful mode for error handling New files: - DTOs: PrecomputedFlag, PrecomputedBandit, PrecomputedConfigurationResponse, BanditResult - Utilities: ObfuscationUtils (MD5 hashing) - Storage: PrecomputedCacheFile, PrecomputedConfigurationStore - Client: EppoPrecomputedClient with Builder pattern - Exception: MissingSubjectKeyException - Tests: Unit and instrumented tests
Add a new activity to the example app demonstrating the EppoPrecomputedClient. The new tab allows users to: - Initialize the precomputed client with a subject ID - Test different flag types (string, boolean, integer, numeric) - View assignment logs in real-time Files changed: - PrecomputedActivity.java: New activity for precomputed client demo - activity_precomputed.xml: Layout for the precomputed activity - MainActivity.java: Add button to launch precomputed activity - activity_main.xml: Add precomputed button to main layout - AndroidManifest.xml: Register PrecomputedActivity - strings.xml: Add new string resources
Use MD5 hash of subject key instead of safeCacheKey() to ensure consistent length and avoid StringIndexOutOfBoundsException when subject key is shorter than 8 characters.
- Add dynamic subject attributes table to PrecomputedActivity - Users can add/remove key-value pairs for subject attributes - Numeric values are automatically detected and typed correctly - Add HTTP request/response logging to EppoPrecomputedClient - Log request URL, payload, and detailed error messages
Both keys and values in bandit actionNumericAttributes and actionCategoricalAttributes are Base64 encoded in the server response.
- Update Makefile to fetch precomputed-v1.json test data - Add tests for all assignment types using sdk-test-data fixtures - Add bandit action tests - Tests validate string, boolean, integer, numeric, and JSON flags
Keep sdk-test-data tests for canonical evaluation behavior. Keep mock data tests only for SDK behavior: cache, lifecycle, validation, logging.
The extraLogging and metaData parameters were swapped, causing getMetaData() to return the wrong map and failing the test.
- Rename MainActivity to HomeActivity - Rename SecondActivity to StandardClientActivity - Add action bar back button to StandardClientActivity and PrecomputedActivity - Set parent activity for proper up navigation
…ctivity - Replace single Initialize button with "From Server" and "From Disk" buttons - From Server: fetches configuration from edge endpoint - From Disk: uses offline mode with cached configuration only - Update status messages to indicate initialization source
- Disable Get Assignment button until client is initialized - Update Clear Cache to also delete precomputed cache files - Show count of deleted precomputed caches in toast message
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a new EppoPrecomputedClient that solves Android ANR (Application Not Responding) issues during initialization for deployments with many flags and complex targeting rules. Instead of evaluating flags client-side, all assignments are computed server-side and delivered as a batch, providing instant O(1) lookups with zero evaluation time.
Changes:
- Implemented
EppoPrecomputedClientwith builder pattern matching existingEppoClientAPI style, supporting all flag types and bandit actions - Added supporting infrastructure including
PrecomputedConfigurationStorefor in-memory/disk caching,ObfuscationUtilsfor MD5 hashing, and related DTOs - Enhanced example app with new
PrecomputedActivitydemonstrating the precomputed client functionality
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
example/src/main/res/values/strings.xml |
Added string resource for launching precomputed client |
example/src/main/res/layout/activity_precomputed.xml |
New layout for precomputed client demo with subject attributes, flag type selection, and assignment logging |
example/src/main/res/layout/activity_home.xml |
Added button to launch precomputed activity |
example/src/main/java/cloud/eppo/androidexample/StandardClientActivity.java |
Renamed from SecondActivity and added back navigation support |
example/src/main/java/cloud/eppo/androidexample/PrecomputedActivity.java |
New activity demonstrating precomputed client with server/disk initialization options |
example/src/main/java/cloud/eppo/androidexample/HomeActivity.java |
Renamed from MainActivity, added precomputed activity launcher and cache clearing for precomputed clients |
example/src/main/AndroidManifest.xml |
Updated activity names and added parent activity declarations for back navigation |
eppo/src/test/java/cloud/eppo/android/PrecomputedConfigurationResponseTest.java |
Unit tests for precomputed configuration response deserialization |
eppo/src/test/java/cloud/eppo/android/ObfuscationUtilsTest.java |
Unit tests for MD5 hashing utility |
eppo/src/main/java/cloud/eppo/android/util/ObfuscationUtils.java |
Utility class for MD5 hashing used in flag key obfuscation |
eppo/src/main/java/cloud/eppo/android/exceptions/MissingSubjectKeyException.java |
New exception for missing subject key validation |
eppo/src/main/java/cloud/eppo/android/dto/PrecomputedFlag.java |
DTO for precomputed flag assignments with Base64-encoded fields |
eppo/src/main/java/cloud/eppo/android/dto/PrecomputedConfigurationResponse.java |
Wire protocol response from precomputed edge endpoint |
eppo/src/main/java/cloud/eppo/android/dto/PrecomputedBandit.java |
DTO for precomputed bandit assignments |
eppo/src/main/java/cloud/eppo/android/dto/BanditResult.java |
Return type for bandit action assignments |
eppo/src/main/java/cloud/eppo/android/PrecomputedConfigurationStore.java |
Configuration storage with disk caching |
eppo/src/main/java/cloud/eppo/android/PrecomputedCacheFile.java |
Disk cache file management for precomputed configuration |
eppo/src/main/java/cloud/eppo/android/EppoPrecomputedClient.java |
Main precomputed client implementation with assignment methods, logging, and polling |
eppo/src/androidTest/java/cloud/eppo/android/EppoPrecomputedClientTest.java |
Integration tests for precomputed client using test data |
Makefile |
Updated test data copying to include precomputed configuration files |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import java.util.List; | ||
|
|
||
| /** | ||
| * Example activity demonstrating the EppoPrecomputedClient. The precomputed client computes all |
There was a problem hiding this comment.
Corrected spelling of 'recieve' to 'receive' in class documentation.
Suggested change
| * Example activity demonstrating the EppoPrecomputedClient. The precomputed client computes all | |
| * Example activity demonstrating the EppoPrecomputedClient. The precomputed client receives all |
- Add synchronized block to loadConfigFromCache to prevent race condition - Recreate executor in resumePolling if it was previously shut down - Remove unnecessary @nullable annotation from getPrecomputedAssignment
- Decode both keys and values in extraLogging (was only decoding values) - Redact API key from debug log output - Add test for resumePolling after stopPolling - Add test for non-graceful mode initialization
- Extract common cache file logic into BaseCacheFile base class - ConfigCacheFile and PrecomputedCacheFile now extend BaseCacheFile - Add @nullable annotations to PrecomputedBandit attribute maps - Fix toBytes() to serialize environment as object (matching server format)
- Add null safety checks for bandit field decoding - Fix polling to start after initial fetch completes - Update in-memory config even if disk write fails - Remove double-set of configuration on init - Extract SUBJECT_KEY_HASH_LENGTH constant - Add @nonnull annotation to baseUrl builder method - Add PrecomputedConfigurationStoreTest
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Motivation
Customers using the standard client with large flag deployments have reported ANRs on Android. The client-side evaluation during initialization can exceed Android's 5-second ANR threshold, causing the app to appear frozen at startup. The precomputed client solves this by moving all flag evaluation to the server. Instead of downloading the full configuration and evaluating rules locally, the client fetches a pre-evaluated response for the specific subject. This makes the response size fixed regardless of how many targeting rules exist, and flag lookups become instant O(1) map accesses with no evaluation overhead.
Changes
This PR adds
EppoPrecomputedClientto the Android SDK, matching the precomputed clients already available for browser and iOS. The implementation includes the client itself with Builder pattern initialization, DTOs for the wire protocol, a configuration store with async disk caching, and obfuscation utilities. The example app has been updated to demonstrate both client types side-by-side, with the precomputed demo showing server vs disk initialization, dynamic subject attributes, and flag type selection.Decisions
The implementation follows the patterns established by the existing iOS and browser precomputed clients. On the Android side, initialization uses
CompletableFuturefor async operations, and the client integrates with Android lifecycle throughpausePolling()/resumePolling()methods that activities can call fromonPause()/onResume().