-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add engine accessor to SQLAlchemySession for closing it when it's created from a URL #1960
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
Add engine accessor to SQLAlchemySession for closing it when it's created from a URL #1960
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds a close() method to SQLAlchemySession to enable proper engine disposal and prevent resource leaks in long-running applications. The implementation follows the same ownership pattern as RedisSession.
Key Changes:
- Adds
_owns_engineflag to track whether the session owns its engine - Sets
_owns_engine=Truewhen usingfrom_url()factory method - Implements
async def close()method that disposes the engine only when owned
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
8652244 to
161ea21
Compare
|
Thanks for the review! I've updated the docstring to include the |
|
I think simply adding the accessor to _engine would be simpler and more flexible |
161ea21 to
e659635
Compare
|
Thank you for the feedback! I've updated the PR to add the Now users have both options:
This approach:
Does this align with what you had in mind? Happy to adjust further if you prefer a different approach. |
| """ | ||
| return self._engine | ||
|
|
||
| async def close(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to remove this general method because the sessions store interface does not declare this method
|
No, I don't think we should add close() method |
Adds a read-only engine property to provide direct access to the underlying AsyncEngine for advanced use cases such as: - Checking connection pool status - Configuring engine settings - Manually disposing the engine when needed Users are responsible for managing the engine lifecycle when using the engine property directly.
e659635 to
74e1d74
Compare
|
Thank you for the clarification! I completely understand now. I've updated the PR to follow your suggestion:
Now the implementation is simple and clean - just a property accessor. Users are responsible for managing the engine lifecycle themselves using This aligns with the SessionABC interface and keeps the implementation minimal. |
|
I've added comprehensive unit tests to verify the Test Coverage
✅ Local Test ResultsAll tests pass successfully: The tests follow the existing test patterns in the file and use the same in-memory SQLite setup. |
Adds three test cases to verify the engine property behavior: 1. test_engine_property_from_url: Verifies that the engine property returns the underlying AsyncEngine when created via from_url() and can be used for advanced operations like manual disposal 2. test_engine_property_from_external_engine: Verifies that the engine property returns the same instance when an external engine is injected, confirming users retain control 3. test_engine_property_is_read_only: Verifies that the engine property is read-only and cannot be reassigned, ensuring immutability of the engine reference
307d00b to
1bdc14f
Compare
|
Fixed CI failures:
All tests still pass locally: CI should pass now. |
Problem
Interface Inconsistency
Currently,
SQLAlchemySessionlacks aclose()method, making it inconsistent with other session implementations:RedisSessionhasasync def close()SQLiteSessionhasdef close()SQLAlchemySessionhas no close() methodThis inconsistency means users cannot properly clean up resources when using
from_url().Resource Leak in Long-Running Applications
When using
from_url(), each call creates a newAsyncEnginewith its own connection pool:SQLAlchemy Official Requirement
From SQLAlchemy Discussion #7531:
Why This Change Is Needed
1. API Consistency
Users expect all session types to have consistent lifecycle management. The
close()method should be available across all session implementations.2. Follows Best Practices
SQLAlchemy's async engine requires explicit disposal. Without it, applications may see:
3. Prevents Resource Leaks
In production web services handling multiple users, each
from_url()call accumulates engines that are never cleaned up.4. Backward Compatible
This is a non-breaking change:
close()methodclose()Solution
Following the same pattern as
RedisSession:1. Track Engine Ownership
2. Mark Ownership in
from_url()3. Add
close()MethodUsage Examples
Example 1: Using
from_url()(SDK owns engine)Example 2: External engine (user owns engine)
Testing
Tested manually with both scenarios:
from_url()disposes engine onclose()close()Changes
_owns_engineflag to track engine ownershipfrom_url()sets_owns_engine=True_owns_engine=Falseasync def close()methodRedisSessionresource management patternImpact
Scope: Long-running applications using
from_url()in multi-user scenariosSeverity: Medium - Not critical for short-lived scripts, but important for production web services
Compatibility: Fully backward compatible - existing code unaffected