Skip to content

fix(fixtures): add clean up teardown code to pytest fixture async_sqla_connection #266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

conste11ations
Copy link

@conste11ations conste11ations commented Feb 6, 2025

Problem

This issue came up on a test (with many test cases) in this PR. cc @anthony-bennett
Running the full tests were producing the following errors, as the connections gradually were exceeding the postgres instance limits (default of 100).

asyncpg.exceptions.TooManyConnectionsError: sorry, too many clients already

I created a page in Notion to document the short-term fix we put on our end and to support the use of Engine.dispose() in relevant fixtures that support our test suites.

Solution

Add teardown commands to the fixture that disposes of the created engine. Perhaps it is another question to ask why we have to create the engine so many times but I still think adding this teardown code is best practice.

Supporting documentation from sqlalchemy

Screenshot 2025-02-10 at 3 07 28 PM

Related

Validation

I'm open to ideas on how to add a test case that can validate fixture teardown code that usually happens outside of a test case - otherwise there is the Notion doc's findings. I can also demonstrate that this change + removing the short-term workaround on the other PR = solves the connection leak.

@conste11ations conste11ations self-assigned this Feb 6, 2025
Copy link

codecov bot commented Feb 6, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (9b36814) to head (a96818f).

Additional details and impacted files
@@            Coverage Diff            @@
##            master      #266   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           10        10           
  Lines          519       521    +2     
  Branches        33        33           
=========================================
+ Hits           519       521    +2     
Flag Coverage Δ
.10-sqlalchemy1.4-pydantic1-asyncpg-aws_rds_iam 98.08% <100.00%> (+<0.01%) ⬆️
.10-sqlalchemy1.4-pydantic1-asyncpg-noaws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.10-sqlalchemy1.4-pydantic1-noasyncpg-aws_rds_iam 74.85% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy1.4-pydantic1-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy1.4-pydantic2-asyncpg-aws_rds_iam 98.08% <100.00%> (+<0.01%) ⬆️
.10-sqlalchemy1.4-pydantic2-asyncpg-noaws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.10-sqlalchemy1.4-pydantic2-noasyncpg-aws_rds_iam 74.85% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy1.4-pydantic2-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy2.0-pydantic1-asyncpg-aws_rds_iam 97.50% <100.00%> (+<0.01%) ⬆️
.10-sqlalchemy2.0-pydantic1-asyncpg-noaws_rds_iam 96.54% <100.00%> (?)
.10-sqlalchemy2.0-pydantic1-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy2.0-pydantic1-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy2.0-pydantic2-asyncpg-aws_rds_iam 97.50% <100.00%> (+<0.01%) ⬆️
.10-sqlalchemy2.0-pydantic2-asyncpg-noaws_rds_iam 96.54% <100.00%> (-2.88%) ⬇️
.10-sqlalchemy2.0-pydantic2-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy2.0-pydantic2-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-aws_rds_iam 97.12% <100.00%> (?)
.10-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-noaws_rds_iam 96.16% <100.00%> (?)
.10-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-aws_rds_iam 73.89% <0.00%> (?)
.10-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-noaws_rds_iam 72.93% <0.00%> (-0.29%) ⬇️
.10-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-aws_rds_iam ?
.10-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-noaws_rds_iam 96.16% <100.00%> (+0.01%) ⬆️
.10-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-aws_rds_iam 73.89% <0.00%> (?)
.10-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-noaws_rds_iam 72.93% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy1.4-pydantic1-asyncpg-aws_rds_iam 98.08% <100.00%> (-0.96%) ⬇️
.11-sqlalchemy1.4-pydantic1-asyncpg-noaws_rds_iam 97.12% <100.00%> (?)
.11-sqlalchemy1.4-pydantic1-noasyncpg-aws_rds_iam ?
.11-sqlalchemy1.4-pydantic1-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy1.4-pydantic2-asyncpg-aws_rds_iam 98.08% <100.00%> (+<0.01%) ⬆️
.11-sqlalchemy1.4-pydantic2-asyncpg-noaws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.11-sqlalchemy1.4-pydantic2-noasyncpg-aws_rds_iam 74.85% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy1.4-pydantic2-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-pydantic1-asyncpg-aws_rds_iam 97.50% <100.00%> (?)
.11-sqlalchemy2.0-pydantic1-asyncpg-noaws_rds_iam 96.54% <100.00%> (+0.01%) ⬆️
.11-sqlalchemy2.0-pydantic1-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-pydantic1-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-pydantic2-asyncpg-aws_rds_iam 97.50% <100.00%> (+<0.01%) ⬆️
.11-sqlalchemy2.0-pydantic2-asyncpg-noaws_rds_iam 96.54% <100.00%> (+0.01%) ⬆️
.11-sqlalchemy2.0-pydantic2-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-pydantic2-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-aws_rds_iam ?
.11-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-noaws_rds_iam 96.16% <100.00%> (+0.01%) ⬆️
.11-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-aws_rds_iam 73.89% <0.00%> (?)
.11-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-noaws_rds_iam 72.93% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-aws_rds_iam 97.12% <100.00%> (-1.34%) ⬇️
.11-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-noaws_rds_iam 96.16% <100.00%> (+0.01%) ⬆️
.11-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-aws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.11-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-noaws_rds_iam 72.93% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy1.4-pydantic1-asyncpg-aws_rds_iam 98.08% <100.00%> (+<0.01%) ⬆️
.12-sqlalchemy1.4-pydantic1-asyncpg-noaws_rds_iam 97.12% <100.00%> (-2.88%) ⬇️
.12-sqlalchemy1.4-pydantic1-noasyncpg-aws_rds_iam 74.85% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy1.4-pydantic1-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy1.4-pydantic2-asyncpg-aws_rds_iam ?
.12-sqlalchemy1.4-pydantic2-asyncpg-noaws_rds_iam ?
.12-sqlalchemy1.4-pydantic2-noasyncpg-aws_rds_iam 74.85% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy1.4-pydantic2-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-pydantic1-asyncpg-aws_rds_iam ?
.12-sqlalchemy2.0-pydantic1-asyncpg-noaws_rds_iam 96.54% <100.00%> (+0.01%) ⬆️
.12-sqlalchemy2.0-pydantic1-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-pydantic1-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-pydantic2-asyncpg-aws_rds_iam ?
.12-sqlalchemy2.0-pydantic2-asyncpg-noaws_rds_iam 96.54% <100.00%> (+0.01%) ⬆️
.12-sqlalchemy2.0-pydantic2-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-pydantic2-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-aws_rds_iam ?
.12-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-noaws_rds_iam 96.16% <100.00%> (+0.01%) ⬆️
.12-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-aws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-noaws_rds_iam ?
.12-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-aws_rds_iam ?
.12-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-noaws_rds_iam 96.16% <100.00%> (+0.01%) ⬆️
.12-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-aws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.12-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-noaws_rds_iam 72.93% <0.00%> (-0.29%) ⬇️
.13-sqlalchemy1.4-pydantic1-asyncpg-noaws_rds_iam ?
.13-sqlalchemy1.4-pydantic1-noasyncpg-aws_rds_iam ?
.13-sqlalchemy1.4-pydantic1-noasyncpg-noaws_rds_iam ?
.13-sqlalchemy1.4-pydantic2-asyncpg-aws_rds_iam ?
.13-sqlalchemy1.4-pydantic2-asyncpg-noaws_rds_iam ?
.13-sqlalchemy1.4-pydantic2-noasyncpg-aws_rds_iam ?
.13-sqlalchemy1.4-pydantic2-noasyncpg-noaws_rds_iam ?
.13-sqlalchemy2.0-pydantic1-asyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-pydantic1-noasyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-pydantic2-asyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-pydantic2-asyncpg-noaws_rds_iam ?
.13-sqlalchemy2.0-pydantic2-noasyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-pydantic2-noasyncpg-noaws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-noaws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-noaws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-noaws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-aws_rds_iam ?
.13-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-noaws_rds_iam ?
.9-sqlalchemy1.4-pydantic1-asyncpg-aws_rds_iam ?
.9-sqlalchemy1.4-pydantic1-asyncpg-noaws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.9-sqlalchemy1.4-pydantic1-noasyncpg-aws_rds_iam 74.85% <0.00%> (?)
.9-sqlalchemy1.4-pydantic1-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy1.4-pydantic2-asyncpg-aws_rds_iam 98.08% <100.00%> (?)
.9-sqlalchemy1.4-pydantic2-asyncpg-noaws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.9-sqlalchemy1.4-pydantic2-noasyncpg-aws_rds_iam 74.85% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy1.4-pydantic2-noasyncpg-noaws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-pydantic1-asyncpg-aws_rds_iam 97.50% <100.00%> (+<0.01%) ⬆️
.9-sqlalchemy2.0-pydantic1-asyncpg-noaws_rds_iam 96.54% <100.00%> (+0.01%) ⬆️
.9-sqlalchemy2.0-pydantic1-noasyncpg-aws_rds_iam 74.28% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-pydantic1-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-pydantic2-asyncpg-aws_rds_iam 97.50% <100.00%> (+<0.01%) ⬆️
.9-sqlalchemy2.0-pydantic2-asyncpg-noaws_rds_iam 96.54% <100.00%> (?)
.9-sqlalchemy2.0-pydantic2-noasyncpg-aws_rds_iam 74.28% <0.00%> (?)
.9-sqlalchemy2.0-pydantic2-noasyncpg-noaws_rds_iam 73.32% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-sqlmodel-pydantic1-asyncpg-aws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.9-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-aws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-sqlmodel-pydantic1-noasyncpg-noaws_rds_iam 72.93% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-aws_rds_iam 97.12% <100.00%> (+0.01%) ⬆️
.9-sqlalchemy2.0-sqlmodel-pydantic2-asyncpg-noaws_rds_iam 96.16% <100.00%> (+0.01%) ⬆️
.9-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-aws_rds_iam 73.89% <0.00%> (-0.29%) ⬇️
.9-sqlalchemy2.0-sqlmodel-pydantic2-noasyncpg-noaws_rds_iam 72.93% <0.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@conste11ations conste11ations changed the title fix(fixtures): add clean up post-conditions to pytest fixture async_sqla_connection fix(fixtures): add clean up teardown code to pytest fixture async_sqla_connection Feb 6, 2025
@conste11ations conste11ations marked this pull request as ready for review February 10, 2025 21:59
@conste11ations conste11ations requested a review from a team as a code owner February 10, 2025 21:59
return create_async_engine(async_sqlalchemy_url)
engine = create_async_engine(async_sqlalchemy_url)
yield engine
engine.dispose()
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be awaited? IIUC it's an async function

