Skip to content

Commit c8ce8cf

Browse files
committed
fix: consistent UTC timestamp handling for PostgreSQL in DatabaseSessionService
Fixes two timestamp inconsistencies that cause stale session detection failures when using PostgreSQL with asyncpg: 1. append_event() now strips timezone info for PostgreSQL (matching create_session() behavior). Previously, PostgreSQL got a local-time naive datetime via datetime.fromtimestamp(), while create_session() stored a UTC-based naive datetime. This mismatch caused get_update_timestamp() to return incorrect values when the server timezone differs from UTC. 2. get_update_timestamp() now uses tzinfo-awareness to decide whether to interpret a naive datetime as UTC, rather than checking only for SQLite. This correctly handles both SQLite and PostgreSQL naive datetimes without requiring callers to pass dialect flags. Fixes #4366 Related: #1848
1 parent ec660ed commit c8ce8cf

File tree

2 files changed

+18
-12
lines changed

2 files changed

+18
-12
lines changed

src/google/adk/sessions/database_session_service.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
_MARIADB_DIALECT = "mariadb"
6565
_MYSQL_DIALECT = "mysql"
6666
_POSTGRESQL_DIALECT = "postgresql"
67+
_DATABRICKS_DIALECT = "databricks"
6768
# Tuple key order for in-process per-session lock maps:
6869
# (app_name, user_id, session_id).
6970
_SessionLockKey: TypeAlias = tuple[str, str, str]
@@ -627,12 +628,14 @@ async def append_event(self, session: Session, event: Event) -> Event:
627628
if session_state_delta:
628629
storage_session.state = storage_session.state | session_state_delta
629630

630-
if is_sqlite:
631-
update_time = datetime.fromtimestamp(
632-
event.timestamp, timezone.utc
633-
).replace(tzinfo=None)
634-
else:
635-
update_time = datetime.fromtimestamp(event.timestamp)
631+
# Convert event timestamp to a UTC datetime. SQLite and PostgreSQL
632+
# use TIMESTAMP WITHOUT TIME ZONE, so the tzinfo must be stripped to
633+
# avoid asyncpg DataError / SQLite comparison issues.
634+
update_time = datetime.fromtimestamp(
635+
event.timestamp, timezone.utc
636+
)
637+
if is_sqlite or self.db_engine.dialect.name == _POSTGRESQL_DIALECT:
638+
update_time = update_time.replace(tzinfo=None)
636639
storage_session.update_time = update_time
637640
sql_session.add(schema.StorageEvent.from_event(session, event))
638641

src/google/adk/sessions/schemas/v1.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,15 @@ def update_timestamp_tz(self) -> float:
119119
)
120120
return self.get_update_timestamp(is_sqlite=is_sqlite)
121121

122-
def get_update_timestamp(self, is_sqlite: bool) -> float:
123-
"""Returns the time zone aware update timestamp."""
124-
if is_sqlite:
125-
# SQLite does not support timezone. SQLAlchemy returns a naive datetime
126-
# object without timezone information. We need to convert it to UTC
127-
# manually.
122+
def get_update_timestamp(self, is_sqlite: bool = False) -> float:
123+
"""Returns the update timestamp as a POSIX float.
124+
125+
Naive datetimes (returned by SQLite and PostgreSQL which use
126+
``TIMESTAMP WITHOUT TIME ZONE``) are interpreted as UTC. Timezone-aware
127+
datetimes (MySQL, MariaDB) are used directly.
128+
"""
129+
if self.update_time.tzinfo is None:
130+
# Naive datetimes from SQLite / PostgreSQL are stored as UTC.
128131
return self.update_time.replace(tzinfo=timezone.utc).timestamp()
129132
return self.update_time.timestamp()
130133

0 commit comments

Comments
 (0)