feat: OpenFeature track and event delivery reliability - #252
feat: OpenFeature track and event delivery reliability#252fabriziodemaria wants to merge 16 commits into
Conversation
|
Consider making The |
|
Here's a failing test that reproduces the issue under production conditions (batch flush policy + slow upload). It currently fails with The fix should make this pass (e.g. buffering @Test
fun stopDrainsAllEventsWhenFlushPolicyBlocksWriter() {
val slowUploader = SlowEventUploader(uploadDelayMillis = 3_000)
val batchFlush = object : FlushPolicy {
private var count = 0
override fun reset() { count = 0 }
override fun hit(event: EngineEvent) { count++ }
override fun shouldFlush(): Boolean = count > 4
}
val engine = EventSenderEngineImpl(
eventStorage = storage,
clientSecret = "secret",
uploader = slowUploader,
flushPolicies = mutableListOf(batchFlush),
dispatcher = Dispatchers.IO,
sdkMetadata = SdkMetadata("id", "1.0"),
debugLogger = null
)
repeat(12) { engine.emit("event-$it", mapOf(), mapOf()) }
Thread.sleep(100)
engine.stop()
assertEquals(
"All 12 events should be written to storage before stop() returns",
12,
storage.storedEventCount()
)
}
private class SlowEventUploader(
private val uploadDelayMillis: Long
) : EventSenderUploader {
val uploadedEventNames = mutableListOf<String>()
override suspend fun upload(events: EventBatchRequest): Boolean {
kotlinx.coroutines.delay(uploadDelayMillis)
synchronized(uploadedEventNames) {
uploadedEventNames.addAll(
events.events.map { it.eventDefinition.removePrefix("eventDefinitions/") }
)
}
return true
}
}
fun storedEventCount(): Int = synchronized(this) {
currentEvents.size + readyEvents.values.sumOf { it.size }
} |
|
Addressed the
The earlier |
Use a conflated sendChannel with trySend so the writer loop never blocks on flush signaling while upload holds uploadMutex. Adds stopDrainsAllEventsWhenFlushPolicyBlocksWriter repro from review. Addresses vahidlazio feedback on PR #252.
Use a conflated sendChannel with trySend so the writer loop never blocks on flush signaling while upload holds uploadMutex. Adds stopDrainsAllEventsWhenFlushPolicyBlocksWriter repro from review. Addresses vahidlazio feedback on PR #252.
c214081 to
dcc7cab
Compare
Merge per-event OpenFeature context with session context, map tracking details to event data, and allow explicit context overrides in the payload — matching Swift provider behavior.
Upload pending batches at startup, flush and upload on stop(), and add an optional periodic flush interval so low-volume events are not left on disk indefinitely.
Do not assign the companion Instance field; main never cached instances and tests rely on a fresh engine per ConfidenceFactory.create() call.
Engine reliability fixes: - Bound stop()'s final upload with a 2s timeout so it can never block the caller indefinitely; unsent batches are retried at next startup - Serialize uploadReadyBatches with a mutex so the flush consumer, startup retry and stop() can no longer upload the same batch twice - Skip uploading empty batches, so periodic flushing on an idle app no longer makes an HTTP request per interval - Enqueue emit()/flush() synchronously on a buffered channel and drain it in stop(), so every event accepted before stop() reaches disk - Open the current events file in append mode: a restart previously truncated the prior session's unsealed events API and mapping fixes: - Log a debug warning when event data's "context" field overrides the evaluation context; deprecate the now-unthrown InvalidContextInMessage - Preserve Long/Float tracking values instead of mapping them to Null - Reject non-positive eventFlushIntervalMillis in ConfidenceFactory - Regenerate the API dump (create() overload was hand-edited, HttpError was missing) Test fixes: - Fix reliability tests: UnconfinedTestDispatcher is not a type, and advanceUntilIdle hangs forever on the self-rescheduling interval job - Make the storage fake honor deletion and seal empty batches like the real implementation; assert exact upload counts - Add tests for drain-on-stop, emit-after-stop, idle-interval no-upload and Long/Float value mapping - Reset the shared minBatchSizeFlushPolicy in setup so EventSenderIntegrationTest is no longer order-dependent Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Clarify the rendezvous-to-buffered channel change for stop() drain reliability and trySend() semantics in response to PR review. Co-authored-by: Cursor <cursoragent@cursor.com>
Use a conflated sendChannel with trySend so the writer loop never blocks on flush signaling while upload holds uploadMutex. Adds stopDrainsAllEventsWhenFlushPolicyBlocksWriter repro from review. Addresses vahidlazio feedback on PR #252.
dcc7cab to
aebae9a
Compare
fabriziodemaria
left a comment
There was a problem hiding this comment.
Bugbot follow-up review: three reliability issues to address before merge.
Drain accepted events asynchronously before bounding network delivery, and snapshot caller-owned payload maps so queued events remain stable. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Summary
track()with Confidence event conventions (details → data, context merge,"value"/"context"overrides).shutdown()→flush().Test plan
EventSenderEngineReliabilityTestSupersedes #253 (reliability) — both changes are on
openfeature-track-context.