Skip to content

Fix disposal race condition in AsyncBatchHandler - #158

Merged
haacked merged 1 commit into
mainfrom
haacked/fix-flushing-behavior
Feb 25, 2026
Merged

Fix disposal race condition in AsyncBatchHandler#158
haacked merged 1 commit into
mainfrom
haacked/fix-flushing-behavior

Conversation

@haacked

@haacked haacked commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Await background tasks during disposalDisposeAsync now stores and awaits both the timer and flush-signal Task references before attempting the final flush. Previously, cancellation fired without waiting for in-flight flushes, so the final FlushBatchesAsync could be silently skipped when _flushing was still 1.
  • Reorder resource disposal — CTS is now disposed after awaiting the background tasks (which need the token during cancellation), rather than immediately after CancelAsync.
  • Add race condition regression testFlushesItemWhenDisposedDuringInFlightFlush blocks a flush handler, starts disposal concurrently, then unblocks and verifies the item was sent.

Fixes #146

Test plan

  • All existing AsyncBatchHandler tests pass (FlushesBatchOnTimer, FlushesBatchWhenDisposed, DoesNotDisposeTwice, etc.)
  • New FlushesItemWhenDisposedDuringInFlightFlush test passes on both net8.0 and netcoreapp3.1
  • Verify in an IIS-hosted app that queued events are reliably flushed on AppDomain shutdown

@github-actions

github-actions Bot commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

posthog-dotnet Compliance Report

Date: 2026-02-25 23:23:33 UTC
Duration: 120001ms

✅ All Tests Passed!

29/29 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 155ms
Format Validation.Event Has Uuid 128ms
Format Validation.Event Has Lib Properties 111ms
Format Validation.Distinct Id Is String 111ms
Format Validation.Token Is Present 110ms
Format Validation.Custom Properties Preserved 118ms
Format Validation.Event Has Timestamp 110ms
Retry Behavior.Retries On 503 8124ms
Retry Behavior.Does Not Retry On 400 2021ms
Retry Behavior.Does Not Retry On 401 2013ms
Retry Behavior.Respects Retry After Header 8119ms
Retry Behavior.Implements Backoff 22133ms
Retry Behavior.Retries On 500 6119ms
Retry Behavior.Retries On 502 6119ms
Retry Behavior.Retries On 504 6119ms
Retry Behavior.Max Retries Respected 22023ms
Deduplication.Generates Unique Uuids 116ms
Deduplication.Preserves Uuid On Retry 6117ms
Deduplication.Preserves Uuid And Timestamp On Retry 13121ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 6121ms
Deduplication.No Duplicate Events In Batch 118ms
Deduplication.Different Events Have Different Uuids 111ms
Compression.Sends Gzip When Enabled 110ms
Batch Format.Uses Proper Batch Structure 109ms
Batch Format.Flush With No Events Sends Nothing 106ms
Batch Format.Multiple Events Batched Together 115ms
Error Handling.Does Not Retry On 403 2010ms
Error Handling.Does Not Retry On 413 2012ms
Error Handling.Retries On 408 6117ms

`DisposeAsync` now awaits both background tasks (`_timerTask` and `_flushSignalTask`) before attempting the final flush. Previously, cancellation and resource disposal happened without waiting for in-flight flushes to complete, so the final `FlushBatchesAsync` could be silently skipped when `_flushing` was still held at 1.

Fixes #146
@haacked
haacked marked this pull request as ready for review February 25, 2026 23:22
@haacked
haacked enabled auto-merge February 25, 2026 23:23
@haacked
haacked merged commit e8e2404 into main Feb 25, 2026
12 checks passed
@haacked
haacked deleted the haacked/fix-flushing-behavior branch February 25, 2026 23:23

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

haacked added a commit that referenced this pull request Feb 26, 2026
Await the polling task during disposal so in-flight API calls complete
before resources are disposed. Follows the same pattern established in
AsyncBatchHandler (PR #158).

Fixes #159
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AsyncBatchHandler should flush all items periodically regardless of FlushAt to prevent missed events

2 participants