feat: provide generic pull event queue - #354
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a pull-based event queue implementation, including a new pullQueueManager and pullReader to support polling events from a Puller, handling inactivity timeouts, and performing access checks. The review feedback highlights several critical issues: NewStaticPullerProvider fails to make Close a no-op as documented, which can prematurely close shared pullers; the channel-based closeSignal in pullReader is prone to deadlocks and should be replaced with context-based cancellation; a bug in the inactivity timeout logic returns ErrInactivityTimeout even when a new task is successfully retrieved, terminating the subscriber loop; and newMessage can be simplified to accept *a2a.Task directly to eliminate redundant type assertions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
This PR migrates the `examples/clustermode` server from its DB-backed event queue to the generic pull event queue introduced in #354. New `puller.go`: implements `eventqueue.Puller` Deleted `eventqueue.go`: replaced by the generic pull manager. `client/main.go`: on SSE stream error during `send`, fall through to `subscribe(taskID)` so the client resumes after transient drops. `Dockerfile.app`: bump Go 1.24 → 1.25 and adjust build paths for the example's module layout. The puller tolerates `ErrTaskNotFound` on the initial snapshot and returns a synthetic `TASK_STATE_SUBMITTED` placeholder, handling the race where the frontend creates the reader before the backend worker has inserted the initial task row. `
🤖 I have created a release *beep* *boop* --- ## [2.4.0](v2.3.1...v2.4.0) (2026-07-28) ### Features * provide generic pull event queue ([#354](#354)) ([6a48d4b](6a48d4b)) * **push:** push sender SSRF protection enabled by default (fixes [#373](#373)) ([#374](#374)) ([0a4f17a](0a4f17a)) ### Bug Fixes * **a2acompat/a2av0:** implement A2A v0.3 REST wire format ([#371](#371)) ([1ca80f9](1ca80f9)), closes [#370](#370) * **a2asrv:** skip TaskID mismatch check when message has no task reference (fixes [#350](#350)) ([#359](#359)) ([8c0dd99](8c0dd99)) * allow empty request bodies and enable GET method for task subscriptions ([#381](#381)) ([8363365](8363365)), closes [#380](#380) * **cli:** emit non-null required Agent Card list fields in synthesized card (fixes [#369](#369)) ([#372](#372)) ([0640869](0640869)) * **eventqueue:** add JSON marshal/unmarshal to Message so events survive roundtrip (fixes [#349](#349)) ([#360](#360)) ([d52d5a1](d52d5a1)) * itk grpc compatibility for v0.3 SDKs ([#367](#367)) ([11340a7](11340a7)) * ListTaskshistoryLength missing ([8b91364](8b91364)) * ListTaskshistoryLength missing (issue [#355](#355)) ([#361](#361)) ([8b91364](8b91364)) * **taskstore,push:** enforce cross-tenant authorization on Get and push config stores ([#357](#357)) ([a9f9c64](a9f9c64)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: Serob Nahapetyan <serob@google.com>
This PR provides a generic "pull-based" queue in the eventqueue package
NewPullQueueManager creates an inner in-memory manager internally. CreateWriter always delegates to the in-memory manage inner. CreateReader delegates to inner if PullConfig.UseInMemory returns true, otherwise a pullReader is returned which:
Calls Provider to get a per-call Puller.
Spawns a polling goroutine that calls Pull every PollInterval, pushes events through a channel, and stops on taskupdate.IsFinal.
First Read emits the Snapshot task as the initial event.
Subsequent Reads either drain the channel, return eventqueue.ErrQueueClosed on final state, return ctx.Err() on
cancellation, or return inactivity error after InactivityTimeout (with optional OnInactivity callback to refresh state).
Destroy delegates to inner. The pull-side reader cleans itself up via Close on the source.