Skip to content

Releases: Automattic/newspack-event-logger-plugins

v2.4.42

Choose a tag to compare

@datapoke datapoke released this 07 May 22:49

[2.4.42] - 2026-05-07

Fixed

  • Newspack_Performance_Aggregator\SettingsSync::maybe_queue_sync — fail-closed polarity now matches the file's documented intent and Newspack_Event_Aggregator\SettingsSync (class-settings-sync.php:113). Previously, missing or non-true enable_workers was not treated as "skip" — the test was isset && false === $config['enable_workers'], which let null/missing/empty count as "yes hub-mode" and silently fanned tuning settings (log_events, custom_events, etc.) out to remote spokes. The file's own comment ("Only hub nodes (enable_workers=true) should sync settings") was the canonical intent; the code was the bug. New test: SettingsSyncTest::test_maybe_queue_sync_skips_when_workers_unset. (newspack-performance-aggregator/includes/class-settings-sync.php)

v2.4.41

Choose a tag to compare

@datapoke datapoke released this 07 May 21:59

Changed

  • Supervisor: removed the supervisor-wide restart-marker file ({base_directory}/restart_supervisor). check_config() now rebuilds worker_locks from the registered_readers / standalone_workers filters on every tick (every 15s), so plugin activation/deactivation propagates within one config-check tick without needing a marker. Supervisor::request_restart() now uses the per-lock Lock::request_restart channel on the supervisor's own lock dir (supervisor.lock.d/restart) — unified with the worker restart mechanism. kill_readers() no longer chains a supervisor restart, since the next tick's filter rebuild already drops the killed readers. Activation/deactivation propagation latency is preserved (≤15s).

Behavior preserved; one concept dropped, one detection branch removed, one cross-reference between request_restart() and kill_readers() eliminated.

Tests: 1591 / 1591 pass.

v2.4.40

Choose a tag to compare

@datapoke datapoke released this 06 May 02:20

[2.4.40] - 2026-05-05

Fixed

  • LogReader::get_live_positions(): self-initialize the Memcached connection. The dashboard's WorkersController calls this static method to read live worker cursor positions; on hubs (where newspack-performance-dashboards is active) and standalone servers (where newspack-performance-workers is active), Memcached::init() got called as a side effect of those plugins' REST controllers / cron handlers being constructed during request bootstrap, so subsequent Memcached::get() calls succeeded. On spokes that activate neither plugin (e.g. event-logger + event-jobs + event-dashboards + performance-logger only), nothing in the request lifecycle calls Memcached::init() before get_live_positions() queries memcache. Memcached::get() returns null when $memd is unset, so the dashboard always fell through to the 30-second offsetlog fallback — every worker appeared "stalled" except for a brief blip every 30s when the offsetlog flushed. Memcached::init() is idempotent ($init_attempted guards re-entry), so the lazy init here is a no-op when something else already initialized the connection. (newspack-event-logger/includes/cron/class-log-reader.php)

v2.4.39

Choose a tag to compare

@datapoke datapoke released this 06 May 00:58

[2.4.39] - 2026-05-05

Fixed

  • LogManager: process (start) no longer lands at line 28+ on wp-admin requests. message(), error(), warning(), info() now go through ensure_started() so the very first call from any path triggers log_process() first, putting process (start) at n=1 regardless of which entry point the caller used. Cron/job paths were unaffected (they explicitly called Log::ensure_started() up front), but admin hooks log via error()/warning() long before any start() happens, leaving process (start) stranded at whatever line counter we'd already reached. Re-entry safe — ensure_started() sets $this->started = true before calling log_process(). (newspack-performance-logger/includes/class-log-manager.php)
  • wp eventlog reqgrep: print the request_id as a header at the top of every formatted request, instead of synthesizing it after the entry that happens to have n=1. The old approach assumed process (start) was always first; on requests where it landed later the rid line attached to whatever unrelated entry was actually first, which made cross-referencing fragile. New header is always present, always at the top, regardless of entry order. The companion n===1 synthesis in format_entry() is removed. (newspack-performance-logger/includes/cli/class-reqgrep-command.php)

v2.4.38

Choose a tag to compare

@datapoke datapoke released this 06 May 00:12

[2.4.38] - 2026-05-05

Changed

  • WorkerBase: drop the redundant [EventLogger] FATAL: ... worker pN died: ... log line that handle_shutdown() emitted on every PHP fatal. PHP already writes its own PHP Fatal error: ... entry to the same log, with the same file/line/message — our duplicate just doubled the noise without adding signal. The [EventLogger] EXIT: branch is kept (PHP is silent about clean exit() / die(), so that line is the only signal those leave behind).

v2.4.35

Choose a tag to compare

@datapoke datapoke released this 05 May 19:42

