Skip to content

PHP: Background flush timer fires despite custom event logging adapter (thread affinity error) #39

@thehammer

Description

@thehammer

Problem

When using statsig-php-core (v0.16.5) with a custom EventLoggingAdapter in a long-running PHP process (Laravel queue worker), the SDK's internal background flush timer fires every ~60 seconds on a Rust tokio thread and attempts to call the PHP adapter's logEvents(). This fails with:

ERROR [Statsig::FunctionBasedEventLoggingAdapterC] Attempted to run FunctionBasedEventLoggingAdapterC on a thread other than the one it was registered on

After 5 retries, events are dropped:

WARN [Statsig::EvtLogger] Scheduled (Max Time) flush failed after 6 attempt(s). 1 Event(s) will be dropped.

Context

  • PHP's FFI callbacks are bound to the OS thread that created them (this is a PHP limitation, not a Statsig bug)
  • The thread affinity check added in PR #2141 correctly detects this and prevents the crash
  • However, there's no way to disable the background flush timer that triggers it

What we've tried

  • event_logging_flush_interval_ms: 2147483647does not work, this parameter is deprecated and ignored in the FFI layer (prefixed with _)
  • event_logging_max_queue_size: 2147483647 — prevents size-triggered flushes but not time-triggered
  • disable_all_logging: true — prevents event enqueueing entirely, which we need for exposure logging

Our workaround

We flush events synchronously on the main PHP thread after each checkGate():

$result = $this->statsig->checkGate($user, $gateName);
$this->statsig->flushEvents(); // Runs on main thread, succeeds
return $result;

This works — events are delivered via our custom Redis adapter. The background flush is redundant but can't be disabled, producing noisy error logs every 60 seconds.

We've suppressed the log output with output_log_level: 'none' but the underlying retry/drop cycle still runs.

Environment

  • statsig/statsig-php-core v0.16.5
  • PHP 8.3 (NTS) with FFI enabled
  • Long-running Laravel queue workers on AWS ECS (Fargate)
  • Custom EventLoggingAdapter that writes to Redis

Requested fix

Any of these would resolve it:

  1. Skip background flush when a PHP function-based adapter is in use — since PHP FFI callbacks can never work from a background thread, the timer is always wasted work
  2. Add a disable_background_flush option — let the caller take responsibility for flushing
  3. Honor event_logging_flush_interval_ms — it's currently deprecated/ignored, but if it worked, setting it to a very large value would be an effective workaround
  4. Make ThreadFailure non-retryable — at minimum, don't retry 5 times and log 6 error lines per minute when the failure is guaranteed to repeat

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions