Skip to content

Conversation

@leoromanovsky
Copy link
Member

@leoromanovsky leoromanovsky commented Jan 21, 2026

Eppo Internal:

🎟️ Fixes issue
πŸ“œ Design Doc: link if applicable

Motivation and Context

When using the standard client with very large flag deployments (many flags with complex targeting rules), the client-side evaluation during initialization can exceed Android's 5-second ANR (Application Not Responding) threshold. This creates a poor user experience with unresponsive apps on startup.

The Precomputed API solves this by:

  • Computing all flag assignments server-side for a specific subject
  • Returning a fixed-size response regardless of the number of targeting rules
  • Providing instant O(1) lookups on the client with zero evaluation time

Description

Core SDK Changes

New EppoPrecomputedClient - A new client optimized for precomputed assignments:

  • Builder pattern matching the existing EppoClient API style
  • Fetches pre-evaluated flags from the edge assignment endpoint
  • Supports all flag types: string, boolean, integer, numeric, JSON
  • Supports bandit actions with the getBanditAction() method
  • Configurable polling for background configuration refresh
  • Offline mode for disk-only initialization
  • Assignment logging with deduplication cache
  • Graceful mode for fault-tolerant operation

Supporting Classes:

  • PrecomputedConfigurationStore - In-memory + disk cache management
  • PrecomputedConfigurationResponse - Wire protocol DTO for edge endpoint
  • PrecomputedFlag / PrecomputedBandit - Flag and bandit assignment DTOs
  • PrecomputedCacheFile - Disk persistence for offline support
  • ObfuscationUtils - MD5 hashing for obfuscated flag key lookups
  • BanditResult - Return type for bandit action assignments
  • MissingSubjectKeyException - Validation for required subject key

How has this been documented?

Example App Improvements

  • Renamed activities for clarity: MainActivity β†’ HomeActivity, SecondActivity β†’ StandardClientActivity
  • Added back navigation with action bar up buttons on all child activities
  • New PrecomputedActivity demonstrating the precomputed client with:
    • "From Server" / "From Disk" initialization options
    • Dynamic subject attributes table
    • Flag type selection (String/Bool/Int/Numeric/JSON)
    • Assignment logging display
    • Disabled Get Assignment button until initialized
  • Clear Cache button now clears both standard and precomputed caches

How has this been tested?

  • Unit tests for ObfuscationUtils and PrecomputedConfigurationResponse
  • Integration tests using sdk-test-data precomputed fixtures
  • Tests for all flag types, logging, caching, and error handling
  • Example app with live review

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
@leoromanovsky leoromanovsky requested a review from Copilot January 22, 2026 03:50
Copy link

Copilot AI left a 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 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 EppoPrecomputedClient with builder pattern matching existing EppoClient API style, supporting all flag types and bandit actions
  • Added supporting infrastructure including PrecomputedConfigurationStore for in-memory/disk caching, ObfuscationUtils for MD5 hashing, and related DTOs
  • Enhanced example app with new PrecomputedActivity demonstrating 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
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants