Skip to content

Feature request: native SpannerSessionService (no SQLAlchemy dependency) #4772

Description

@pztang9

Summary

Spanner users of DatabaseSessionService are blocked from upgrading past ADK 1.18 because there is no official async SQLAlchemy dialect for Spanner. ADK 1.18+ requires create_async_engine, but sqlalchemy-spanner only provides a synchronous dialect. Users must either maintain a custom async wrapper or stay pinned to old ADK versions.

A native SpannerSessionService using the google-cloud-spanner client directly would unblock Spanner users and eliminate an entire class of bugs caused by the SQLAlchemy abstraction layer.

Motivation

1. No official async Spanner dialect for SQLAlchemy

sqlalchemy-spanner (maintained by Google in googleapis/python-spanner-sqlalchemy) only provides a synchronous dialect. Post-1.18, DatabaseSessionService requires an async engine (create_async_engine + async_sessionmaker). Spanner users must write and maintain a custom async wrapper to bridge this gap, which is fragile and unsupported.

2. SQLAlchemy abstracts away Spanner-specific features

DatabaseSessionService uses a single async_sessionmaker for all operations. This means read-only methods (get_session, list_sessions) run inside read-write transactions, causing RetryAborted errors from Spanner's OCC when concurrent writes are in flight (#4771). The google-cloud-spanner client natively supports read-only snapshots, but SQLAlchemy has no way to express this.

3. Small, well-defined interface

BaseSessionService defines only 5 operations: create_session, get_session, list_sessions, delete_session, append_event. ADK already has multiple implementations of this interface (InMemorySessionService, DatabaseSessionService, VertexAiSessionService). A SpannerSessionService fits naturally alongside them.

4. Precedent

There is already a feature request for FirestoreSessionService (#3776) based on the same reasoning — that DatabaseSessionService forces users into managing a SQL database when a purpose-built implementation would be simpler and more reliable.

Proposed design

A SpannerSessionService that:

  • Uses google-cloud-spanner (AsyncClient or thread-pooled Client) directly
  • Uses read-only snapshots for get_session() and list_sessions() (eliminates RetryAborted on reads)
  • Uses read-write transactions only for create_session(), append_event(), and delete_session()
  • Reuses the same table schema as DatabaseSessionService (schemas v0/v1) for migration compatibility
  • Lives under google.adk.sessions.spanner_session_service
  • Has google-cloud-spanner as an optional dependency (extras: [spanner])

Estimated scope: ~200-300 lines for the 5 CRUD operations, plus schema setup.

Related

Activity

  1. added
    services[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc
    on Mar 9, 2026
  2. added theissue type on Mar 9, 2026
  3. klateefa commented on Mar 11, 2026

    @klateefa

    @pztang9 Thank you for submitting this feature request. It’s currently under review by our team, and I’ll keep you posted on any updates. We appreciate your input!

  4. added
    needs review[Status] The PR/issue is awaiting review from the maintainer
    on Mar 11, 2026
  5. Danigm-dev commented on May 26, 2026

    @Danigm-dev

    Adding our team's case to the thread. We're in production and got caught by the gap between documentation and runtime reality:

    • The Spanner integration page states inside the code example: "Optionally, Spanner can be used as the Database Session Service for production."
    • AGENTS.md on main lists Spanner under sessions/.
    • The canonical Sessions documentation lists only PostgreSQL, MySQL and SQLite as DatabaseSessionService backends.
    • At runtime, it fails with async-dialect / DDL-in-tx exactly as described above.
    • Previous code paths in v1.17 (the if dialect.name == "spanner+spanner" branch with SpannerPickleType) confirm Spanner was an intentional target before 1.18 broke it.

    We migrated to ADK trusting the docs. Discovering this mid-migration, with Spanner as a hard infra constraint, was costly. Two paths forward would both unblock us:

    1. Ship SpannerSessionService natively per the proposal above (preferred).
    2. As a minimum, update the toolset doc page and AGENTS.md to stop advertising Spanner as a session backend until it actually is one. Right now both statements are simultaneously published and contradictory.

    Happy to share details of our custom implementation if useful as a starting point.

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

Metadata

Metadata

Labels

needs review[Status] The PR/issue is awaiting review from the maintainerservices[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions