Skip to content

fix: Preserve event delivery with gevent queues - #867

Merged
marandaneto merged 6 commits into
mainfrom
fix/gevent-865
Aug 12, 2026
Merged

fix: Preserve event delivery with gevent queues#867
marandaneto merged 6 commits into
mainfrom
fix/gevent-865

Conversation

@marandaneto

@marandanetomarandaneto commented Aug 12, 2026

Copy link
Copy Markdown
Member

💡 Motivation and Context

gevent 25.4.1 and later replaces queue.Queue when monkey.patch_all() runs. The replacement supports the public queue API but does not expose the synchronization attributes used by the PostHog consumer and flush paths. Consumer threads then stop with an AttributeError, flush fails, and captured events remain queued.

Fixes#865.

PostHog now checks whether the active queue exposes the synchronization interface required by its consumers. A working standard library queue is used without importing gevent. If gevent replaced the queue, PostHog retrieves the original queue through gevent.monkey.get_original().

If a lane cannot create a usable queue because gevent is unavailable or broken, the SDK logs the specific queue initialization or gevent recovery failure and disables asynchronous capture for that lane. Synchronous capture and queue-independent APIs such as feature flags remain available. A minimal private queue remains only so flush, shutdown, and fork cleanup stay safe on the unavailable lane.

The included example shows capture from gunicorn workers started with --worker-class gevent --preload.

💚 How did you test it?

  • Reproduced the failure before the fix with gevent monkey-patching. The consumer stopped on a missing queue synchronization attribute, batch_post was not called, and the event remained queued.
  • Added a subprocess regression test that applies gevent monkey-patching before importing the SDK. It verifies the client restores the original queue interface, sends the event, and drains the queue.
  • Added fallback tests that verify a working queue does not import gevent and an unusable queue disables only asynchronous capture without raising. Synchronous capture and feature flag requests remain available.
  • Ran the full test suite after addressing review feedback: 2063 passed, 15 skipped.
  • Ran the focused client, consumer, fork, and gevent suites after the fallback change: 273 passed.
  • Ran Ruff formatting and lint checks, mypy, the public API check, the warning-free import check, and the lockfile check.
  • Ran the gunicorn gevent preload example against a local HTTP receiver and confirmed that both test requests were delivered.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Implemented with Pi under human direction. We compared a copied CPython queue implementation with gevent's supported patch-state APIs. We chose capability detection with is_object_patched() and get_original() because it preserves the standard library queue type with a smaller maintenance surface. ReviewHog later identified that client-wide disabling would also suppress synchronous capture and feature flags; queue failures are now lane-local with regression coverage for those paths.

@marandanetomarandaneto self-assigned this Aug 12, 2026
@github-actions

github-actionsBot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

posthog-python Compliance Report

Date: 2026-08-12 17:11:44 UTC
Duration: 256449ms

✅ All Tests Passed!

111/111 tests passed


Capture_V1 Tests

94/94 tests passed

