Commit fa93ee7
fix: Implement reference counting for EventQueue to prevent premature MainQueue closure
Previously, MainQueues closed immediately when any ChildQueue closed, causing
race conditions where resubscription attempts would fail with TaskNotFoundError.
This was particularly problematic in scenarios with multiple concurrent consumers
or when clients resubscribed to active tasks.
This commit introduces a reference counting mechanism where MainQueues track
active ChildQueues and only close when all children have closed, preventing
premature closure while consumers are still active.
Changes:
EventQueue architecture:
- MainQueue now maintains a list of active ChildQueues
- ChildQueue.close() notifies parent via childClosing() callback
- MainQueue only closes when: immediate=true OR all children closed
- Added getActiveChildCount() for testing reference counting mechanism
Non-blocking message support:
- Non-blocking non-final tasks keep MainQueue alive for resubscription
- Added close(immediate, notifyParent) method to EventQueue
- ChildQueue can close without decrementing parent reference count
- DefaultRequestHandler detects non-blocking non-final scenarios
- Support multiple messages to same task after non-blocking sendMessage
- AUTH_REQUIRED state now continues processing in background
ResultAggregator improvements:
- consumeAndBreakOnInterrupt() supports blocking and non-blocking modes
- AUTH_REQUIRED events trigger background continuation for additional messages
- Refactored to use AsyncUtils.consumer() for consistent event processing
- EventConsumer.close() explicitly closes queue to prevent resource leaks
Robustness improvements:
- onResubscribeToTask() calls createOrTap() for non-final tasks when queue missing
- Provides recovery path for edge cases (server restart, explicit queue closure)
- Allows client resubscription even if MainQueue was unexpectedly removed
- Note: historical events unavailable, only future events delivered
Test coverage:
- Added testMainQueueReferenceCountingWithMultipleConsumers()
- Added testNonBlockingWithMultipleMessages()
- Verified TCK compliance for resubscription behavior
Fixes race condition where:
1. Client A subscribes to task → ChildQueue A created
2. Client B resubscribes → ChildQueue B created
3. Client A disconnects → ChildQueue A closes → MainQueue closed prematurely
4. Client B receives no further events (incorrect)
With reference counting:
- MainQueue stays alive while any ChildQueue exists
- All active consumers receive events until MainQueue closes
- Resubscription works reliably for active tasks
- Multiple messages can be sent to same non-blocking task
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>1 parent 75b4150 commit fa93ee7
File tree
24 files changed
+905
-76
lines changed- .serena
- cache/java
- extras/queue-manager-replicated/core/src
- main/java/io/a2a/extras/queuemanager/replicated/core
- test/java/io/a2a
- extras/queuemanager/replicated/core
- server/events
- reference
- grpc/src/test/java/io/a2a/server/grpc/quarkus
- jsonrpc/src/test
- java/io/a2a/server/apps/quarkus
- resources
- rest/src/test/java/io/a2a/server/rest/quarkus
- server-common/src
- main/java/io/a2a/server
- events
- requesthandlers
- tasks
- test/java/io/a2a/server
- events
- requesthandlers
- tests/server-common/src/test/java/io/a2a/server/apps/common
- transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler
24 files changed
+905
-76
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Binary file not shown.
| 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 | + | |
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
97 | 102 | | |
98 | 103 | | |
99 | 104 | | |
| |||
Lines changed: 6 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| 21 | + | |
20 | 22 | | |
21 | 23 | | |
22 | 24 | | |
| |||
147 | 149 | | |
148 | 150 | | |
149 | 151 | | |
| 152 | + | |
150 | 153 | | |
151 | | - | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
152 | 157 | | |
153 | 158 | | |
154 | 159 | | |
| |||
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
108 | 116 | | |
109 | 117 | | |
110 | 118 | | |
| |||
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
142 | 150 | | |
143 | 151 | | |
144 | 152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
144 | 152 | | |
145 | 153 | | |
146 | 154 | | |
| |||
0 commit comments