Skip to content

feat: Implementation of MySQL-backed session service for ADK - #647

Open
voborl00 wants to merge 1 commit into
google:mainfrom
appsatori:mysql-session-service-clean
Open

voborl00 wants to merge 1 commit into
google:mainfrom
appsatori:mysql-session-service-clean

Conversation

@voborl00

@voborl00 voborl00 commented Dec 5, 2025

Copy link
Copy Markdown

This PR introduces a MySQL implementation of the BaseSessionService, allowing the ADK to persist session state and history in a MySQL. This is crucial for distributed deployments and long-running agent interactions. The service automatically expects/uses session tables (schema provided in the README)

Key Changes

  1. New Module: google-adk-mysql-session-service
    A new contribution module located in contrib/mysql-session-service containing:
  • MySqlSessionService:

    • Implements the BaseSessionService interface.
    • Manages the full session lifecycle (create, get, list, delete).
    • Handles event appending with transactional integrity.
    • Uses same ObjectMapper as parent ADK library for efficient JSON serialization of session state and context.
  • MySqlDBHelper:

    • Encapsulates low-level JDBC operations to ensure clean separation of concerns.
    • Handles safe parameter mapping and result set extraction.
    • Optimistic Locking: Implements robust concurrency control using the updated_at column.
    • Precision Handling: Includes specific logic to truncate Java Instant nanoseconds to MySQL TIMESTAMP millisecond precision, preventing spurious ConcurrentModificationException errors caused by rounding mismatches.
  1. Test Coverage
  • MySqlSessionServiceUnitTest: Tests service logic in isolation using Mockito to mock database interactions.
  • MySqlDBHelperTest: Focuses on the data access layer, verifying correct SQL generation, JSON conversion, and optimistic locking mechanics.
  • MySqlSessionServiceIT: An integration test suite that verifies end-to-end functionality (CRUD, history appending, concurrency) against a live MySQL instance.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @voborl00, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a critical feature for the Agent Development Kit (ADK) by introducing a robust MySQL-backed session service. This implementation enables the ADK to persist session state and history in a relational database, which is essential for supporting distributed deployments and ensuring reliable, long-running agent interactions. The service is designed with transactional integrity, optimistic locking, and precise timestamp handling to maintain data consistency and prevent concurrency conflicts.

Highlights

  • New MySQL Session Service Module: Introduced a new contribution module, google-adk-mysql-session-service, located in contrib/mysql-session-service, providing a MySQL-backed implementation for session management.
  • MySqlSessionService Implementation: The MySqlSessionService implements the BaseSessionService interface, offering full session lifecycle management (create, get, list, delete) and transactional event appending, crucial for distributed ADK deployments.
  • Robust Database Helper: A dedicated MySqlDBHelper class encapsulates low-level JDBC operations, ensuring clean separation of concerns. It includes optimistic locking using the updated_at column and precision handling for Java Instant nanoseconds to MySQL TIMESTAMP milliseconds to prevent concurrency issues.
  • Comprehensive Test Coverage: The new module includes extensive testing: MySqlSessionServiceUnitTest for isolated service logic, MySqlDBHelperTest for data access layer verification using H2, and MySqlSessionServiceIT for end-to-end integration testing against a live MySQL instance via Testcontainers.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a robust MySQL-backed session service, which is a valuable addition for enabling persistent sessions in distributed environments. The implementation is well-structured, with a clear separation between the service layer (MySqlSessionService) and the data access layer (MySqlDBHelper). The inclusion of both unit and integration tests (using Testcontainers) is excellent and ensures high quality. My review focuses on a few key areas to further improve the module: refining dependency scopes for better consumer experience, enhancing transaction and error handling, improving maintainability by reducing code duplication, and ensuring consistency in data retrieval logic.

Comment thread contrib/mysql-session-service/pom.xml Resolved
@voborl00
voborl00 force-pushed the mysql-session-service-clean branch 2 times, most recently from cdfca97 to a27ebfa Compare December 5, 2025 19:20
@prasadskarmarkar

Copy link
Copy Markdown
Contributor

