fix(sessions): stop DatabaseSessionService.update_time onupdate from masking explicit writes - #7277
Open
chelsealong wants to merge 1 commit into
Open
chelsealong wants to merge 1 commit into
chelsealong wants to merge 1 commit into
Conversation
…masking explicit writes append_event always sets StorageSession.update_time explicitly from the event's timestamp. When that timestamp equals the value already stored, SQLAlchemy sees no change to the column and drops it from the UPDATE, but the UPDATE still runs for other changed columns (e.g. state), so the onupdate=func.now() default fires and overwrites it with the database's own clock instead. The in-memory revision marker, read before commit, no longer matches what storage holds, and the next append_event from the same, only, writer is rejected with a false StaleSessionError. Drop onupdate=func.now() from StorageSession.update_time: create_session already sets a default for inserts, and append_event is the only code path that updates the column, always explicitly. Fixes google#7276
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
Problem:
DatabaseSessionService.append_eventraisesStaleSessionErrorwhen there isonly one writer, whenever an event shares the same microsecond timestamp as
the previous event and carries a state change.
append_eventalways setsStorageSession.update_timeexplicitly from theevent's timestamp (
database_session_service.py), and the column is alsodeclared with
onupdate=func.now()(schemas/v1.py). When the new timestampequals the value already stored, SQLAlchemy sees no change to
update_timeand drops it from the UPDATE statement's explicit values. The UPDATE still
runs because another column (
state) changed, soonupdatefires and thedatabase's own clock is written instead — whole seconds on SQLite, the
transaction start time on Postgres. The in-memory revision marker, read
before commit, no longer matches what storage actually holds, so the next
append_eventon that session is incorrectly rejected as stale.Equal timestamps are common with coarser clocks (e.g. Python 3.12 on Windows,
15.6ms
GetSystemTimeAsFileTimeticks), so a workflow emitting several eventsper step hits this regularly.
Solution:
Drop
onupdate=func.now()fromStorageSession.update_time(
src/google/adk/sessions/schemas/v1.py).create_sessionalready sets adefaultfor inserts, andappend_eventis the only code path that updatesthis column on an existing row — and it always sets it explicitly. No other
column relies on this
onupdate(verifiedStorageAppState/StorageUserStateare untouched, since they still rely on their own
onupdateand are never setexplicitly).
Testing Plan
Unit Tests:
Added
test_append_event_same_timestamp_single_writer_not_staletotests/unittests/sessions/test_session_service.py, reproducing the issue'srepro script against the
DATABASE(SQLite) backend: two events with anidentical timestamp, the second carrying a state delta, followed by a third
event from the same session object.
pre-commit run --files src/google/adk/sessions/schemas/v1.py tests/unittests/sessions/test_session_service.pypasses (isort, pyink, ruff,addlicense, compliance checks, codespell). The one hook failure it reports
(
check-new-py-prefixonsrc/google/adk/memory/_sqlite_memory_service.py)is pre-existing on
main/unmodified HEAD and unrelated to this change.Checklist
AI assistance disclosure
This change (analysis, fix, and test) was developed with the assistance of
Claude Code (Anthropic).