google / google/adk-python

Feature request: native SpannerSessionService (no SQLAlchemy dependency)

オープン
#4,772 コメント 2 件 リアクション 1 件 担当者 2 名 @wuliang229 が担当を希望しています GitHub で見る
needs review services
主要言語
Python
スター
21.5k
フォーク
4k
平均マージ
1日 14時間
マージ済み PR(30日)
37

説明

## 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

- #4771 — `get_session`/`list_sessions` use read-write transactions unnecessarily (symptom of the SQLAlchemy abstraction)
- #4751 — OCC corruption from concurrent `append_event` (Spanner concurrency semantics lost behind SQLAlchemy)
- #3776 — Feature request for native `FirestoreSessionService` (same pattern: purpose-built > generic ORM)
- #4673 — `get_session` loads all events (easier to optimize with direct Spanner queries)

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。