I reviewed this implementation, and it has several reliability and design gaps compared to the ADK architecture. It only supports MySQL, lacks parity with the Python ADK, and introduces behavior differences that would require a full architectural rewrite to align with the broader ADK goals.

This implementation is MySQL-specific and lives in contrib rather than core, limiting its applicability.
It does not offer multi-database support or integrate with the ADK core’s expectations for session services.
It also lacks parity with other ADK language implementations (e.g., Python) in terms of API surface and overall feature coverage.

A complete and production-ready alternative is already available here:
🔗 #654

this production ready alternative implementation provides:

  • A database-backed session service using JPA/Hibernate
  • Flyway-based schema versioning and migrations
  • HikariCP connection pooling
  • Multi-database support (PostgreSQL, MySQL, H2, etc.)
  • Thread-safe operations using pessimistic locking
  • Dialect-aware JSON storage (JSONB for PostgreSQL, CLOB for others)
  • Comprehensive test coverage using H2
  • Architecture consistent with the Python ADK (database session service included in the core package)

Given that a robust, feature-complete solution already exists and aligns fully with the ADK design principles, the most appropriate path forward is for the maintainers to close this PR to avoid duplication of effort and architectural inconsistencies.

Flagging this for maintainer review and consideration.

@voborl00

Copy link
Copy Markdown
Author

I've been following path which was agreed in implementation of Firestore session service (#601) as a contribution for good reasons and I will gladly discuss your stated opinions:

  1. My PR honors the "plugin architecture" by being dependency-free at compile time - it uses native java.sql package and only is relying on the runtime MySQL driver. In fact, my PR is better fitted to be part of the core of the ADK library than yours (though still mine targeted as contribution), because your PR forces heavy transitive dependencies (Hibernate ORM, Flyway, HikariCP, Jakarta Persistence) into the lightweight core module. This imposes a significant footprint on all library users, even those using built-in in-memory or alternative storage, dictates technology stack, increasing risk of "classpath hell" and version conflicts for users.

  2. There are many areas in which your PR is being wrongly implemented in opposition to my which is keeping line with built-in in-memory and Firestore implementations, for example:

  • data integrity violation: operating on "live" attached entities instead of detached copies making read operations destructive
  • concurrency & state management defects: failing to use optimistic or correct pessimistic locking on shared resources like user and app states
  1. Your PR's intention to be multiple database compliant brings performance and scalability issues:
  • generic JPA approach performs multiple synchronous database round-trips to fetch sessions, events, user and app states, while my specific implementation optimizes it into single-shot queries or joins, significantly reduces latency
  • using generic strings/CLOBs (to support all DBs), implementation sacrifices ability to index or query specific states within the database which degrades performance for state-heavy agent logic compared to native JSON support
  1. Insufficient test coverage which verifies "happy paths" but fails to verify side effects on persistence (did the list returns 3 items vs. did the db keep other 97 items?), missing regression scenarios with no test covering specific failure modes of generic implementation such as schema validation failures during migration locks or state merging race conditions.

While your PR aims for generality, it does so at the cost of data safety, performance, and modularity. My specific MySQL implementation is currently safer, faster, and architecturally cleaner for the Core library, but I am keeping it as `contribution following Firestore session service path. Maybe @glaforge who was validating the Firestore alternative can lead this to some productive outcome?

@glaforge

Copy link
Copy Markdown
Contributor

With the end of year festivities, we probably won't be looking at those contributions very closely in the next couple of weeks.
But it's wonderful to see both your contributions! Thank you very much.
They both have merits, so no need to fight against each other to find out which one is best!

@voborl00
voborl00 force-pushed the mysql-session-service-clean branch 7 times, most recently from 3a3a908 to 07ce188 Compare January 12, 2026 14:47
@voborl00

Copy link
Copy Markdown
Author

aligned mysql session service with current inmemoery session service progress (state delta attributes handling) and added unit and IT test accordingly, merged meantime library updates

@voborl00
voborl00 force-pushed the mysql-session-service-clean branch from 07ce188 to 08cec21 Compare March 26, 2026 13:38
@voborl00

Copy link
Copy Markdown
Author

updated with progress in 1.0 rc1 ("temp" attrs prefix, deprecated methods)

@hemasekhar-p hemasekhar-p self-assigned this Jul 30, 2026
@hemasekhar-p

Copy link
Copy Markdown
Contributor

Hi @voborl00, Thank you for your contribution! We appreciate you taking the time to submit this pull request. I noticed that the current build is failing with your changes. Could you please investigate and resolve these Maven build issues?

@hemasekhar-p hemasekhar-p added the waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. label Jul 30, 2026
@voborl00
voborl00 force-pushed the mysql-session-service-clean branch 3 times, most recently from 8026ea0 to fabce63 Compare August 4, 2026 06:55
@voborl00

voborl00 commented Aug 4, 2026

Copy link
Copy Markdown
Author

should be up-to-date in all aspects (code, deps, ...) now @hemasekhar-p

@hemasekhar-p

Copy link
Copy Markdown
Contributor

@voborl00, Thank you for addressing the comments. This PR is currently under review by our team and we will keep you posted if any changes are required. Thank you.

@hemasekhar-p hemasekhar-p added needs review and removed waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. labels Aug 24, 2026
@voborl00
voborl00 force-pushed the mysql-session-service-clean branch from fabce63 to a2be831 Compare October 20, 2026 11:49
@voborl00

Copy link
Copy Markdown
Author

alligned with latest adk version (newer session event apis), updated dependecies, fix ensuring appendEvent mutates caller's session only after db commit, tests

@damianmomotgoogle

Copy link
Copy Markdown
Contributor

Thx for update,

I'd like to clarify a need for MySQL specific database session service vs more generalized DatabaseSessionService.

i.e. if we get generalized Database / JDBC session service right, what do we gain from MySQL specific one?

@damianmomotgoogle damianmomotgoogle added waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. and removed needs review labels Oct 22, 2026
@voborl00

voborl00 commented Nov 3, 2026

Copy link
Copy Markdown
Author

@damianmomotgoogle, thanks for raising this

after comparing this implementation with the current jdbc proposal in #700, i believe there is still value in offering it as a separate, opt-in mysql contribution

hibernate/core-dependency concerns from my earlier comment no longer apply to #700. the remaining distinction is the operational and persistence model:

  • application-owned infrastructure: this service accepts an existing datasource and leaves connection pooling and schema deployment to the application (it does not create another pool or run migrations during construction, which fits deployments that already manage those concerns through their platform or deployment pipeline, without requiring hikari or flyway)

  • focused write path: in the current feat: Add DatabaseSessionService with JDBC and Flyway support #700 implementation, every append first loads and locks the session, app state and user state through three select for update queries. this implementation uses an optimistic session update and does not acquire preliminary locks on shared app/user state for local-only events

  • per-key shared-state updates: changing one app or user value updates that key through mysql upsert/delete, without fetching and rewriting the entire shared-state document - this reduces unnecessary data movement when shared state grows

  • mysql-specific storage choices: schema uses native json columns, whereas feat: Add DatabaseSessionService with JDBC and Flyway support #700 currently uses longtext for mysql. it also includes an explicit event sequence to preserve deterministic ordering when timestamps match. these are concrete storage choices. i would not claim a universal performance advantage without benchmarks

the service implements the standard base session service interface and reuses adk’s event/state handling. backend-specific, separately installable module also follows the existing firestore contribution pattern

generalized service can incorporate these capabilities, but portability does not require every deployment to adopt the same pooling, migration and persistence model. keeping this focused adapter in contrib would give mysql users that choice without adding dependencies to adk core

for those reasons, i think it merits inclusion as a separate mysql contribution alongside the generalized service. shared contract tests would help keep both implementations aligned with adk semantics.

@damianmomotgoogle

Copy link
Copy Markdown
Contributor

I checked all other ADKs:

  • Python: generic DatabaseSessionService
  • Go: generic session/database
  • TypeScript: generic DatabaseSessionService

So we'd still focus on a general DatabaseSessionService for ADK Java.

#700 still requires big rework, see my comment #700 (comment) and PR looks stale now

Questions is - would it make sense to upgrade this PR to a generalized DatabaseSessionService ? Following some of asks I included in #700 (comment) + other useful rework if applicable?

@voborl00

voborl00 commented Nov 3, 2026

Copy link
Copy Markdown
Author

@damianmomotgoogle thanks for clarifying the direction

yes, i am happy to evolve this existing PR (or create separate new - probably better) into a generalized database session service

i reviewed python, go and ts implementations and would use them as references for capabilities and design choices, while keeping the java base session service contract and inmemory session service as the behavioral reference. sharing a database schema across sdk languages would not be a goal of this PR.

my plan is:

  • initial db support: sqlite, postgresql, mysql and mariadb, with small internal dialect implementations and database-specific sql/types where useful
  • api and deps: caller-owned dataSource, a builder with explicit dialect selection and instantsource/uuid provider, package-private implementation classes, and no internally managed connection pool
  • schema management: explicit migrate() or opt-in migration, flyway over the supplied datasource, adk-specific migration locations/history, optional database modules, and the compatibility checks requested in your feat: Add DatabaseSessionService with JDBC and Flyway support #700 review
  • correctness: transactional event/state updates, stale-writer rejection, no unnecessary shared-state locking for local-only events, and in-memory updates only after a successful commit. full event json, utc timestamps, temporary-state filtering and exception handling would follow java contract and your review
  • testing: one reusable contract suite covering java inmemory behavior and all supported dbs, plus db-specific concurrency and schema tests. postgre/mysql/maria would use actual engines through testcontainers; sqlite would also include file-backed, multi-connection tests. CI would cover timestamp precision, ordering, key/collation behavior, constraints and rollback

before starting implementation - could you confirm two design points:

  1. is this four db initial scope appropriate, with sql server and spanner (supported in some of mentioned sdks) left for follow-up work?
  2. would retaining per-key app/user state storage be acceptable, provided java session semantics are preserved? it avoids reading and rewriting entire shared-state documents - alternatively, i can adopt the document-per-app/user model used by the other sdks

@damianmomotgoogle

Copy link
Copy Markdown
Contributor
  1. Initial set of supported DBs sounds fine - once we have core available it's gonna be easy to add more
  2. For now place it under contrib/database-session-service with google-adk-database-session-service artifact ID
  3. Once you have new PR, reference it from here. I can close other staled ones
  4. Regarding schema - it would be good to try to replicate existing ADK Python schema:

adk_internal_metadata

Column Type Key Notes
key VARCHAR(128) PK
value VARCHAR(256) One row: schema_version = "1"

sessions

Column Type Key Notes
app_name VARCHAR(128) PK
user_id VARCHAR(128) PK
id VARCHAR(128) PK
state JSON Session keys only (no app:, user: or temp:)
create_time TIMESTAMP(6)
update_time TIMESTAMP(6) Last event's timestamp; used for stale-writer detection

events

Column Type Key Notes
id VARCHAR(128) PK
app_name VARCHAR(128) PK, FK (app_name, user_id, session_id) → sessions, ON DELETE CASCADE
user_id VARCHAR(128) PK, FK
session_id VARCHAR(128) PK, FK
invocation_id VARCHAR(256)
timestamp TIMESTAMP(6)
event_data JSON (nullable) Whole event as JSON

Index idx_events_app_user_session_ts_id on (app_name, user_id, session_id, timestamp DESC, id DESC).

app_states

Column Type Key Notes
app_name VARCHAR(128) PK
state JSON All app: keys, prefix stripped
update_time TIMESTAMP(6) Database clock at the last write

user_states

Column Type Key Notes
app_name VARCHAR(128) PK
user_id VARCHAR(128) PK
state JSON All user: keys, prefix stripped
update_time TIMESTAMP(6) Database clock at the last write

Types:

  • JSON: JSONB on PostgreSQL, LONGTEXT on MySQL, TEXT holding serialized JSON elsewhere.
  • TIMESTAMP(6): DATETIME(6) on MySQL, DATETIME2(6) on SQL Server, the default datetime type elsewhere; stored as UTC without a time zone.
  • Every column is NOT NULL except events.event_data.

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

Labels

contrib waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants