a2aproject / a2aproject/a2a-dotnet

Task live coordination is process-local across server instances

Đang mở
#463 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
C#
Star
262
Fork
64
Merge trung bình
5 ngày 2 giờ
Pull request đã merge (30 ngày)
31

Mô tả

## Summary

Task persistence can be shared across server instances, but two live coordination paths in `A2AServer` are process-local:

- `SubscribeToTask` uses `ChannelEventNotifier` subscriber channels.
- `CancelTask` looks up the running task in `_backgroundCancellations`, an in-process dictionary of `CancellationTokenSource` instances.

Consequently, a request routed to a different process/pod can read and mutate the durable task while failing to notify the subscriber or interrupt the process that is executing the task.

This is not a task snapshot persistence issue. `GetTask` may return the correct durable state while live delivery or execution interruption has not occurred.

## Current behavior

### Subscribe across instances

From [`ChannelEventNotifier`](https://github.com/a2aproject/a2a-dotnet/blob/87fd44843dd16339cdb59c2ff547fe374ac46736/src/A2A/Server/ChannelEventNotifier.cs), subscribers and per-task locks are stored in process memory.

1. Pod B accepts `SubscribeToTask` and creates a local subscriber channel.
2. Pod A applies and persists a later task event.
3. Pod A notifies only subscribers registered in Pod A.
4. The subscriber on Pod B can miss the live event even though `GetTask` later returns the updated snapshot.

### Cancel across instances

In [`A2AServer.CancelTaskAsync`](https://github.com/a2aproject/a2a-dotnet/blob/fdf22d790cc3c80a157311c41d2ec0cd71815173/src/A2A/Server/A2AServer.cs#L822-L863), background execution cancellation uses `_backgroundCancellations.TryRemove(request.Id, ...)`.

1. Pod A owns a return-immediately background execution and its local cancellation token.
2. Pod B accepts `CancelTask` for the same durably stored task.
3. Pod B has no matching entry in its `_backgroundCancellations` dictionary.
4. The task may be projected or persisted as canceled by the handler/store while the actual work on Pod A continues consuming resources.

Application-level fencing can reject stale terminal writes, but it does not stop the running work.

## Expected behavior

For multi-instance deployments, the SDK should expose an explicit live-coordination extension point or clearly document that the default implementation requires routing affinity/single-instance hosting.

A robust application-provided implementation should be able to use:

- durable task state/events as the authority;
- a distributed notifier/backplane as a low-latency wake-up hint;
- durable replay or polling as a fallback for lost notifications;
- separate notification semantics for subscriber wake-up and execution cancellation.

The notifier must not become the source of truth, and duplicate, delayed, reordered, or lost notifications must be safe.

## Possible SDK directions

- Document that `ChannelEventNotifier` and `_backgroundCancellations` are process-local.
- Expose injectable abstractions for subscription notification and active-execution cancellation/routing while retaining the current in-memory defaults.
- Keep persistence (`ITaskStore`) separate from notification, but document how a distributed store and distributed coordination provider compose.
- Add deterministic two-server tests demonstrating the default limitation and validating custom distributed implementations.

## Minimal repros

### Subscribe

1. Register a subscriber for `taskId` on notifier/server A.
2. Apply or notify an event on notifier/server B.
3. Observe that A's subscriber does not receive it.
4. Repeat with one notifier and observe successful delivery.

### Cancel

1. Start return-immediately work for `taskId` on server A.
2. Share the task snapshot through a common `ITaskStore`.
3. Call `CancelTaskAsync` on server B.
4. Observe that server A's cancellation token is not canceled.
5. Repeat on server A and observe that local cancellation succeeds.

## Why this matters

A service can pass single-process tests and expose correct durable snapshots while still hanging subscriptions or continuing canceled work under load-balanced production routing. The topology limitation should be explicit, and applications need supported extension seams to provide cross-instance coordination.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.