Copy link

@thibremy-dialogue thibremy-dialogue left a comment

Choose a reason for hiding this comment

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

Thank you !

return create_async_engine(async_sqlalchemy_url)
engine = create_async_engine(async_sqlalchemy_url)
yield engine
engine.dispose()

Choose a reason for hiding this comment

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

On your notion page your context is anasync fixture`.

The engine.dispose() will just block on this sync fixture context or behind the scene a future/promise is run and will be completed at some point in time ?

Copy link
Contributor

@arththebird arththebird left a comment

Choose a reason for hiding this comment

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

I'm open to ideas on how to add a test case that can validate fixture teardown code that usually happens outside of a test case - otherwise there is the Notion doc's findings. I can also demonstrate that this change + removing the short-term workaround on the other PR = solves the connection leak.

Can we add a test that replicates what was happening when you encountered the issue and confirm that this indeed fixes the issue?

@conste11ations
Copy link
Author

Hi @thibremy-dialogue @arththebird, I'm parking this temporarily for now:

As it turns out, document-center uses a hardcoded version of fastapi-sqla at 2.10.1. I've got to get it up to 3.4.8 (document-center tests fail on this version right now) before I can demonstrate that the change I am proposing (and what sqlalchemy recommends as best practice) will resolve the original problem.

@arththebird just putting down notes here - we had a chat and we agreed that, in place of a test, I will link this PR branch as a dependency to document-center and demonstrate that the issue is resolved with this PR. I'll provide video evidence of that once I get document-center to use the latest version of fastapi-sqla!

@thibremy-dialogue thibremy-dialogue dismissed their stale review February 20, 2025 18:41

Failed downstream

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

Successfully merging this pull request may close these issues.

3 participants