Changed

  • Aggregator: post-spoke-add settings push is now queued via JobIntake instead of called inline. The previous v2.4.33 implementation called RemoteManager::sync_all_settings([$id]) synchronously from ServersController::create_item() / update_item(), which meant the admin "Add Server" REST response could block on outbound HTTP for up to ~3 minutes (REQUEST_TIMEOUT = 15s × 13 synced settings) if the new spoke URL was bad/slow. The new RemoteManager::queue_sync_all_settings( $server_ids ) helper iterates the newspack_event_aggregator_synced_settings filter and queues one sync_setting job per setting via JobIntake::queue, with the targeted server list embedded in the job payload. JobWorker picks them up within milliseconds and dispatches via the same handle_jobsync_setting path the periodic uses; the admin response returns immediately. handle_job's sync_setting branch now reads and forwards an optional servers parameter so per-spoke targeting survives the queue round-trip.

v2.4.34

Choose a tag to compare

@datapoke datapoke released this 05 May 18:57

Fixed

  • Jobs / Aggregator: enable_jobs=false was silently destructive. JobRouter and JobWorker never registered, but Firehose::write continued accumulating k:"job" entries from update_option fan-out, supervisor_periodic health checks, and any plugin using JobIntake. Symptom: aggregator settings stop syncing to spokes, FlameBuilder auto-tune writes never apply, and queued jobs sit unread in firehose for hours/weeks until retention deletes them. No errors anywhere — the only visible signal was wp eventlog worker types listing firehose-workers without a job-router handler. Two guardrails: (1) the admin Enable Jobs field renders a red dependency warning listing each active feature that requires the job pipeline (Aggregator settings sync, Performance Aggregator fan-out, FlameBuilder auto-tune), so toggling jobs off becomes a deliberate choice with surfaced consequences. (2) The aggregator's supervisor_periodic callback now checks enable_jobs before queueing health-check jobs and emits a rate-limited (1/hour) error_log when jobs are off but spokes are configured, so the broken state is loud instead of silent.

v2.4.33

Choose a tag to compare

@datapoke datapoke released this 05 May 18:21

Fixed

  • Aggregator: settings sync to a freshly-added (or re-enabled) spoke could lag by up to ~15 minutes. Two compounding bugs: (1) ServerRegistry is a process-lifetime singleton that caches $this->servers on first read; long-running JobWorker processes (which dispatch the periodic health_check job that runs sync_all_settings) kept a stale view of the server list until they hit MAX_RUNTIME_SECONDS = 595 and respawned, so a new spoke was invisible to in-flight workers. (2) Nothing in ServersController::create_item() triggered a sync — the new spoke depended entirely on the next 300-second supervisor periodic tick and a JobWorker with a fresh cache aligning. Fix: RemoteManager::health_check() and sync_all_settings() now call ServerRegistry::reset_cache() at the top so each dispatch starts from current data; sync_all_settings() accepts an optional ?array $server_ids for targeted pushes; create_item() and the false→true enabled transition in update_item() synchronously call RemoteManager::sync_all_settings( [ $id ] ) so user-initiated server changes don't depend on the periodic chain.

v2.4.32

Choose a tag to compare

@datapoke datapoke released this 05 May 18:07

Fixed

  • Aggregator: SSE slot TTL / heartbeat asymmetry caused 429 "Maximum concurrent SSE streams reached" rate-limiting on spokes after StreamMerger restarts. The browser side runs 5s heartbeat against a 10s slot TTL (2x headroom — slots free within ~5s of disconnect); the aggregator side ran 15s heartbeat against a 300s TTL (20x headroom — slots stayed reserved up to ~285s after a worker exited). With MAX_SSE_SLOTS = 10 per user_id:ip_hash, a few rapid restart cycles exhausted the pool. Brought SLOT_TTL_AGGREGATOR to 30s (same 2x ratio as browsers) and updated the constant's comment to explain the relationship.
  • Aggregator: SSE slots are now scoped per-partition instead of sharing the global MAX_SSE_SLOTS = 10 pool with browser tabs and other partitions. Memcached::{acquire,check,touch,release}_sse_slot() and sse_slot_key() accept an optional int $partition (default -1 preserves the browser-style shared pool). SSEControllerBase::start_sse_stream() threads the partition from $connected_data into the slot acquisition when is_aggregator=true, and stores it for matching releases/checks. The firehose/heartbeat endpoint accepts a partition arg, and StreamMerger::maybe_send_heartbeat() includes it in the POST so each partition refreshes its own keyed slot. Added a regression test covering pool-independence and partition-scoped lookup.

v2.4.31

Choose a tag to compare

@datapoke datapoke released this 05 May 15:47

Fixed

  • Aggregator: ServerRegistry::is_config_server() read Config::load_config('full'), but the aggregator plugin registers aggregator_servers in the extended option schema (newspack-event-aggregator.php), which causes load_config('full') to overlay the WP option event_logger_aggregator_servers onto the file defaults. Every WP-managed server therefore appeared as a "config-file server," tripping the immutability guards in remove() and update()Remove produced a generic 500 "Failed to delete server." in the admin UI, and update() silently stripped all fields except enabled. Switched to Config::load_config_defaults() so only file-defined servers are flagged. Added a regression test that reproduces the schema-extension scenario. (newspack-event-aggregator/includes/class-server-registry.php, tests/unit/ServerRegistryTest.php)