Skip to content

feat: Add DatabaseSessionService with JDBC and Flyway support - #700

Open
prasadskarmarkar wants to merge 1 commit into
google:mainfrom
prasadskarmarkar:contrib/database-session-service
Open

prasadskarmarkar wants to merge 1 commit into
google:mainfrom
prasadskarmarkar:contrib/database-session-service

Conversation

@prasadskarmarkar

@prasadskarmarkar prasadskarmarkar commented Jan 2, 2026 •

Copy link
Copy Markdown
Contributor

fixes #665

Summary
Implements a production-ready database-backed session service that provides persistent storage for ADK sessions, events, and state using JDBC.

Key Features

  • JDBC with HikariCP connection pooling for optimal performance
  • Flyway migrations for schema versioning and zero-downtime deployments
  • Multi-database support: PostgreSQL, MySQL, H2, Cloud Spanner, and other RDBMS
  • Thread-safe operations with pessimistic locking for concurrent updates
  • Comprehensive test coverage with H2 in-memory database and Integration tests for PostgreSQL , MySQL and Spanner ( using emulator ) .
  • Dialect-aware JSON storage (JSONB for PostgreSQL, CLOB for others)
  • Event filtering and pagination for efficient data retrieval

Architecture

  • Located in contrib/database-session-service module

  • Minimal core dependencies footprint

  • Users explicitly opt-in via dependency

  • Follows existing contrib pattern

    Tasks

    • Implement DatabaseSessionService with JDBC (no ORM dependencies)
    • Add multi-database support (PostgreSQL, MySQL, H2, Spanner)
    • Implement 3-tier state storage (app/user/session levels)
    • Add Flyway migrations for schema management
    • Add comprehensive test suite (unit + integration tests)
    • Add documentation and usage examples
    • Address code review feedback

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @prasadskarmarkar, 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 robust DatabaseSessionService for the Agent Development Kit (ADK) Java, providing persistent session management capabilities. It utilizes standard JDBC with HikariCP for efficient connection pooling and Flyway for seamless schema migrations across multiple database systems like PostgreSQL, MySQL, H2, and Cloud Spanner. The service features a sophisticated 3-tier state management system and ensures data integrity through thread-safe, pessimistic locking. Accompanying tutorials illustrate its practical application in various agent workflows, making it easier for developers to integrate persistent state into their ADK agents.

Highlights

  • New Database Session Service: Introduces a production-ready, database-backed session service for ADK Java, enabling persistent storage for sessions, events, and state.
  • JDBC with HikariCP and Flyway: The service leverages pure JDBC with HikariCP for high-performance connection pooling and Flyway for automatic, versioned database schema migrations.
  • Multi-Database Support: It supports a variety of relational databases including PostgreSQL, MySQL, H2, and Cloud Spanner, with dialect-aware JSON storage.
  • 3-Tier State Management: Implements a flexible 3-tier state storage model (app-level, user-level, session-level) with defined merging priorities.
  • Thread-Safe Operations: Ensures thread-safe operations through connection pooling, database transactions, and pessimistic locking for concurrent updates.
  • Comprehensive Tutorials: New tutorials demonstrate the service's integration with sequential, loop, and custom tool agent patterns, showcasing session persistence and state sharing.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

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 significant new feature: a JDBC-based DatabaseSessionService. The implementation is robust, leveraging HikariCP for connection pooling and Flyway for database migrations. It's well-structured with clear separation of concerns between the service, DAOs, and SQL dialects. The code is thread-safe and handles resources correctly. I've identified a few areas for improvement, including a performance issue in the Spanner dialect, a bug related to session timestamp updates, and opportunities to use native JSON types in some database schemas for better performance and data integrity. There are also some minor documentation and code duplication points. Overall, this is a high-quality contribution.

@prasadskarmarkar
prasadskarmarkar force-pushed the contrib/database-session-service branch 7 times, most recently from 81b6887 to 7be5ac6 Compare January 4, 2026 15:46
@prasadskarmarkar
prasadskarmarkar force-pushed the contrib/database-session-service branch 3 times, most recently from 07e499e to 8bd47fd Compare January 16, 2026 02:24
@prasadskarmarkar

Copy link
Copy Markdown
Contributor Author

Hi ,

this PR addresses #665.
Could someone please review it when convenient?
Happy to make any changes based on feedback. Thanks!

@asorian0

Copy link
Copy Markdown

+1

* be empty). Use {@link #getSession} to retrieve full session details.
*
* <p>Sessions are ordered by update_time descending (most recently updated first), with a limit
* of 1000 sessions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this accurate? In SessionDao I see

    "SELECT * FROM sessions "
        + "WHERE app_name = :appName AND user_id = :userId "
        + "ORDER BY update_time DESC";

With no limit provided, or tests to verify

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @natdorshimer ,

You're absolutely right - thanks for catching this! The documentation incorrectly mentioned a 1000 session limit, but the SQL query has no LIMIT clause.

I've corrected the documentation to match the actual implementation. This also keeps the Java version consistent with the Python implementation, which also has no limit.

@prasadskarmarkar
prasadskarmarkar force-pushed the contrib/database-session-service branch 2 times, most recently from 8bd47fd to 9907827 Compare February 5, 2026 19:34
@prasadskarmarkar

Copy link
Copy Markdown
Contributor Author

Hi team — just checking in on this PR when you have a chance.

I’d appreciate a review whenever you have bandwidth and am happy to address any feedback.

Also, I’m working on updating related tutorials and documentation so this can be reflected in the official adk-java docs once merged.

Thanks!

@natdorshimer

Copy link
Copy Markdown

Just mentioning that I would also like to see this

Hi team — just checking in on this PR when you have a chance.

I’d appreciate a review whenever you have bandwidth and am happy to address any feedback.

Also, I’m working on updating related tutorials and documentation so this can be reflected in the official adk-java docs once merged.

Thanks!

Seconding that I would also like to see this soon - I've forked this and added it to a project I work on and have been using it extensively for maybe a month now. I haven't encountered any bugs yet. It saved me a lot of time, and hopefully save others more time in the future.

CREATE INDEX IF NOT EXISTS idx_sessions_app_user ON sessions(app_name, user_id);

-- Index for looking up events by session
CREATE INDEX IF NOT EXISTS idx_events_session ON events(app_name, user_id, session_id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I see in python they added desc('timestamp') to that index.
Maybe this is somethig to consider to be 1-1 parity with the python version? effectively this could be our only index then - replacing this one and the timestamp below. Also, the PK b-tree index should get us covered with app_name and user_id - hence maybe we can also drop ordering idx_sessions_app_user?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@sylwestergryzio - thanks for your comments.
I will take a look into this week.
Also, merge is blocked due to conflicts. resolving those as well.

@sylwestergryzio

Copy link
Copy Markdown

Thanks for that implementaiton @prasadskarmarkar!
Seconding that feature too! In adk-java I think this is a second highest missing feature after evaluations.
Curious if the ADK team has that on a radar anytime soon :) Thanks!

@hemasekhar-p

Copy link
Copy Markdown
Contributor

Hi @prasadskarmarkar, Thank you for your contribution! We appreciate you taking the time to submit this pull request. I have observed that this branch as conflicts. could you please resolve them?

@hemasekhar-p hemasekhar-p self-assigned this May 29, 2026
@hemasekhar-p hemasekhar-p added the waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. label May 29, 2026
@prasadskarmarkar

Copy link
Copy Markdown
Contributor Author

@hemasekhar-p - merge conflicts are resolved now.

@hemasekhar-p

Copy link
Copy Markdown
Contributor

@prasadskarmarkar, it looks like there are still some unresolved merge conflicts in this PR. Could you please rebase your branch and please squash your changes into a single commit ?

@hemasekhar-p hemasekhar-p removed the waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. label Jul 23, 2026
@hemasekhar-p hemasekhar-p added needs review waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. and removed needs review labels Jul 23, 2026
@prasadskarmarkar
prasadskarmarkar force-pushed the contrib/database-session-service branch from dcd7e1b to 87e506f Compare July 30, 2026 14:19
@prasadskarmarkar

prasadskarmarkar commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@hemasekhar-p - I've rebased onto the latest upstream/main and squashed everything into a single commit.
The branch is now conflict-free and up to date with main.

@hemasekhar-p

Copy link
Copy Markdown
Contributor

@prasadskarmarkar, thank you for resolving the merge conflicts and squashing the commits. Unfortunately, the Maven build is currently failing, could you please investigate and resolve these build issues?

@hemasekhar-p hemasekhar-p added waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. and removed waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. labels Jul 31, 2026
@paulparkinson

Copy link
Copy Markdown

I'd like to add Oracle Database support as well. Would you prefer Oracle support to be added to this PR, or submitted as a follow-up PR after #700 lands? Thanks!

fixes google#665

Summary
Implements a production-ready database-backed session service that provides persistent storage for ADK sessions, events, and state using JDBC.

Key Features

JDBC with HikariCP connection pooling for optimal performance
Flyway migrations for schema versioning and zero-downtime deployments
Multi-database support: PostgreSQL, MySQL, H2, Cloud Spanner, and other RDBMS
Thread-safe operations with pessimistic locking for concurrent updates
Comprehensive test coverage with H2 in-memory database and Integration tests for PostgreSQL , MySQL and Spanner(using emulator).
Dialect-aware JSON storage (JSONB for PostgreSQL, CLOB for others)
Event filtering and pagination for efficient data retrieval

Architecture

Located in contrib/database-session-service module
Minimal core dependencies footprint
Users explicitly opt-in via dependency
Follows existing contrib pattern

Tasks

[] Implement DatabaseSessionService with JDBC (no ORM dependencies)
[] Add multi-database support (PostgreSQL, MySQL, H2, Spanner)
[] Implement 3-tier state storage (app/user/session levels)
[] Add Flyway migrations for schema management
[] Add comprehensive test suite (unit + integration tests)
[] Add documentation and usage examples
[] Address code review feedback
@hemasekhar-p
hemasekhar-p force-pushed the contrib/database-session-service branch from 87e506f to 19913c7 Compare August 25, 2026 11:24
@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 25, 2026
@yvesemmanuel

Copy link
Copy Markdown

Hi everyone, thank you for your contributions to the Java version of the ADK.

I am following up to see if there are any updates regarding this PR.

@damianmomotgoogle

Copy link
Copy Markdown
Contributor

Thanks for this. Before a line-level review, please address the larger changes below; most lines will move, so I will follow up with detailed comments after the rebase and split.

  • API
    • Replace the URL and properties-map constructors with DatabaseSessionService(DataSource) and a builder (dialect, opt-in auto-migration, and InstantSource/UuidProvider like FirestoreSessionService).
    • Run migrations only through an explicit migrate() call (or the opt-in builder flag), not implicitly in the constructor. Drop the FLYWAY_* system properties and environment variables.
    • Make DatabaseSessionService the only public type, and make the dao, dialect, model, and util classes package-private (JdbcOperations.getConnection() currently exposes the raw Connection).
    • Remove getAppState, setAppState, getUserState, and setUserState. They are not part of BaseSessionService or the Python service, and getAppState fails when no row exists.
    • Throw only SessionException and SessionNotFoundException, without driver or Flyway exceptions or caller data in messages. Map duplicate ids to SessionException(SESSION_ALREADY_EXISTS) as documented by BaseSessionService.
    • Let callers specify the dialect explicitly; detecting it from the URL prefix rejects MariaDB, SQLite, SQL Server, and others.
  • Approach
    • Rebase on main so the module's parent version matches the root pom
    • Keep Flyway, but isolate it as an embedded library: run it over the caller's DataSource, use a dedicated history table (e.g. adk_schema_history) with baselineOnMigrate(true) and baselineVersion("0"), make the per-database Flyway modules optional, and verify compatibility with Flyway 12.4.0 (Spring Boot 4.1).
    • Rework appendEvent concurrency: lock the app and user state rows only when the event updates them (currently every append locks the app-wide row), create those rows in createSession (concurrent first writes currently lose data on PostgreSQL and deadlock on MySQL), and reject stale writers.
    • Finalize the storage format before the first release: store the full event.toJson() payload, store timestamps in UTC (they currently depend on the JVM time zone), and drop temp: keys.
    • Replace the per-database test copies with a single parameterized contract suite (like Go ADK's sessiontestsuite) and run it against every dialect in CI; currently, the PostgreSQL, MySQL, and Spanner suites are skipped when no database is reachable.
  • File layout
    • Move the migration scripts out of db/migration (Flyway's default location) to an ADK-specific path such as com/google/adk/sessions/migration/<dialect>/. Otherwise, an application running its own Flyway instance picks them up and fails with "Found more than one migration with version 1".
    • Fold dao/, model/, and util/ into package-private classes next to DatabaseSessionService. A single store class with small per-dialect SQL definitions is sufficient here, rather than a custom Spring-style JdbcTemplate layer.
    • In the module pom.xml, use the root version for the parent with the x-version-update marker, and keep HikariCP out of compile scope. In the root pom.xml, re-apply only the module entry and the Flyway entries on top of current main.
    • In tests, replace the copied TestLlm with core's test jar, and fix the two test classes that never run (NegativeTestCases due to its name, and AppendEventRaceConditionTest because it uses JUnit 4). The three ~800-line *IntegrationTest copies will be replaced by the contract suite.

@damianmomotgoogle damianmomotgoogle 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.

See above comment

@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 29, 2026
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.

Implement Database-Backed Session Storage (DatabaseSessionService) for ADK Java

9 participants