Edge Event Batching implementation - #199
Open
sagar-sharma-adobe wants to merge 10 commits into
Open
Conversation
…pline BatchOutcome.done() now carries a resolvedCount so EdgeBatchingHitQueue only removes what processBatch actually resolved, instead of always removing the full peeked window — closing a bug where truncated batches (Consent/Reset boundary, decode failures, single non-ExperienceEvent head) silently dropped untouched trailing entities. Also flips earlyPerEventCompletionEnabled to true by default so the completion(N)-before-handle(N+1) ordering guarantee is actually active in production, matching its own doc comment.
- 400 handling: single-event requests (incl. exploded resends and Consent/Reset) now match main's outcome and code path exactly, via an isBatchRequest flag on doRequest; only real N>1 batches explode. Batch 400s drain one event at a time through the single-event path, retrying in place without skipping. - Only batch consecutive events that share the head's snapshotted config. - Model BatchOutcome as Kind + value; make RetryResult.shouldRetry computed; drop the earlyPerEventCompletion flag (now default); dedup the batch/single request-build and send paths; preserve main's log messages. - Tests cover the new paths; full Edge suite green (468 tests).
sagar-sharma-adobe
marked this pull request as ready for review
August 10, 2026 06:56
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.
Description
Summary
Adds Experience Event batching to the Edge extension: consecutive
ExperienceEvents that share a snapshotted request config can now be coalesced into a single Edge Network request instead of one request per event, reducing network overhead for high-volume event sources (e.g. Optimize/Target tracking, Messaging interactions). Batching is opt-in and off by default — SDKs that never enable it see byte-for-byte identical behavior to today.Also adds a per-event error callback:
EdgeCallbackWithError/EdgeEventError, delivered via a newEdge.sendEvent(ExperienceEvent, EdgeCallbackWithError)overload, so callers can receive Edge Network errors for their specific event, not just success handles.Configuration
Two new keys, both read from the
Configurationshared state:edge.batching.enabled(bool, defaultfalse)edge.batching.eventNameAllowlist(list of event names) — batching is opt-in per event name; an event only batches if its name is explicitly allowlisted, even whenenabledistrue.Both keys also support a first-launch fallback: an optional
adb_edgeBatchingConfig.jsonbundled in the app's assets (EdgeBundledBatchingConfig), consulted per-key only when that key is absent from the Configuration shared state. Anything set viaMobileCore.updateConfiguration()or a remote/Launch config always wins over the bundled file for that key.How batching works
EdgeBatchingHitQueuereplacesPersistentHitQueueas the Edge extension's hit queue. Each cycle peeks a window sized from the head entity's snapshotted config: 1 if batching is disabled (identical to today), ormin(queueDepth, MAX_BATCH_SIZE=10)if enabled.EdgeHitProcessor.processBatchwalks the peeked window and only folds in consecutive entities that are all ExperienceEvents, all on the allowlist, and all share the head's config (a batched request can only carry one datastream ID/override — an entity with a different config truncates the run and becomes its own head on a later cycle). Hitting a Consent/Reset event, a non-allowlisted name, or a config mismatch stops the run; anything before that point still batches.BatchOutcome.explode(N)and the queue drains those N entities one at a time through the exact same single-event send path non-batching builds already use, retrying a recoverable failure in place (never skipping it). This is not the same as before batching existed: on 400/other unrecoverable codes,isBatchRequestonEdgeNetworkService.doRequestgates whether the network layer classifies a 400 as explodable — only real batches take that branch; a single event's 400 falls through to the identical unrecoverable-error handling as pre-batching code.NetworkResponseHandlernow completes each event in a batch as soon as a higher-indexed response fragment arrives, instead of waiting for the whole request's stream to close — so an early event in a batch doesn't wait on a later one. A handle/error with noeventIndexis treated as request-scoped: forstate:store/locationHint:result(session-global side effects) it's broadcast to all waiting events; for errors, a root-level error is treated as the whole batch failing, so it fans out to every waiting event's callback. Single events are unaffected — with one waiting event, this degrades to exactly today's behavior.Notable internal refactors (behavior-preserving)
BatchOutcomeis a smallKind(DONE/RETRY/EXPLODE) + singleint value, rather than three independent counters where only one was ever meaningful.RetryResult.shouldRetryis now computed fromNetworkRequestOutcomerather than stored redundantly.buildExperienceEventHit/sendEdgeHitinstead of duplicating logic.main.Compatibility
processBatchroutes every entity through the pre-existing single-event path, and 400/error/retry handling is unchanged from before this feature.EdgeCallbackWithError,EdgeEventError, and the newEdge.sendEventoverload — no existing signatures changed.Testing
EdgeHitProcessor,EdgeBatchingHitQueue,NetworkResponseHandler,EdgeNetworkService,EventUtils,EdgeBundledBatchingConfig,CompletionCallbacksManager— batch formation, config-mismatch truncation, 400/explode/drain (including retry-without-skip), per-event completion ordering, global-handle/error broadcast, and bundled-config precedence.androidTest, on-device/emulator): newEdgeBatchingFunctionalTests(non-batching regression, allowlist gating, real batch coalescing, per-event completion under a server error) plus updates toNetworkResponseHandlerFunctionalTestsandCompletionHandlerFunctionalTestsfor the new global-handle/error-fanout semantics and theEdgeCallbackWithErrorpath.state:store,locationHint:result) broadcasting correctly in a real multi-event response, the non-batching path producing one request per event with batching off, and a real per-event error response — with no crashes, warnings, or response-ordering violations across all scenarios.Related Issue
Motivation and Context
How Has This Been Tested?
Screenshots (if appropriate):
Types of changes
Checklist: