Skip to content

feat: add TypesenseSessionService#140

Open
deniskrds wants to merge 9 commits into
google:mainfrom
deniskrds:feat/7-typesense-session-service
Open

feat: add TypesenseSessionService#140
deniskrds wants to merge 9 commits into
google:mainfrom
deniskrds:feat/7-typesense-session-service

Conversation

@deniskrds
Copy link
Copy Markdown

This change adds TypesenseSessionService, implementing all five BaseSessionService methods — create_session, get_session, list_sessions, delete_session, and append_event — backed by Typesense collections. Four collections are managed automatically on first use ({prefix}_sessions, {prefix}_events, {prefix}_app_states, {prefix}_user_states), following the same collection-per-scope pattern used by DatabaseSessionService for app, user, and session state. The typesense package is added as an optional dependency with a lazy import in init.py to avoid breaking users who don't have typesense installed.

App and user state updates are serialized per-key via asyncio locks within a single process. Note that app_name, user_id, and session_id must not contain || (used as an internal document-ID separator).

@DeanChensj
Copy link
Copy Markdown
Collaborator

@gemini-cli /review

@github-actions
Copy link
Copy Markdown

🤖 Hi @DeanChensj, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Thanks for the detailed contribution! The implementation of TypesenseSessionService is clean, well-tested, and follows the project's patterns.

I've left a few comments regarding:

  1. Memory management: The state locks dictionaries grow indefinitely.
  2. Scalability: list_sessions might struggle with a large number of users.
  3. Distributed concurrency: Local locks won't work in multi-instance deployments (as noted in your docstring).

Overall, this is a great addition to the community plugins. Please consider addressing the memory leak and scalability points if this is intended for high-traffic use cases.

collection_prefix: str = 'adk',
connection_timeout_seconds: int = 5,
):
try:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These dictionaries grow indefinitely as new apps or users are encountered, which could lead to a memory leak in long-running services with many unique users. Consider using an LRU cache or a similar mechanism to limit the number of active locks.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

A naive LRU here would risk evicting a lock that's currently held, breaking serialization for that key. A correct implementation would require reference counting or evict-only-when-idle semantics. I've opted to leave the dicts unbounded and documented the limitation in the class docstring. I'm happy to revisit this if a bounded structure is preferred.

return None

async def _search_all(
self, collection: str, filter_by: str, sort_by: Optional[str] = None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

As noted in the docstring, these local locks won't synchronize state updates across multiple instances/processes. While Typesense doesn't have native transactions, using a distributed lock (e.g., via Redis) would be a more robust solution for multi-process deployments. For this community contribution, the current approach is acceptable but the limitation is worth highlighting.

'filter_by': filter_by,
'sort_by': 'timestamp:desc',
'per_page': min(remaining, 250),
'page': page,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fetching all user states for an app when user_id is None could be extremely slow and memory-intensive if the app has a large number of users. Since list_sessions already fetches session documents, perhaps user states could be fetched on-demand or only for the users returned in the session list (e.g., using a filter with multiple IDs if the number of sessions is small).

Copy link
Copy Markdown
Author

@deniskrds deniskrds May 20, 2026

Choose a reason for hiding this comment

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

Fixed. list_sessions now collects the unique user_ids from the returned session documents and fetches user state only for those rather than retrieving all user states for the app.

* App and user state updates are serialized per-key within a single process.
Multi-process deployments sharing the same Typesense instance may still
lose concurrent state updates because Typesense has no native transactions.
"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice job handling the optional typesense dependency with lazy imports and clear error messages. This keeps the core package lightweight for users who don't need this specific service.

@deniskrds
Copy link
Copy Markdown
Author

deniskrds commented May 20, 2026

@DeanChensj addressed the comments in my recent commits.

list_sessions user-state scoping is fixed. Now collects unique user_ids from the returned session documents and fetches user state only for those, instead of scanning all user states for the app.

Lock dictionary growth I held off on a fix because a naive LRU would risk evicting held locks and breaking serialization. A ref-counted evict-when-idle approach would work correctly. Docstring now documents the limitation. Happy to add the ref-counted version in this PR if you'd prefer otherwise can be a follow-up.

Distributed locking agree this is a real limitation for multi-process deployments, but a proper fix needs a distributed lock backend (Redis etc.) which feels out of scope here. Noted in the class docstring for now. Open to revisiting in a follow-up.

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.

feat: add Typesense-based session service

2 participants