Memoize device template to improve rule evaluation performance#429
Merged
Conversation
Building the device template assembled ~55 fields and ran a full encodeToString/decodeFromString round trip on every call — per rule, per evaluation, plus twice concurrently during config fetch. Each build also repeated system IPCs (Settings.Secure ANDROID_ID, PackageManager lookups) and allocated six SimpleDateFormats. - Cache the serialized template keyed by a fingerprint of every mutable input (identity, entitlements, subscription status, active products, interface style, platform wrapper, locale/timezone, storage counters, storefront). Any change to those inputs rebuilds the template; enrichment and experimental properties stay outside the cache and are applied fresh. - Overlay time-derived fields (utc/local date-time strings, days/minutes since install and last paywall view) on every read instead of keying the cache on them, deriving all of them from a single timestamp. - Memoize process-constant fields: vendorId/deviceId (Settings.Secure), app version/build/install date (reuse the constructor-cached PackageInfo), padded versions, capabilities JSON, kotlinVersion. - Replace the JSON string round trip in DeviceTemplate.toDictionary with encodeToJsonElement and a direct JsonObject-to-map conversion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ukxof6pYoqDdcFMvwCtb23
7dc686a to
7134a20
Compare
8615b5d wrapped message parsing in runCatching so unknown messages don't crash the batch, but left the original unguarded add in place. Every message was added twice, and an unknown message still threw and failed the whole parse. Keep only the guarded add and log a warning when a message can't be parsed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ukxof6pYoqDdcFMvwCtb23
Follow-ups to get CI green after the develop changes this branch sits on: - parseWrappedPaywallMessages: skip messages with unknown event names (logging a warning) instead of catching every parse error. Newer paywalls can send events an older SDK doesn't know about without breaking the batch, while a malformed payload for a known event still fails the whole parse, as parsePageView_missingRequiredField_fails expects. Update parse_unknown_event_returns_failure to the new semantics and have it prove a known message alongside the unknown one still parses. - PaywallViewDismissTest: pass forceCleanup = true to beforeOnDestroy/ destroyed. Since 4a4ad72 those are no-ops on non-finishing stops; production reaches teardown via forceCleanup = isFinishing, and these tests simulate a dismissal that finishes the Activity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ukxof6pYoqDdcFMvwCtb23
Feature PRs land in develop, so preload regressions were only caught at release time when a PR into main ran the benchmark. Trigger it on PRs into develop too; merges already kept baselines fresh via push. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ukxof6pYoqDdcFMvwCtb23
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.
Changes in this pull request
Memoizes the device template used for audience-filter evaluation and paywall templating. The template is now rebuilt only when one of its mutable inputs (identity, entitlements, subscription status, etc.) changes, rather than on every rule evaluation.
Separates volatile and stable template fields:
utcDateTime,daysSinceLastPaywallView) are recomputed and overlaid on cache hitsOptimizes property access:
appVersion,vendorId,deviceId,kotlinVersion, etc.) to lazy-initialized valuesPowerManagersystem service lookupvolatileTemplateFields()functionRefactors template building:
buildDeviceTemplate()method for claritytemplateFingerprint()to detect when cache invalidation is neededVolatileTemplateFieldsdata class to manage time-derived valuesAtomicReferencefor thread-safe cache managementRemoves duplicate code:
daysSinceLastPaywallViewandminutesSinceLastPaywallViewpropertiesAdds comprehensive test coverage (
DeviceHelperMemoizationTest):Checklist
DeviceHelperMemoizationTestwith 5 test cases)DeviceTemplateTestupdated to verifytoDictionary()compatibilityCHANGELOG.mdfor this enhancementPerformance Impact
This change significantly reduces CPU and memory overhead during rule evaluation: