{{ message }}
Repository navigation
Commit 942d621
authored
feat(server): add multi-replica cluster mode (a2aproject#1281)
Adds a2a.server.cluster, key interfaces (__VersionedTaskStore__ and
__TaskEventStream__) for running the A2A server as several replicas,
with no need to route a task's requests back to the replica that started
it, and wires DefaultRequestHandlerV2 to use them so any replica can
serve any request for a remote task. __Opt-in and backward compatible:__
with no event stream and a plain (non-versioned) TaskStore, pre-existing
behavior is preserved.
__Key changes:__
- VersionedTaskStore - a task store that keeps track of which revision
of a task a replica last read. When that replica saves, the store checks
the task has not changed in the meantime and refuses the write / retries
if it has, instead of quietly overwriting whatever another replica just
did. VersionedDatabaseTaskStore is the database-backed implementation.
- TaskEventStream - a shared record of the events a task produces, so a
replica that is not running the agent can still see the event stream.
This lets a client reconnect to any replica and keep receiving updates
for a task running elsewhere. DatabaseTaskEventStream is the
database-backed implementation.
- Schema for both: a new task_versions table and a new task_events
table. The tasks table is unchanged.
- New optional event_stream argument; task_store now accepts a plain or
a versioned store and is still exposed unchanged as self.task_store.
- Cancel works from any replica. The cancelling replica records the
cancellation in the shared store, and the replica actually running the
agent notices on its next save and stops.
- Resubscribe works from any replica. It returns the current task, then
follows the shared event stream from that point on, so the client sees
no gap and no duplicates.
- Alembic revision b5e3d1c8a2f7 (revises 38ce57e08137) creates the
task_versions table and the task_events table with an index on task_id.
It only creates tables and does not alter existing ones.
- A runnable sample showing how to deploy the server as several
replicas: it starts N replicas as separate processes sharing one
database, and a driver starts a task on one replica, watches it from a
second, and cancels it from a third. Defaults to a shared SQLite file so
it runs with no external services; set A2A_CLUSTER_DSN to point every
replica at Postgres or MySQL instead.
__Tested:__
- Integration tests run two DefaultRequestHandlerV2 replicas against one
real Postgres or MySQL database, each replica on its own engine. They
cover send, resubscribe and cancel handled by a replica other than the
one running the agent, and a multi-turn input_required conversation that
moves between replicas.
- An integration test sends follow-ups to the same task from both
replicas at once and asserts that neither turn is lost.
- Unit tests cover the same cross-replica flows without a database,
using in-memory versions of the shared store and event stream.
- Unit tests for the migration's full up/down cycle, idempotency and
offline SQL generation.
- Adds the database-backed cluster tests to scripts/run_db_tests.sh.
Run the sample from the repository root:
```
python -m samples.clustermode.run_cluster --replicas 3 --base-port 41241
python -m samples.clustermode.exercise --host 127.0.0.1 --ports 41241,41242,41243
```
__Design discussion:__
a2aproject#1224
Release-As: 1.2.01 parent dc5a5da commit 942d621
34 files changed
Lines changed: 5315 additions & 59 deletions
File tree
- .github/actions/spelling
- samples/clustermode
- scripts
- src/a2a
- migrations
- versions
- server
- agent_execution
- cluster
- request_handlers
- tasks
- tests
- integration
- migrations/versions
- server
- cluster
- request_handlers
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
Whitespace-only changes.

0 commit comments