-
Notifications
You must be signed in to change notification settings - Fork 1
Configuration Source Extraction part 2/4 #202
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
base: typo/dto-interface-extraction
Are you sure you want to change the base?
Conversation
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 implements HTTP ETag-based caching to optimize configuration polling by avoiding redundant data transfer and processing when configurations haven't changed.
Changes:
- Added
flagsETagfield toConfigurationclass to store and persist ETag values across requests - Created
EppoHttpResponsewrapper to capture HTTP metadata (status code, ETag header) alongside response body - Updated
EppoHttpClientto support conditional requests viaIf-None-Matchheaders and handle 304 Not Modified responses - Modified
ConfigurationRequestorto skip parsing, bandit fetching, and callbacks when receiving 304 responses
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/cloud/eppo/api/Configuration.java | Added flagsETag field with getter/builder methods and updated equals/hashCode/toString |
| src/main/java/cloud/eppo/EppoHttpResponse.java | New wrapper class for HTTP responses with status code, body, and ETag support |
| src/main/java/cloud/eppo/EppoHttpClient.java | Updated to send If-None-Match headers, extract ETags, and return EppoHttpResponse objects |
| src/main/java/cloud/eppo/ConfigurationRequestor.java | Added 304 handling to skip processing when config unchanged, storing ETags in configurations |
| src/test/java/cloud/eppo/ConfigurationRequestorTest.java | Added comprehensive tests for ETag functionality including 304 handling, callback suppression, and round-trip scenarios |
| src/test/java/cloud/eppo/api/ConfigurationBuilderTest.java | Updated constructor calls to include new flagsETag parameter |
| src/test/java/cloud/eppo/BaseEppoClientTest.java | Updated mock to return EppoHttpResponse instead of raw bytes |
| src/test/java/cloud/eppo/helpers/TestUtils.java | Updated mock helper to create EppoHttpResponse objects and handle both method signatures |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Configuration.Builder configBuilder = | ||
| Configuration.builder(flagConfigurationJsonBytes, expectObfuscatedConfig) | ||
| .banditParametersFromConfig(lastConfig); | ||
| Configuration.builder(flagsResponse.getBody()) |
Copilot
AI
Jan 20, 2026
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.
The Configuration.builder() call is missing the expectObfuscatedConfig parameter that was present in the original code. This could cause incorrect parsing of obfuscated configurations.
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.
expectObfuscatedConfig was deprecated and now removed
c97b24b to
d1c6591
Compare
Implement HTTP caching using ETags to reduce bandwidth and processing overhead. Changes: - Add flagsETag field to Configuration for storing current eTag - Create EppoHttpResponse wrapper class (status code, body, eTag) - Update EppoHttpClient to accept If-None-Match header and return EppoHttpResponse - Update ConfigurationRequestor to handle 304 Not Modified responses - Early exit on 304 - skip JSON parsing, bandit fetches, and callbacks Benefits: - ~80% bandwidth reduction (assuming 80% cache hit rate on polling) - Reduced CPU usage - skip parsing and processing when config unchanged - Fewer unnecessary bandit parameter fetches - Reduced callback notifications Implementation: - Get previous eTag from Configuration.getFlagsETag() - Include If-None-Match header in HTTP request if eTag exists - Server returns 304 if config hasn't changed - ConfigurationRequestor early returns (no processing) on 304 - Server returns 200 with new eTag if config changed - New eTag stored in Configuration for next request Tests: - Updated all test mocks to return EppoHttpResponse - Added null safety for empty configurations - All 249 tests passing
📚 Downstream SDK Extensibility Stacked Pull Requests 📚
☑️ Extract DTO interfaces for downstream SDK extensibility (#197)
👉 Add HTTP ETag-based caching for configuration fetches (this PR)
🔲 Extract IConfigurationSource interface for pluggable configuration loading (#203)
🔲 Make OkHttp a peer dependency for version flexibility (#204)
Eppo Internal
🎟️ Reduces bandwidth and CPU overhead for polling SDKs
📜 Design Doc: N/A - Standard HTTP caching implementation
Motivation and Context
Currently, every configuration poll fetches the full configuration JSON even when nothing has changed. This results in significant waste:
HTTP ETags are a standard caching mechanism (RFC 7232) that allows servers to signal when a resource hasn't changed via 304 Not Modified responses, eliminating redundant data transfer and processing.
With high-frequency polling (default 30 seconds) and typical cache hit rates of 70-90%, this optimization can reduce bandwidth and processing by an order of magnitude.
Description
Implement HTTP caching using ETags to optimize configuration fetching.
Added Configuration.flagsETag Field
emptyConfig())Created EppoHttpResponse Wrapper
byte[]returns from EppoHttpClientUpdated EppoHttpClient for ETags
New Method Signatures:
EppoHttpResponse get(String path, String ifNoneMatch)CompletableFuture<EppoHttpResponse> getAsync(String path, String ifNoneMatch)Implementation:
If-None-Match: {eTag}header when ifNoneMatch is non-nullresponse.header("ETag")Updated ConfigurationRequestor for 304 Handling
Fetch Flow:
String previousETag = lastConfig.getFlagsETag()EppoHttpResponse response = client.get(endpoint, previousETag)if (response.isNotModified()) { return; }Optimization:
When flags endpoint returns 304, bandit fetching is automatically skipped (no model version check needed).
How has this been documented?
Configuration.getFlagsETag()explains usageEppoHttpResponsedescribes status codes and eTag handlingHow has this been tested?
Automated Testing
./gradlew test✅ All 249 tests passing
Test Updates
TestUtils.mockHttpResponse()to returnEppoHttpResponseinstead ofbyte[]get(path, ifNoneMatch))Functionality Verified