Skip to content

fix(server): create the cluster tables lazily in VersionedDatabaseTaskStore get and delete - #1357

Open
cchinchilla-dev wants to merge 1 commit into
a2aproject:mainfrom
cchinchilla-dev:fix/cluster-store-lazy-tables
Open

cchinchilla-dev wants to merge 1 commit into
a2aproject:mainfrom
cchinchilla-dev:fix/cluster-store-lazy-tables

Conversation

@cchinchilla-dev

Copy link
Copy Markdown
Contributor

Description

Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Follow the CONTRIBUTING Guide.
  • Make your Pull Request title in the https://www.conventionalcommits.org/ specification.
    • Important Prefixes for release-please:
      • fix: which represents bug fixes, and correlates to a SemVer patch.
      • feat: represents a new feature, and correlates to a SemVer minor.
      • feat!:, or fix!:, refactor!:, etc., which represent a breaking change (indicated by the !) and will result in a SemVer major.
  • Ensure the tests and linter pass (Run bash scripts/format.sh from the repository root to format)
  • Appropriate docs were updated (if necessary)

Fixes #1356 🦕

Problem

VersionedDatabaseTaskStore creates its tables lazily, like DatabaseTaskStore, but only save() creates the cluster tables. get() and delete() initialize the underlying DatabaseTaskStore only, so when the store is used without calling initialize() and the first operation is a get() or a delete(), the query on task_versions fails:

store = VersionedDatabaseTaskStore(engine=create_async_engine('sqlite+aiosqlite:///:memory:'))

await store.get('missing', ServerCallContext())
# sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: task_versions

Fix

get() and delete() (src/a2a/server/cluster/database_task_store.py) now call self._ensure_cluster_tables() after initializing the underlying store, the same way save() does. delete() also initializes the underlying store before creating the cluster tables, so the tables are created in the same order as in save().

_ensure_cluster_tables() returns right away once the tables are ready, so stores that call initialize() are not affected.

Test

Tests were added to the existing suite at tests/server/cluster/test_database_versioned_task_store.py, using a new uninitialized_store fixture that builds the store on an empty database with create_table=True and without calling initialize():

  • test_get_without_initialize_creates_tables: get() of a missing task returns None.
  • test_delete_without_initialize_creates_tables: delete() of a missing task does not fail, and a later get() returns None.

Both tests fail on main with no such table: task_versions and pass with the fix. No existing test was modified or removed. Happy to adapt the test approach based on maintainer feedback.

@cchinchilla-dev
cchinchilla-dev requested a review from a team as a code owner October 20, 2026 08:40
@github-actions

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/cluster/database_task_store.py 96.43% 99.30% 🟢 +2.87%
src/a2a/server/tasks/database_task_store.py 92.47% 95.16% 🟢 +2.69%
Total 93.14% 93.23% 🟢 +0.09%

Generated by coverage-comment.yml

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: VersionedDatabaseTaskStore get() and delete() fail with "no such table" when initialize() was not called

2 participants