Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/google/adk/integrations/redis/_redis_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ async def list_sessions(
except Exception as e:
logger.warning("Failed to parse session at key %s: %s", key, e)

# Sort descending by last_update_time
sessions.sort(key=lambda s: s.last_update_time, reverse=True)
# Sort oldest first, with stable ordering when update times are equal.
sessions.sort(key=lambda s: (s.last_update_time, s.user_id, s.id))
return ListSessionsResponse(sessions=sessions)

async def delete_session(
Expand Down
34 changes: 34 additions & 0 deletions tests/unittests/integrations/redis/test_redis_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from __future__ import annotations

import json
from unittest import mock

from google.adk.errors.already_exists_error import AlreadyExistsError
from google.adk.events.event import Event
Expand Down Expand Up @@ -203,6 +204,39 @@ async def test_list_sessions(session_service):
assert session_ids_all == {"s1", "s2", "s3"}


@pytest.mark.asyncio
@pytest.mark.parametrize(
"user_id, expected",
[
("u1", [("u1", "s1"), ("u1", "s2")]),
(None, [("u2", "s0"), ("u1", "s1"), ("u1", "s2"), ("u2", "s1")]),
],
)
async def test_list_sessions_ordered_by_activity_with_stable_ties(
session_service, user_id, expected
):
"""Sessions are oldest first, with ties ordered by user and session id."""
with mock.patch(
"google.adk.integrations.redis._redis_session_service.time"
) as clock:
for owner, session_id, timestamp in (
("u2", "s1", 20.0),
("u1", "s2", 20.0),
("u1", "s1", 20.0),
("u2", "s0", 10.0),
):
clock.time.return_value = timestamp
await session_service.create_session(
app_name="app1", user_id=owner, session_id=session_id
)

response = await session_service.list_sessions(
app_name="app1", user_id=user_id
)

assert [(s.user_id, s.id) for s in response.sessions] == expected


@pytest.mark.asyncio
async def test_list_sessions_glob_metacharacters_match_literally(
session_service, fake_redis
Expand Down
4 changes: 0 additions & 4 deletions tests/unittests/sessions/_conformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,6 @@ async def _make_per_agent_database(
'redis',
_make_redis,
divergences={
'test_list_sessions_ordered_by_last_update_time': (
'Redis sorts sessions newest first, while the base class'
' documents oldest first.'
),
'test_session_last_update_time_updates_on_event': (
'Redis stamps the session with the wall clock instead of the'
" appended event's timestamp."
Expand Down