From f53459204c21dff9dfb25d7b815b87dbfb11dc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Contreras=20Guill=C3=A9n?= Date: Tue, 19 May 2026 14:29:39 +0200 Subject: [PATCH] release(v26.05.05): PostgresEventBus multi-worker safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps version from 26.5.4 → 26.5.5 and adds the CHANGELOG entry for the per-group ``pg_try_advisory_lock`` fix on ``pyfly.eda.adapters.postgres.PostgresEventBus._drain``. The fix itself shipped in #13 (commit a4c7eb5). This commit cuts the release. --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efc71c1..ad6d239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). --- +## v26.05.05 (2026-05-19) + +### Fixed — `PostgresEventBus` is multi-worker safe + +The Postgres EDA adapter (`pyfly.eda.adapters.postgres`) used a +per-group cursor (`pyfly_eda_offsets.last_event_id`) without any +row-level claim, so scaling consumers in the same group resulted in +**every replica dispatching every event in parallel**. The +`WHERE last_event_id < $1` guard on the cursor-advance UPDATE only +prevents going backwards — it doesn't prevent duplicate dispatch when +two replicas read the same offset concurrently. + +Wrapped `_drain` in `pg_try_advisory_lock(group_key)`. The key is a +deterministic SHA-256 fold of the consumer-group name into a signed +bigint. Whoever wins the lock drains the outbox; everyone else +returns immediately and waits for the next NOTIFY or poll tick. +Session-level lock — auto-releases on connection death, so a crashed +worker never zombies the group. + +The Kafka and Redis Streams adapters were already safe (their +respective brokers handle competitive consumption natively). + +Helper `_group_lock_key()` exposed for tests; pinned in +[`tests/eda/test_postgres_event_bus.py::TestGroupLockKey`](tests/eda/test_postgres_event_bus.py). + +--- + ## v26.05.04 (2026-05-08) ### Fixed — `pyfly.security` no longer needs `pyjwt` to import diff --git a/pyproject.toml b/pyproject.toml index 83e41ae..65fe893 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "pyfly" # CalVer YY.MM.PATCH — package metadata uses PEP 440 normalized form (26.5.4); # git tag, GitHub release and human-readable display use leading-zero form # (v26.05.04) to match the Java/.NET/Go siblings. -version = "26.5.4" +version = "26.5.5" description = "The official Python implementation of the Firefly Framework — DI, CQRS, EDA, hexagonal architecture, and more." readme = "README.md" license = "Apache-2.0"