feat(events): add flushAndWait with a bounded timeout (tier 2) - #403
Open
abelonogov-ld wants to merge 31 commits into
Open
abelonogov-ld wants to merge 31 commits into
abelonogov-ld wants to merge 31 commits into
Conversation
Tier 2 of the event durability spec: recoverable failure. flushAndWait(timeout) delivers what has been recorded and reports whether it got there inside the budget, so an application that knows it is about to go away — backgrounding, or an uncaught exception handler on its way out — gets an answer instead of a fire-and-forget flush. Delivery now reports its outcome so the bounded call can tell delivered from not. Spec: Event Durability, "Tier 2 — recoverable failure", §10. Co-authored-by: Cursor <cursoragent@cursor.com>
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/DirectEventProcessor.java
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/DirectEventProcessor.java
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # example/src/main/java/com/launchdarkly/example/MainActivity.java # example/src/main/res/layout/activity_main.xml
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush * andrey/event-durability-tier1-buffer: unserializable unit test
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/DirectEventProcessorTest.java
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush * andrey/event-durability-tier1-buffer: test(fdv2): stop requiring a changeset to be the first result
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/DirectEventProcessor.java
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com>
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/DirectEventProcessor.java
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/DirectEventProcessorTest.java
…-durability-tier2-bounded-flush
…-durability-tier2-bounded-flush * andrey/event-durability-tier1-buffer: Document that close() discards events held while offline
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/DirectEventProcessorTest.java
…-durability-tier2-bounded-flush
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit df9e409. Configure here.
flushAndWait started from delivered = true and only updated it for the environments this client still belongs to. After close() that set is empty, so the call reported success for events it could not deliver. It now returns false when the client has been closed or replaced. Co-authored-by: Cursor <cursoragent@cursor.com>
3 tasks
…-durability-tier2-bounded-flush Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/DirectEventProcessorTest.java
This branch has not been deployed
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.

Requirements
Related issues
Tier 2 of the Event Durability spec, "Tier 2 — recoverable failure" (§10). Stacked on #397 (tier 1), and targets that branch so this diff shows only tier 2. It is small and self-contained, so it can be reviewed in parallel with #397. It will be retargeted to
mainonce #397 merges.Describe the solution you've provided
A new public method,
LDClientInterface.flushAndWait(long timeout, TimeUnit unit). It sends what has been recorded and returns whether the events were delivered within the timeout.flush()is fire-and-forget, so an application that knows it is about to lose the chance to send (an uncaught exception handler, a move to the background) had no way to give its events that chance and find out whether it worked.flushAndWaitgives it both, with a bound the caller chooses.EventProcessorgainsblockingFlush(long, TimeUnit)as a default method, so custom implementations written before it still compile. The default falls back to the unboundedblockingFlush()and returns true.DirectEventProcessorimplements it. Delivery now reports its outcome: true if the events were accepted or there was nothing to send, false if offline, closed, or the send failed. If the timeout expires, the delivery is left running rather than cancelled, because the buffer has already been drained into the payload and interrupting it would only make the loss certain.LDClient.flushAndWaitshares one budget across them rather than giving each a fresh copy, so the timeout is the most the call can take.Describe alternatives you've considered
Make
flush()return aFuture. That changes the signature of an existing public method, and aFuturestill leaves the caller to pick a timeout and interpret the exceptions. A boolean with the timeout in the call is the shape a crash handler actually needs.Cancel the delivery on timeout. It frees the thread sooner but guarantees the events are lost, since they are no longer in the buffer. Letting it finish in the background gives them the best chance at no cost to the caller, who has already stopped waiting.
Additional context
Tests. Four new tests in
DirectEventProcessorTestcover delivered events, nothing to send, offline, and the timeout expiring first. The full unit suite and the instrumented suite (126 tests on an emulator) pass locally.Test app. Gains two controls that show what this tier can and cannot do:
FlushOnCrashHandlercallsflushAndWaitwith a two-second budget before handing the crash on, so the events are delivered.SIGKILL. Nothing can run, so the events are lost. That is the gap tier 3 (on-disk persistence) closes.Version. The new methods are marked
@since 5.17.0; adjust if the release lands under a different version.Note
Overview
Adds
flushAndWait(long timeout, TimeUnit unit)onLDClientInterfaceso apps can flush buffered analytics events and learn whether delivery succeeded before the process or client goes away—unlike fire-and-forgetflush().DirectEventProcessorimplements timedblockingFlush: delivery runs on the scheduler via aCallableand returns true when the service accepts the payload (or there is nothing to send), false when offline, stopped, send fails, or the wait times out. On timeout the in-flight HTTP work is not cancelled because the buffer was already drained.LDClient.flushAndWaitapplies one timeout budget across all configured environments (remaining time is shared, not per env) and returns false if the client is closed.EventProcessorgets a default timedblockingFlushso older custom processors still compile (unbounded flush, reports success).Tests cover success, empty buffer, offline, timeout, closed client, and instrumented
LDClientpaths. The test app adds Eval+Kill now vs Eval+Crash now andFlushOnCrashHandlerto demonstrate rescue viaflushAndWaitin an uncaught-exception handler versus instant SIGKILL.Reviewed by Cursor Bugbot for commit 4ac8572. Bugbot is set up for automated code reviews on this repo. Configure here.