Resource Management: Potential Memory Leak in A2aEventQueue #8861

Closed
opened 2026-04-14 03:00:31 +00:00 by HAL9000 · 1 comment
Owner

Metadata

  • Commit Message: fix(a2a): replace unbounded _events list in A2aEventQueue with fixed-size deque
  • Branch: fix/a2a-event-queue-memory-leak

Background and Context

The _events list in A2aEventQueue (cleveragents.a2a.events.A2aEventQueue) can grow indefinitely, as there is no mechanism to cap its size. In long-running applications — such as persistent agent sessions or server-mode deployments — this can lead to a memory leak, consuming all available memory and eventually crashing the application.

The _events list is appended to on every publish() call and is only cleared when the queue is explicitly closed. There is no eviction policy, no maximum size, and no automatic pruning. This is a resource management defect that violates the principle of bounded resource usage in long-lived processes.

Code Evidence:

# cleveragents.a2a.events.A2aEventQueue.__init__
def __init__(self) -> None:
    self._events: list[A2aEvent] = []
    # ...

# cleveragents.a2a.events.A2aEventQueue.publish
self._events.append(event)

The _events list is appended to in the publish method but is only cleared when the queue is closed.

Recommendation:
Implement a mechanism to limit the size of the _events list. A collections.deque with a maxlen is a suitable and efficient way to implement a fixed-size event buffer. When the deque reaches capacity, the oldest events are automatically evicted, bounding memory usage without requiring manual pruning logic.

Expected Behavior

The A2aEventQueue should maintain a bounded internal event buffer. In long-running applications, memory usage attributable to the event queue should remain stable and not grow without limit, regardless of how many events are published over the lifetime of the queue.

Acceptance Criteria

  • The _events storage in A2aEventQueue is replaced with a collections.deque configured with a maxlen to enforce a fixed upper bound on the number of retained events.
  • The maxlen value is configurable (e.g., via a constructor parameter with a sensible default).
  • The get_events(limit) method continues to return the most recent limit events correctly after the change.
  • The close() method continues to clear the buffer correctly.
  • All existing BDD scenarios for A2aEventQueue continue to pass without modification.
  • New BDD scenarios are added to verify that the buffer does not grow beyond maxlen when more than maxlen events are published.
  • Type annotations are updated to reflect the deque[A2aEvent] type (no type: ignore suppressions).
  • Test coverage remains ≥ 97%.

Subtasks

  • Replace self._events: list[A2aEvent] = [] with self._events: deque[A2aEvent] = deque(maxlen=<default>) in A2aEventQueue.__init__
  • Add a max_events: int constructor parameter (with a sensible default, e.g. 1000) to make the buffer size configurable
  • Update type annotations throughout A2aEventQueue to use deque[A2aEvent] (import deque from collections)
  • Verify get_events(limit) still works correctly with a deque (slicing via list(self._events)[-limit:])
  • Tests (Behave): Add BDD scenarios verifying that publishing more than maxlen events does not grow the buffer beyond maxlen
  • Tests (Behave): Add BDD scenario verifying that the oldest events are evicted when the buffer is full
  • Tests (Robot): Add integration test verifying stable memory usage under sustained event publishing
  • Verify coverage ≥ 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly (fix(a2a): replace unbounded _events list in A2aEventQueue with fixed-size deque), followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly (fix/a2a-event-queue-memory-leak).
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.

Automated by CleverAgents Bot
Agent: new-issue-creator

## Metadata - **Commit Message**: `fix(a2a): replace unbounded _events list in A2aEventQueue with fixed-size deque` - **Branch**: `fix/a2a-event-queue-memory-leak` ## Background and Context The `_events` list in `A2aEventQueue` (`cleveragents.a2a.events.A2aEventQueue`) can grow indefinitely, as there is no mechanism to cap its size. In long-running applications — such as persistent agent sessions or server-mode deployments — this can lead to a memory leak, consuming all available memory and eventually crashing the application. The `_events` list is appended to on every `publish()` call and is only cleared when the queue is explicitly closed. There is no eviction policy, no maximum size, and no automatic pruning. This is a resource management defect that violates the principle of bounded resource usage in long-lived processes. **Code Evidence:** ```python # cleveragents.a2a.events.A2aEventQueue.__init__ def __init__(self) -> None: self._events: list[A2aEvent] = [] # ... # cleveragents.a2a.events.A2aEventQueue.publish self._events.append(event) ``` The `_events` list is appended to in the `publish` method but is only cleared when the queue is closed. **Recommendation:** Implement a mechanism to limit the size of the `_events` list. A `collections.deque` with a `maxlen` is a suitable and efficient way to implement a fixed-size event buffer. When the deque reaches capacity, the oldest events are automatically evicted, bounding memory usage without requiring manual pruning logic. ## Expected Behavior The `A2aEventQueue` should maintain a bounded internal event buffer. In long-running applications, memory usage attributable to the event queue should remain stable and not grow without limit, regardless of how many events are published over the lifetime of the queue. ## Acceptance Criteria - [ ] The `_events` storage in `A2aEventQueue` is replaced with a `collections.deque` configured with a `maxlen` to enforce a fixed upper bound on the number of retained events. - [ ] The `maxlen` value is configurable (e.g., via a constructor parameter with a sensible default). - [ ] The `get_events(limit)` method continues to return the most recent `limit` events correctly after the change. - [ ] The `close()` method continues to clear the buffer correctly. - [ ] All existing BDD scenarios for `A2aEventQueue` continue to pass without modification. - [ ] New BDD scenarios are added to verify that the buffer does not grow beyond `maxlen` when more than `maxlen` events are published. - [ ] Type annotations are updated to reflect the `deque[A2aEvent]` type (no `type: ignore` suppressions). - [ ] Test coverage remains ≥ 97%. ## Subtasks - [ ] Replace `self._events: list[A2aEvent] = []` with `self._events: deque[A2aEvent] = deque(maxlen=<default>)` in `A2aEventQueue.__init__` - [ ] Add a `max_events: int` constructor parameter (with a sensible default, e.g. `1000`) to make the buffer size configurable - [ ] Update type annotations throughout `A2aEventQueue` to use `deque[A2aEvent]` (import `deque` from `collections`) - [ ] Verify `get_events(limit)` still works correctly with a `deque` (slicing via `list(self._events)[-limit:]`) - [ ] Tests (Behave): Add BDD scenarios verifying that publishing more than `maxlen` events does not grow the buffer beyond `maxlen` - [ ] Tests (Behave): Add BDD scenario verifying that the oldest events are evicted when the buffer is full - [ ] Tests (Robot): Add integration test verifying stable memory usage under sustained event publishing - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly (`fix(a2a): replace unbounded _events list in A2aEventQueue with fixed-size deque`), followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly (`fix/a2a-event-queue-memory-leak`). - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. --- **Automated by CleverAgents Bot** Agent: new-issue-creator
HAL9000 added this to the v3.5.0 milestone 2026-04-14 03:02:36 +00:00
Author
Owner

Triage Decision: DUPLICATE — Closing as Wont Do

This issue is a duplicate of #8412 (A2aEventQueue._events list grows unboundedly — no size cap causes memory leak in long-running processes), which is already verified and open with MoSCoW/Must Have priority.

Please track this work in #8412.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

❌ **Triage Decision: DUPLICATE — Closing as Wont Do** This issue is a duplicate of #8412 (A2aEventQueue._events list grows unboundedly — no size cap causes memory leak in long-running processes), which is already verified and open with MoSCoW/Must Have priority. Please track this work in #8412. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#8861
No description provided.