View Details
TestStatusDuration
Endpoint And Method.Targets V1 Endpoint517ms
Endpoint And Method.Does Not Use Legacy Endpoints511ms
Required Headers.Has Authorization Bearer Header511ms
Required Headers.Has Content Type Json510ms
Required Headers.Has Posthog Sdk Info Format511ms
Required Headers.Has Posthog Attempt Header510ms
Required Headers.Has Posthog Request Id511ms
Required Headers.Has Posthog Request Timestamp510ms
Required Headers.Has User Agent510ms
Body Format.Body Has Created At And Batch510ms
Body Format.No Api Key In Body511ms
Body Format.No Sent At In Body510ms
Event Format.Event Has Required Root Fields510ms
Event Format.Event Uuid Is Valid511ms
Event Format.Event Timestamp Is Rfc3339511ms
Event Format.Distinct Id Is String510ms
Event Format.Distinct Id At Root Not Properties511ms
Event Format.Custom Properties Preserved510ms
Event Format.Set Properties Preserved510ms
Event Format.Set Once Properties Preserved511ms
Event Format.Groups Properties Preserved510ms
Event Format.Sdk Generates Uuid If Not Provided511ms
Event Format.Event Has Required Root Fields Batch514ms
Event Format.Event Uuid Is Valid Batch514ms
Event Format.Event Timestamp Is Rfc3339 Batch514ms
Event Format.Distinct Id Is String Batch514ms
Event Format.Distinct Id At Root Not Properties Batch514ms
Event Format.Custom Properties Preserved Batch515ms
Event Format.Set Properties Preserved Batch515ms
Event Format.Set Once Properties Preserved Batch514ms
Event Format.Groups Properties Preserved Batch514ms
Event Format.Sdk Generates Uuid If Not Provided Batch514ms
Batch Behavior.Multiple Events In Single Batch518ms
Batch Behavior.Batch Envelope Smoke516ms
Batch Behavior.Flush With No Events Sends Nothing506ms
Batch Behavior.Flush At Triggers Batch1012ms
Batch Behavior.Created At Reflects Batch Creation Time511ms
Deduplication.Generates Unique Uuids518ms
Deduplication.Different Events Same Content Different Uuids512ms
Deduplication.Preserves Uuid On Retry6519ms
Deduplication.Preserves Timestamp On Retry6520ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry6519ms
Deduplication.No Duplicate Events In Batch519ms
Header Behavior On Retry.Attempt Header Starts At One511ms
Header Behavior On Retry.Attempt Header Increments On Retry13529ms
Header Behavior On Retry.Request Id Preserved On Retry6517ms
Header Behavior On Retry.Different Requests Have Different Request Ids3020ms
Header Behavior On Retry.Request Timestamp Changes On Retry6519ms
Response Format Validation.Success Response Has Uuid Keyed Results512ms
Response Format Validation.Success Response Has Ok For Each Event515ms
Response Format Validation.Success No Retry After When All Ok513ms
Response Format Validation.Success Retry After Present When Retry Events1516ms
Response Format Validation.Success No Retry After When Drop Only513ms
Response Format Validation.Response Echoes Request Id510ms
Retry Behavior.Retries On 4086515ms
Retry Behavior.Retries On 5006520ms
Retry Behavior.Retries On 5038524ms
Retry Behavior.Retries On 5046520ms
Retry Behavior.Retryable Errors Have Retry After3515ms
Retry Behavior.Respects Retry After On Retryable Error11523ms
Retry Behavior.Does Not Retry On 4002512ms
Retry Behavior.Does Not Retry On 4012514ms
Retry Behavior.Does Not Retry On 4022513ms
Retry Behavior.Does Not Retry On 4132514ms
Retry Behavior.Does Not Retry On 4152513ms
Retry Behavior.Non Retryable Errors Have No Retry After2514ms
Retry Behavior.Implements Backoff22526ms
Retry Behavior.Max Retries Respected22536ms
Partial Batch Handling.Handles 200 Full Success2514ms
Partial Batch Handling.Handles 200 With All Ok3516ms
Partial Batch Handling.Does Not Retry Dropped Events3516ms
Partial Batch Handling.Does Not Retry Limited Events3516ms
Partial Batch Handling.Prunes Ok Events On Partial Retry6522ms
Partial Batch Handling.Prunes Dropped Events On Partial Retry6518ms
Partial Batch Handling.Retries Only Retry Events From Partial6523ms
Partial Batch Handling.Partial Retry Preserves Uuids6522ms
Partial Batch Handling.Partial Retry Attempt Header Increments6517ms
Partial Batch Handling.Partial Retry Request Id Preserved6522ms
Partial Batch Handling.Respects Retry After On Partial8521ms
Partial Batch Handling.Unknown Result Treated As Terminal3517ms
Partial Batch Handling.Mixed Ok Drop Limited No Retry3517ms
Compression.Sends Gzip Content Encoding517ms
Compression.No Content Encoding When Disabled511ms
Compression.Compressed Body Is Decompressible510ms
Error Handling.Does Not Retry On Unknown 4Xx2513ms
Event Options.Cookieless Mode Override511ms
Event Options.Disable Skew Correction Override510ms
Event Options.Process Person Profile Override511ms
Event Options.Product Tour Id Override510ms
Event Options.Unset Options Omitted511ms
Event Options.Options Override In Batch513ms
Geoip And Historical Migration.Geoip Disable Injected Into Properties511ms
Geoip And Historical Migration.Historical Migration Set In Body510ms
Geoip And Historical Migration.Historical Migration Absent By Default510ms

Feature_Flags Tests

17/17 tests passed

View Details
TestStatusDuration
Request Payload.Request With Person Properties Device Id12ms
Request Payload.Flags Request Uses V2 Query Param9ms
Request Payload.Flags Request Hits Flags Path Not Decide9ms
Request Payload.Flags Request Omits Authorization Header9ms
Request Payload.Token In Flags Body Matches Init10ms
Request Payload.Groups Round Trip10ms
Request Payload.Groups Default To Empty Object10ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False9ms
Request Payload.Disable Geoip Omitted Defaults To False9ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key10ms
Request Lifecycle.No Flags Request On Init Alone3ms
Request Lifecycle.No Flags Request On Normal Capture510ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests14ms
Request Lifecycle.Mock Response Value Is Returned To Caller9ms
Retry Behavior.Retries Flags On 502313ms
Retry Behavior.Retries Flags On 504313ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event512ms

@marandaneto
marandaneto marked this pull request as ready for review August 12, 2026 07:34
@marandaneto
marandaneto requested a review from a team as a code ownerAugust 12, 2026 07:34
@greptile-apps

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix: preserve event delivery with gevent..." | Re-trigger Greptile

@posthog

posthogBot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Found 1 must fix, 0 should fix, 0 consider.

Published 1 finding (view the review).

@posthog

posthogBot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthogposthogBot 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.

ReviewHog Report

Changes

Issues: 1 issue

Files (5)
  • .sampo/changesets/gevent-queue-compat.md
  • examples/gevent_gunicorn.py
  • posthog/_disabled_lane_queue.py
  • posthog/client.py
  • pyproject.toml

Comment threadposthog/client.py Outdated
@marandaneto
marandaneto merged commit 9beed86 into mainAug 12, 2026
38 checks passed
@marandaneto
marandaneto deleted the fix/gevent-865 branch August 12, 2026 17:42
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants

@marandaneto@dustinbyrne