diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b47ef6a..b0340917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [3.0.5] - 2026-04-03 + +### Fixed + +- explicit rollback on all transactions to force connection closed + ## [3.0.4] - 2026-04-02 ### Added diff --git a/README.md b/README.md index c1ded2bc..c1555554 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ --- -![Static Badge](https://img.shields.io/badge/Version-3.0.4-blue) +![Static Badge](https://img.shields.io/badge/Version-3.0.5-blue) [![PyPI version](https://img.shields.io/pypi/v/honcho-ai.svg)](https://pypi.org/project/honcho-ai/) [![NPM version](https://img.shields.io/npm/v/@honcho-ai/sdk.svg)](https://npmjs.org/package/@honcho-ai/sdk) [![Discord](https://img.shields.io/discord/1016845111637839922?style=flat&logo=discord&logoColor=23ffffff&label=Plastic%20Labs&labelColor=235865F2)](https://discord.gg/plasticlabs) diff --git a/docs/changelog/compatibility-guide.mdx b/docs/changelog/compatibility-guide.mdx index 801865fb..3c86ef49 100644 --- a/docs/changelog/compatibility-guide.mdx +++ b/docs/changelog/compatibility-guide.mdx @@ -30,7 +30,8 @@ This guide helps you match the right SDK version to your Honcho API version. New | Honcho API Version | TypeScript SDK | Python SDK | |-------------------|---------------|------------| -| v3.0.4 (Current) | v2.1.0 | v2.1.0 | +| v3.0.5 (Current) | v2.1.0 | v2.1.0 | +| v3.0.4 | v2.1.0 | v2.1.0 | | v3.0.3 | v2.1.0 | v2.1.0 | | v3.0.2 | v2.0.0+ | v2.0.0+ | | v3.0.1 | v2.0.0+ | v2.0.0+ | diff --git a/docs/changelog/introduction.mdx b/docs/changelog/introduction.mdx index 1c56b862..562778b3 100644 --- a/docs/changelog/introduction.mdx +++ b/docs/changelog/introduction.mdx @@ -27,7 +27,13 @@ Welcome to the Honcho changelog! This section documents all notable changes to t ### Honcho API and SDK Changelogs - + + ### Fixed + + - explicit rollback on all transactions to force connection closed + + + ### Added - JSONB metadata validation enforces 100 key limit and max depth of 5 (#419) diff --git a/docs/docs.json b/docs/docs.json index 6de93942..05053e44 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -29,7 +29,7 @@ "navigation": { "versions": [ { - "version": "v3.0.4", + "version": "v3.0.5", "api": { "openapi": [ "v3/openapi.json" diff --git a/pyproject.toml b/pyproject.toml index 5b384389..4000f4b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "honcho" -version = "3.0.4" +version = "3.0.5" description = "Honcho Server" authors = [ {name = "Plastic Labs", email = "hello@plasticlabs.ai"}, diff --git a/src/dependencies.py b/src/dependencies.py index ee0a4a80..66b1931e 100644 --- a/src/dependencies.py +++ b/src/dependencies.py @@ -26,8 +26,12 @@ async def get_db(): await db.rollback() raise finally: - if db.in_transaction(): - await db.rollback() + # Always send ROLLBACK unconditionally so the wire-level transaction + # is closed before the TCP connection drops. Supavisor v2 does NOT + # clean up orphaned transactions on client disconnect in transaction- + # pooling mode, so relying on `in_transaction()` (Python-side state) + # can leave the backend pinned with an open BEGIN. + await db.rollback() await db.close() @@ -57,8 +61,8 @@ async def tracked_db(operation_name: str | None = None): await db.rollback() raise finally: - if db.in_transaction(): - await db.rollback() + # Always send ROLLBACK unconditionally — see get_db() comment. + await db.rollback() await db.close() if token: # Only reset if we set it request_context.reset(token) diff --git a/src/main.py b/src/main.py index a8caceeb..571bbb54 100644 --- a/src/main.py +++ b/src/main.py @@ -154,7 +154,7 @@ app = FastAPI( title="Honcho API", summary="The Identity Layer for the Agentic World", description="""Honcho is a platform for giving agents user-centric memory and social cognition.""", - version="3.0.4", + version="3.0.5", contact={ "name": "Plastic Labs", "url": "https://honcho.dev", diff --git a/tests/test_dependencies.py b/tests/test_dependencies.py index 48b61501..c90643ec 100644 --- a/tests/test_dependencies.py +++ b/tests/test_dependencies.py @@ -52,7 +52,7 @@ async def test_get_db_sets_application_name_when_tracing_enabled( await dep_gen.aclose() request_context.reset(context_token) - assert fake_db.rollback_calls == 0 + assert fake_db.rollback_calls == 1 # unconditional rollback in finally assert fake_db.close_calls == 1 @@ -70,7 +70,7 @@ async def test_get_db_rolls_back_and_closes_when_consumer_raises( with pytest.raises(RuntimeError, match="boom"): await dep_gen.athrow(RuntimeError("boom")) - assert fake_db.rollback_calls == 1 + assert fake_db.rollback_calls == 2 # once in except, once in finally assert fake_db.close_calls == 1 @@ -99,7 +99,7 @@ async def test_tracked_db_creates_and_resets_task_context( stmt, params = fake_db.execute_calls[0] assert "set_config" in str(stmt) assert params == {"name": "task:cleanup_job:12345678"} - assert fake_db.rollback_calls == 0 + assert fake_db.rollback_calls == 1 # unconditional rollback in finally assert fake_db.close_calls == 1 @@ -122,7 +122,7 @@ async def test_tracked_db_preserves_existing_request_context( stmt, params = fake_db.execute_calls[0] assert "set_config" in str(stmt) assert params == {"name": "request:existing"} - assert fake_db.rollback_calls == 0 + assert fake_db.rollback_calls == 1 # unconditional rollback in finally assert fake_db.close_calls == 1 @@ -138,7 +138,7 @@ async def test_tracked_db_rolls_back_on_error_and_closes( async with real_tracked_db("operation"): raise ValueError("failed operation") - assert fake_db.rollback_calls == 1 + assert fake_db.rollback_calls == 2 # once in except, once in finally assert fake_db.close_calls == 1 diff --git a/uv.lock b/uv.lock index bb43a61b..a206b216 100644 --- a/uv.lock +++ b/uv.lock @@ -1282,7 +1282,7 @@ wheels = [ [[package]] name = "honcho" -version = "3.0.4" +version = "3.0.5" source = { virtual = "." } dependencies = [ { name = "alembic" },