Skip to content

fix: deduplicate concurrent database session initialization#646

Merged
MarvelNwachukwu merged 2 commits intomainfrom
fix-db-session-init-race
Mar 25, 2026
Merged

fix: deduplicate concurrent database session initialization#646
MarvelNwachukwu merged 2 commits intomainfrom
fix-db-session-init-race

Conversation

@MarvelNwachukwu
Copy link
Copy Markdown
Contributor

@MarvelNwachukwu MarvelNwachukwu commented Mar 19, 2026

Pull Request

Description

The DatabaseSessionService constructor fires initializeDatabase() as a fire-and-forget async call without deduplication. Concurrent callers (via ensureInitialized()) could pass the this.initialized boolean guard before either completes, triggering duplicate table creation work. This fix caches the initialization promise in initPromise so all concurrent callers share a single initialization. On failure, the cached promise is cleared to allow retry.

Related Issue

Closes #647

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

How Has This Been Tested?

  • All 479 existing tests pass (pnpm test --filter=@iqai/adk)
  • Build succeeds (pnpm build --filter=@iqai/adk)

Checklist

  • My code follows the code style of this project
  • All new and existing tests passed
  • My changes generate no new warnings
  • I have checked for potential breaking changes and addressed them

The constructor fired initializeDatabase() without deduplication, so
concurrent callers via ensureInitialized() could trigger duplicate
initializations. Cache the init promise so all callers share a single
initialization, with retry on failure.
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
adk-typescript-docs Ready Ready Preview Mar 20, 2026 0:02am
adk-web Ready Ready Preview Mar 20, 2026 0:02am

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 19, 2026

🦋 Changeset detected

Latest commit: d25219b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@iqai/adk Patch
@iqai/adk-cli Patch
@iqai/mcp-docs Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 resolves a critical race condition within the DatabaseSessionService that could lead to multiple, concurrent database initialization attempts. By introducing a promise-based deduplication strategy, it guarantees that the database setup, including table creation, is executed exactly once, enhancing the stability and integrity of the application's data layer.

Highlights

  • Database Initialization Deduplication: Implemented a mechanism to deduplicate concurrent calls to initializeDatabase, ensuring that the database setup process runs only once and preventing duplicate table creation.
  • Race Condition Fix: Addressed a race condition where multiple ensureInitialized() calls could bypass the initialized boolean guard, leading to redundant and potentially problematic database table creation.
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.

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.

Copy link
Copy Markdown
Contributor

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

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 fixes a race condition in DatabaseSessionService where concurrent initializations could occur. The fix introduces promise deduplication by caching the initialization promise, which is a solid approach. My review includes a suggestion to refactor the error handling logic slightly to improve separation of concerns, making the code more modular and maintainable. Overall, the change is correct and effectively solves the described issue.

Comment on lines +105 to +106
this.initPromise = this._doInitialize();
return this.initPromise;
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.

medium

For better separation of concerns, the logic for clearing initPromise on failure could be handled here in initializeDatabase rather than inside _doInitialize. This would make _doInitialize responsible only for the initialization logic, improving modularity. This change should be paired with removing the corresponding line from the catch block in _doInitialize (line 199).

    const promise = this._doInitialize();
    this.initPromise = promise;

    // Clear the promise on failure to allow retries.
    promise.catch(() => {
      // Avoid race conditions if a new initialization has started.
      if (this.initPromise === promise) {
        this.initPromise = null;
      }
    });

    return promise;


this.initialized = true;
} catch (error) {
this.initPromise = null; // Allow retry on failure
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.

medium

If the logic to clear initPromise is moved to initializeDatabase as suggested in my other comment, this line should be removed to avoid redundancy and keep _doInitialize free of side effects related to promise caching.

Move the initPromise clearing logic from _doInitialize's catch block
into initializeDatabase, with a guard to avoid clearing a newer
initialization's promise. Also adds changeset for the patch.
@MarvelNwachukwu
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 20, 2026

Claude finished @MarvelNwachukwu's task —— View job


Code Review for Database Session Initialization Race Condition Fix

Todo List:

  • Read and analyze the modified database session service file
  • Review the changeset file
  • Analyze the race condition fix implementation
  • Provide comprehensive review feedback
  • Consider existing review suggestions from Gemini

Analysis

The race condition fix in DatabaseSessionService is well-implemented and addresses a real concurrency issue. Here's my detailed review:

Problem Identified: The original constructor fired initializeDatabase() as fire-and-forget without deduplication, allowing concurrent ensureInitialized() calls to pass the boolean guard and trigger duplicate table creation.

Solution Overview: The fix introduces promise-based deduplication by caching the initialization promise in initPromise, ensuring all concurrent callers share a single initialization.

Key Changes Review

✅ Strong Points:

  1. Effective Race Condition Fix: Lines 79, 101-103, 105-106 properly implement promise deduplication
  2. Proper Error Handling: Lines 109-114 correctly clear the failed promise to allow retries
  3. Race Condition Prevention: The check if (this.initPromise === promise) prevents clearing a newer initialization attempt
  4. Clean Separation: _doInitialize() contains pure initialization logic while initializeDatabase() handles promise management
  5. Backward Compatibility: All existing APIs remain unchanged

⚠️ Areas for Consideration:

  1. Error Handling in Constructor: Lines 87-90 still use fire-and-forget pattern with just console.error, though this is less critical now with deduplication
  2. Missing Type Safety: initPromise could use more explicit typing as Promise<void> | null

Detailed Technical Review

Promise Deduplication Logic (Lines 97-117):

private initializeDatabase(): Promise<void> {
    if (this.initialized) {
        return Promise.resolve();
    }
    if (this.initPromise) {
        return this.initPromise; // ✅ Proper deduplication
    }
    
    const promise = this._doInitialize();
    this.initPromise = promise;
    
    promise.catch(() => {
        if (this.initPromise === promise) { // ✅ Prevents race conditions
            this.initPromise = null;
        }
    });
    
    return promise;
}

This is a textbook implementation of promise deduplication. The logic correctly handles:

  • Fast path for already initialized state
  • Promise reuse for concurrent calls
  • Safe cleanup on failure without interfering with newer attempts

Testing Considerations:
The fix should handle these scenarios correctly:

  • Multiple concurrent ensureInitialized() calls
  • Initialization failure followed by retry
  • Constructor initialization vs. explicit ensureInitialized() calls

Comparison with Gemini's Suggestions

Gemini suggested moving the error handling logic to initializeDatabase() and removing the this.initPromise = null from _doInitialize() (line 199). However, I don't see line 199 setting this.initPromise = null in the current code, so this may be an outdated suggestion.

The current implementation already follows good separation of concerns with _doInitialize() being pure and initializeDatabase() handling promise management.

Final Assessment

Verdict: ✅ Approve - This is a solid fix that effectively resolves the race condition

Impact: Low risk, high value bug fix that prevents duplicate database operations
Code Quality: Clean, well-structured, follows TypeScript best practices
Testing: All existing tests pass, indicating good backward compatibility

The implementation correctly solves the described race condition with minimal code changes and maintains all existing functionality.

@MarvelNwachukwu MarvelNwachukwu requested a review from Timonwa March 20, 2026 00:39
Copy link
Copy Markdown
Contributor

@Timonwa Timonwa left a comment

Choose a reason for hiding this comment

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

LGTM

@MarvelNwachukwu MarvelNwachukwu merged commit f5dbd33 into main Mar 25, 2026
5 checks passed
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.

DatabaseSessionService: concurrent initialization race condition

2 participants