Skip to content

fix(sessions): preserve event append order on tied timestamps (#8051) - #8124

Open
mohitdixit02 wants to merge 2 commits into
google:mainfrom
mohitdixit02:fix/db-session-event-ordering-8051
Open

mohitdixit02 wants to merge 2 commits into
google:mainfrom
mohitdixit02:fix/db-session-event-ordering-8051

Conversation

@mohitdixit02

Copy link
Copy Markdown

Link to Issue or Description of Change

Problem:
When multiple session events share the same timestamp (for instance, events created within the same microsecond or during batch processing), DatabaseSessionService orders events by StorageEvent.timestamp.desc(), StorageEvent.id.desc(). Because event IDs are randomly generated UUIDs, sorting by id.desc() reorders events arbitrarily rather than preserving their chronological append order. On databases such as PostgreSQL and MySQL, replaying or reloading the session scrambles the event sequence and causes out-of-order execution.

Solution:
Introduce a monotonically increasing event_index: Mapped[int] per session in StorageEvent (V1 schema) and calculate the next index sequentially during append_event() under the existing session lock. Update get_session() and _session_matches_storage_revision() to order by timestamp.desc(), event_index.desc(), id.desc(), strictly preserving FIFO append order across all database backends while maintaining backward-compatibility with existing tables via automatic schema migration.

Before & After Example

# Multiple events appended within the same microsecond timestamp:
await service.append_event(session, Event(id="id_2_beta", timestamp=100.0))
await service.append_event(session, Event(id="id_1_alpha", timestamp=100.0))

# Before: Sorted by timestamp DESC, id DESC -> ties broken alphabetically by UUID id
[e.id for e in session.events]  # ['id_1_alpha', 'id_2_beta'] (Scrambled)

# After: Sorted by timestamp DESC, event_index DESC, id DESC -> ties broken by append sequence
[e.id for e in session.events]  # ['id_2_beta', 'id_1_alpha'] (Preserved FIFO order)

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.

  • All unit tests pass locally.

  • Updated test_get_session_orders_tied_timestamps_by_id to assert FIFO append order.

  • Added test_database_session_service_multiple_events_tied_timestamp_order verifying 8 events sharing identical timestamps reload in exact append order.

  • Added test_database_session_service_migration_adds_event_index_column ensuring existing tables without event_index automatically migrate and order tied events properly.

  • Local test execution results:

    • pytest tests/unittests/sessions/test_session_service.py: 509 passed, 1 xfailed
    • pytest tests/unittests/sessions/migration/: 61 passed (19 schema + 42 migration)
    • ruff check src/google/adk/sessions tests/unittests/sessions: All checks passed

def _setup_database_schema(connection: Connection, metadata: MetaData) -> None:
"""Ensures tables and indexes declared in metadata exist."""
metadata.create_all(bind=connection)
_ensure_schema_columns_exist(connection)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also runs on the V0 path (prepare_tables calls _setup_database_schema with BaseV0.metadata), so a legacy db gets an ALTER TABLE for a column V0 never reads, and a db user without ALTER rights now fails at startup. gating it on the metadata keeps it V1 only:

Suggested change
_ensure_schema_columns_exist(connection)
if "event_index" in metadata.tables["events"].c:
_ensure_schema_columns_exist(connection)

fixed this plus a test in this commit, you can just cherry pick it instead of redoing it: its-amann@8f7ceeb

@mohitdixit02

Copy link
Copy Markdown
Author

Thanks for catching this edge case with the V0 legacy schema and providing the test!

I've cherry-picked your commit (4c14fbc) onto this branch. All unit tests (test_v0_database_is_not_altered_with_event_index and tie-breaking tests) are passing cleanly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DatabaseSessionService on PostgreSQL reloads events with equal timestamps out of append order

3 participants