diff --git a/src/user_engine/adapters/postgres.py b/src/user_engine/adapters/postgres.py index 7be6900..1abc547 100644 --- a/src/user_engine/adapters/postgres.py +++ b/src/user_engine/adapters/postgres.py @@ -591,7 +591,12 @@ class PostgresUserEngineStore: """, (LATEST_SCHEMA_VERSION,), ) - return cursor.fetchone() is not None + present = cursor.fetchone() is not None + # psycopg starts a transaction even for this readiness SELECT. + # End it immediately so probes cannot retain relation locks that + # block the next replica's idempotent schema migration. + self.connection.rollback() + return present except Exception: self.connection.rollback() return False diff --git a/tests/test_postgres_store_adapter.py b/tests/test_postgres_store_adapter.py index ce009da..cf230b9 100644 --- a/tests/test_postgres_store_adapter.py +++ b/tests/test_postgres_store_adapter.py @@ -19,10 +19,12 @@ class PostgresStoreAdapterTests(unittest.TestCase): ) def test_ready_is_false_before_migration(self): - store = PostgresUserEngineStore(_FakePostgresConnection()) + connection = _FakePostgresConnection() + store = PostgresUserEngineStore(connection) self.assertFalse(store.ready) self.assertIsNone(store.schema_version) + self.assertEqual(2, connection.rollback_count) def test_pending_query_keeps_failed_non_dead_letter_events_retryable(self): connection = _FakePostgresConnection() @@ -49,6 +51,7 @@ class _FakePostgresConnection: list[dict[str, Any]], ] | None = None self.last_sql = "" + self.rollback_count = 0 def cursor(self) -> "_FakePostgresCursor": return _FakePostgresCursor(self) @@ -65,6 +68,7 @@ class _FakePostgresConnection: self._snapshot = None def rollback(self) -> None: + self.rollback_count += 1 if self._snapshot is None: return (