-
Notifications
You must be signed in to change notification settings - Fork 31
fix: index sessions in sorted set for listing #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: index sessions in sorted set for listing #119
Conversation
|
Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset. In case there are security findings, they will be communicated to you as a comment inside the PR. Hope you’ll enjoy using Jit. Questions? Comments? Want to learn more? Get in touch with us. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Fixes session listing by ensuring sessions are indexed in Redis when working memory is saved and removed from the index when working memory is deleted.
Changes:
- Add Redis
ZADDindexing toset_working_memory()solist_sessions()has data to read. - Add Redis
ZREMcleanup todelete_working_memory()to keep the index in sync on explicit deletes. - Add unit/integration tests validating session indexing and listing behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| agent_memory_server/working_memory.py | Populates and maintains the sessions:{namespace} sorted-set index used by list_sessions(). |
| tests/test_working_memory.py | Adds tests covering indexing on set, de-indexing on delete, and list_sessions() returning indexed sessions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Index session in sorted set for listing | ||
| sessions_key = Keys.sessions_key(namespace=working_memory.namespace) | ||
| await redis_client.zadd( | ||
| sessions_key, | ||
| {working_memory.session_id: time.time()} | ||
| ) |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set_working_memory() now always adds the session ID to the sessions:{namespace} sorted set, but nothing removes that entry when the working-memory JSON key expires via TTL. With the current default TTL usage (e.g., MCP tool defaults), this will accumulate stale session IDs indefinitely and list_sessions() may return sessions whose working memory no longer exists. Consider implementing a cleanup strategy (e.g., store expiration timestamp as the score and prune expired members in list_sessions(), or run a background/keyspace-notification cleanup) so the index doesn’t grow without bound and doesn’t return expired sessions.
| await redis_client.zadd( | ||
| sessions_key, | ||
| {working_memory.session_id: time.time()} |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sessions index is keyed only by namespace and stores only session_id as the member. However, working memory storage supports user_id-scoped keys (Keys.working_memory_key(..., user_id=...)). If two users reuse the same session_id within a namespace, zadd will collide and the index can’t represent both sessions; it also makes it hard to safely filter listings by user. Consider incorporating user_id into the index key (e.g., sessions:{namespace}:{user_id}) or into the sorted-set member format, and updating list_sessions() accordingly.
| await redis_client.zadd( | |
| sessions_key, | |
| {working_memory.session_id: time.time()} | |
| # Include user_id in the member to avoid collisions between users | |
| session_index_member = ( | |
| f"{working_memory.user_id}:{working_memory.session_id}" | |
| if working_memory.user_id is not None | |
| else working_memory.session_id | |
| ) | |
| await redis_client.zadd( | |
| sessions_key, | |
| {session_index_member: time.time()} |
| await redis_client.delete(key) | ||
| # Remove session from sorted set index | ||
| sessions_key = Keys.sessions_key(namespace=namespace) | ||
| await redis_client.zrem(sessions_key, session_id) |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete_working_memory() removes from the sessions index using only session_id (and only namespace for the key). If working memories are stored with user_id (supported by Keys.working_memory_key), deleting one user’s session can incorrectly remove the index entry for another user that happens to share the same session_id. If you change the indexing scheme to be user-scoped (or encode user_id into the member), this zrem should be updated to match.
| await redis_client.zrem(sessions_key, session_id) | |
| # Remove both legacy session-only member and user-scoped member (if applicable) | |
| members = [session_id] | |
| if user_id is not None: | |
| members.append(f"{user_id}:{session_id}") | |
| await redis_client.zrem(sessions_key, *members) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good. It looks like we dropped those calls during a refactor. However, the TTLs issue is a certainly a point against continuing to use a sorted set instead of a search index. A search index will update to reflect garbage-collected keys. We should merge your PR first though and return to that later.
|
@epicdigitalmedia Can you run |
|
@abrookins all set thanks! |
The set_working_memory function now calls zadd to index sessions, so the test mock needs to include this async method. Built with Epic Flowstate
|
Merged, thanks again @epicdigitalmedia. Also, thanks for swooping in with that test fix @tegument! |
Summary
zaddcall inset_working_memory()to index sessionszremcall indelete_working_memory()to remove sessions from indexProblem
The
list_sessions()function reads from a sorted set (sessions:{namespace}) that was never populated. Sessions were saved to Redis but never indexed, causing the list endpoint to always return empty.Solution
Testing