Skip to content

DatabaseSessionService on MySQL/MariaDB treats user ids that differ only in case or accents as the same user #7867

Open

Description

@RyanLin5967

🔴 Required Information

Describe the Bug:
Currently, DatabaseSessionService creates its tables with String(DEFAULT_MAX_KEY_LENGTH) key columns (app_name, user_id, id, session_id in schemas/v1.py) and no collation. On MySQL and MariaDB they get the database's default collation, which is case-insensitive and accent-insensitive (utf8mb4_general_ci on MariaDB, utf8mb4_0900_ai_ci on MySQL 8). Every lookup is an = comparison on those columns, so "alice", "Alice" and "ALICE" (and "rené" and "rene") are the same key. As a result:

  • a new session for "Alice" is created with the user: state of "alice";
  • get_session(user_id="Alice", session_id=<alice's session id>) returns alice's session, including its events and state;
  • list_sessions(user_id="Alice") lists alice's sessions.

InMemorySessionService, SqliteSessionService and DatabaseSessionService on SQLite or PostgreSQL keep these users apart. With the database created as COLLATE utf8mb4_bin, the same script gives the expected output. MariaDB's utf8mb4_bin is still PAD SPACE, so there "bob" and "bob " also map to one user.

Steps to Reproduce:

  1. pip install 'google-adk[db]' aiomysql
  2. CREATE DATABASE adk; on MariaDB or MySQL with the default collation.
  3. Run the script below.

Expected Behavior:
None, None, 1: "Alice" is a different user from "alice", as it is with every other session service.

Observed Behavior:

alice@example.com
id='...' app_name='app' user_id='alice' state={'note': 'alice private', 'user:email': 'alice@example.com'} events=[...]
2

Environment Details:

  • ADK Library Version (pip show google-adk): 2.13.0 (same schemas/v1.py key columns on main)
  • Desktop OS: macOS 26.6.2, arm64 (Darwin 25.6.0)
  • Python Version (python -V): 3.12.13
  • Database: MariaDB 12.3.3, aiomysql 0.3.2, SQLAlchemy 2.1.6

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: N/A (session service only)

🟡 Optional Information

Regression:
N/A

Additional Context:
user_id is the only isolation boundary for user: state and for get_session / list_sessions. Applications that use emails or external account names as user ids get one shared user per case or accent variant without any error. Declaring the key columns with a binary collation on MySQL/MariaDB (for example String(..., collation="utf8mb4_bin") via a dialect variant) keeps the comparison exact.

Minimal Reproduction Code:

import asyncio
from google.adk.events import Event, EventActions
from google.adk.sessions import DatabaseSessionService

DB_URL = "mysql+aiomysql://user:password@localhost:3306/adk"


async def main():
    async with DatabaseSessionService(db_url=DB_URL) as svc:
        alice = await svc.create_session(app_name="app", user_id="alice")
        await svc.append_event(alice, Event(
            author="user", invocation_id="i1",
            actions=EventActions(state_delta={"user:email": "alice@example.com", "note": "alice private"}),
        ))

        other = await svc.create_session(app_name="app", user_id="Alice")
        print(other.state.get("user:email"))
        # expected: None
        print(await svc.get_session(app_name="app", user_id="Alice", session_id=alice.id))
        # expected: None
        print(len((await svc.list_sessions(app_name="app", user_id="Alice")).sessions))
        # expected: 1


asyncio.run(main())

How often has this issue occurred?:

  • Always (100%)

Activity

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

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions