Skip to content

Commit 20ee4a2

Browse files
DeanChensjcopybara-github
authored andcommitted
docs(sessions): correct the rationale for InMemorySessionService event deduplication
The deduplication guard in `InMemorySessionService.append_event` was documented as protecting against an orchestrator broadcasting the same `Event` to several concurrent `Session` references (#5723). ADK has no such broadcast: within one invocation all parallel branches share a single `InvocationContext.session` and serialize their appends, every produced event carries a fresh id, and persistent backends reject stale snapshots with `StaleSessionError` and enforce unique `event.id` keys. The guard is still worth keeping for a different reason: it makes `append_event` idempotent for an equal re-delivery of an event, which callers that retry the call (e.g. a write buffer behind an RPC) rely on to avoid double-applying state or inflating their buffer. Reword the comment and the test docstrings to describe that contract; no behavior change. Co-authored-by: Shangjie Chen <deanchen@google.com> PiperOrigin-RevId: 996637458
1 parent cb6d679 commit 20ee4a2

2 files changed

Lines changed: 15 additions & 14 deletions

File tree

‎src/google/adk/sessions/in_memory_session_service.py‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -341,15 +341,15 @@ async def append_event(self, session: Session, event: Event) -> Event:
341341
if session_id not in self.sessions.get(app_name, {}).get(user_id, {}):
342342
raise SessionNotFoundError(f'Session {session_id} not found.')
343343

344-
# Fetch the canonical storage session early so we can drop a re-delivered
345-
# event before modifying any state. The same event can be delivered more
346-
# than once when the orchestrator broadcasts a shared-state delta to
347-
# several concurrent session references; deduplicating here prevents
348-
# double-application of state updates and duplicate entries in event lists.
349-
# A re-delivery is an equal event -- the same object, or a copy carrying
350-
# the same id and fields -- so dedupe on equality, gated on a matching id
351-
# for speed. Distinct events that merely share an id (e.g. tests that
352-
# stamp a fixed uuid) are not equal and are kept.
344+
# ``append_event`` is idempotent for an equal re-delivery of an event (the
345+
# same object, or a copy carrying the same id and fields), so a caller
346+
# that retries this call -- e.g. a service that buffers writes behind an
347+
# RPC -- neither double-applies the state delta nor duplicates the entry.
348+
# ADK itself never re-delivers an event: parallel branches of one
349+
# invocation share a single ``InvocationContext.session`` and serialize
350+
# their appends, and every produced event carries a fresh id. Dedupe on
351+
# equality gated on a matching id; distinct events that merely share an
352+
# id (e.g. tests that stamp a fixed uuid) are not equal and are kept.
353353
storage_session = self.sessions[app_name][user_id][session_id]
354354
if any(e == event for e in storage_session.events if e.id == event.id):
355355
return event

‎tests/unittests/sessions/test_session_service.py‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3889,7 +3889,7 @@ def test_list_sessions_sync_unknown_app_or_user_returns_empty_response():
38893889

38903890

38913891
# ---------------------------------------------------------------------------
3892-
# Regression tests for duplicate-event deduplication (issue #5723)
3892+
# append_event idempotency for re-delivered events
38933893
# ---------------------------------------------------------------------------
38943894

38953895

@@ -3902,8 +3902,9 @@ def test_list_sessions_sync_unknown_app_or_user_returns_empty_response():
39023902
async def test_append_event_is_idempotent_for_same_event_id(session_service):
39033903
"""Re-delivering an event must not duplicate entries or double-apply state.
39043904
3905-
A broadcast can re-deliver the same event either as the same object or as
3906-
an equal copy, so both must be deduplicated.
3905+
A caller that retries ``append_event`` (e.g. a write buffer behind an RPC)
3906+
can re-deliver the same event either as the same object or as an equal
3907+
copy, so both must be deduplicated.
39073908
"""
39083909
app_name = 'test_app'
39093910
user_id = 'user_dup'
@@ -3917,8 +3918,8 @@ async def test_append_event_is_idempotent_for_same_event_id(session_service):
39173918
actions=EventActions(state_delta={'session:counter': 1}),
39183919
)
39193920

3920-
# Re-deliver as the same object and again as an equal copy (a broadcast
3921-
# to several concurrent session references can produce either).
3921+
# Re-deliver as the same object and again as an equal copy (a retried
3922+
# call can produce either).
39223923
await session_service.append_event(session=session, event=event)
39233924
await session_service.append_event(session=session, event=event)
39243925
await session_service.append_event(

0 commit comments

Comments
 (0)