Skip to content

ENG-3666 Parallelize warm_async_pool connections with asyncio.gather#8097

Merged
johnewart merged 4 commits into
mainfrom
erosselli/ENG-3666
May 4, 2026
Merged

ENG-3666 Parallelize warm_async_pool connections with asyncio.gather#8097
johnewart merged 4 commits into
mainfrom
erosselli/ENG-3666

Conversation

@erosselli
Copy link
Copy Markdown
Contributor

@erosselli erosselli commented May 4, 2026

Ticket ENG-3666

Description Of Changes

Parallelizes the warm_async_pool function to open all database connections concurrently using asyncio.gather instead of sequentially. This reduces pool warm-up time from N * RTT to ~1 RTT, which significantly improves startup time for deployments with large pool sizes and/or high-latency database connections.

Partial connection failures are handled gracefully via return_exceptions=True — successful connections are still tracked and cleaned up properly.

Code Changes

  • src/fides/api/db/ctl_session.py: Replace sequential for loop with asyncio.gather for concurrent connection opening; also parallelize close() calls in the finally block
  • tests/lib/test_ctl_session.py: New test file with unit tests (mocked engine) and an integration test (real async engine against Postgres)

Steps to Confirm

  1. Run tests/lib/test_ctl_session.py — all 4 tests should pass
  2. Verify pool warm-up logs show N/N connections on startup with a readonly pool configured

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • No UX review needed
  • Followup issues:
    • No followup issues
  • Database migrations:
    • No migrations
  • Documentation:
    • No documentation updates required

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 4, 2026

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
fides-plus-nightly Ignored Ignored Preview May 4, 2026 3:31pm
fides-privacy-center Ignored Ignored May 4, 2026 3:31pm

Request Review

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@erosselli
Copy link
Copy Markdown
Contributor Author

/code-review

Copy link
Copy Markdown
Contributor

@claude claude 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 — Parallelize async pool warm-up (#8097)

The change is well-motivated and the approach is sound: replacing the sequential connection loop with asyncio.gather(..., return_exceptions=True) correctly avoids paying N × RTT on startup. Partial-failure handling, improved logging, and the parallel close in finally are all nice additions. The test suite is thorough and covers the key cases.

One real issue

conn.close() gather in finally is missing return_exceptions=True — if any close call raises, the exception propagates out of the finally block, where it will either silence an in-flight exception or produce an unhandled error. The fix is one word: return_exceptions=True on line 192, consistent with how the open-gather is written.

Minor / nit

  • The outer except Exception is now effectively dead code. With return_exceptions=True, individual coroutine failures are captured as return values and never raised, so there's no realistic path that reaches the catch. It could be removed or replaced with a clearer comment.
  • import asyncio (new) and from asyncio import Lock (existing) coexist — minor inconsistency worth consolidating.
  • import pytest in the test file is unused.

Overall this is a clean, focused improvement. The single actionable fix is the missing return_exceptions=True on the close-gather.

🔬 Codegraph: connected (48630 nodes)

💡 Write /code-review in a comment to re-run this review.

Comment thread src/fides/api/db/ctl_session.py Outdated
# Release all connections back to the pool
for conn in connections:
await conn.close()
await asyncio.gather(*(conn.close() for conn in connections))
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.

src/fides/api/db/ctl_session.py:192

The asyncio.gather in the finally block is missing return_exceptions=True. If any conn.close() raises, the gather will propagate that exception, which in a finally block will silently replace any exception already in flight (or cause an unhandled error in the normal case). Since closing a connection is best-effort cleanup, this should mirror the approach used for opening:

await asyncio.gather(*(conn.close() for conn in connections), return_exceptions=True)

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.

I don't think closing a connection should fail ? if it does, a loud exception might be better

Comment thread src/fides/api/db/ctl_session.py Outdated
Comment thread src/fides/api/db/ctl_session.py Outdated
Comment thread tests/lib/test_ctl_session.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.01%. Comparing base (5496449) to head (5e96c64).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8097      +/-   ##
==========================================
+ Coverage   84.98%   85.01%   +0.03%     
==========================================
  Files         633      633              
  Lines       41737    41736       -1     
  Branches     4886     4886              
==========================================
+ Hits        35471    35483      +12     
+ Misses       5157     5145      -12     
+ Partials     1109     1108       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

erosselli and others added 2 commits May 4, 2026 12:16
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@erosselli erosselli requested a review from johnewart May 4, 2026 15:30
@erosselli erosselli marked this pull request as ready for review May 4, 2026 15:31
@erosselli erosselli requested a review from a team as a code owner May 4, 2026 15:31
@erosselli erosselli changed the title Parallelize warm_async_pool connections with asyncio.gather ENG-3666 Parallelize warm_async_pool connections with asyncio.gather May 4, 2026
Copy link
Copy Markdown
Collaborator

@johnewart johnewart left a comment

Choose a reason for hiding this comment

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

👍🏼

@johnewart johnewart added this pull request to the merge queue May 4, 2026
Merged via the queue into main with commit 6f9417b May 4, 2026
69 checks passed
@johnewart johnewart deleted the erosselli/ENG-3666 branch May 4, 2026 17:25
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.

2 participants