-
Notifications
You must be signed in to change notification settings - Fork 25
Description
Describe the bug
Connection pooling hangs indefinitely when pooling(enabled=False)
is called after any connection has been created from the pool.
The pooling(enabled=False)
function causes an infinite hang regardless of whether connections are still open or have been properly closed. This affects both individual scripts and the test suite, making it impossible to disable pooling cleanly.
Root Cause: The PoolingManager.disable()
method only sets an internal flag but never actually calls the underlying C++ close_pooling()
function to clean up connection pools. This leaves active connection pools with their mutexes and threads running, causing a deadlock during program termination.
To reproduce
from mssql_python import connect, pooling
import os
# Set your connection string
conn_str = "Driver={ODBC Driver 18 for SQL Server};Server=localhost,1433;Database=master;UID=sa;Pwd=YourPassword;Encrypt=no;TrustServerCertificate=yes;"
# This sequence ALWAYS hangs:
print("1. Enabling pooling...")
pooling(enabled=True) # ✅ Works fine
print("2. Creating connection...")
conn = connect(conn_str) # ✅ Works fine
print("3. Closing connection...")
conn.close() # ✅ Works fine (optional - hang occurs even without this)
print("4. Disabling pooling...")
pooling(enabled=False) # ❌ HANGS FOREVER - never returns
print("This line is never reached")
What works (no hang):
pooling(enabled=True)
alonepooling(enabled=False)
alonepooling(enabled=True)
followed immediately bypooling(enabled=False)
(no connections created)
What hangs:
pooling(enabled=False)
after ANY connection has been created from the pool- Happens regardless of whether connections are closed or left open
- Happens in test suites, causing them to hang indefinitely
Expected behavior
pooling(enabled=False)
should:
- Immediately clean up all active connection pools
- Terminate background threads and release resources
- Return control to the caller without hanging
- Allow the program to exit normally
Further technical details
Python version: 3.13.3
SQL Server version: SQL Server (Docker container with Microsoft ODBC Driver 18)
Operating system: macOS 14.7.1 (also affects Linux)
Impact:
- Makes it impossible to disable pooling in production code
- Causes test suites to hang when they try to clean up pooling
- Requires manual process termination (Ctrl+C)
- Affects any code that needs to toggle pooling on/off
Technical Details:
- Issue is in pooling.py in the
PoolingManager.disable()
method - The method sets
_enabled = False
but never callsddbc_bindings.close_pooling()
- The
atexit.register(shutdown_pooling)
checksis_enabled()
which returnsFalse
, so cleanup never happens - Connection pools remain alive with active mutexes, causing deadlock during termination
Additional context
This bug makes pooling effectively unusable in any scenario where you need to disable it programmatically. It particularly affects:
- Test Suites: Tests that enable pooling cause subsequent tests to hang when trying to clean up
- Production Applications: Cannot safely disable pooling during runtime
- Development/Debugging: Cannot toggle pooling on/off for performance testing
The bug appears to be a design flaw in the pooling disable mechanism rather than a race condition or platform-specific issue.
Proposed Fix:
The fix involves modifying PoolingManager.disable()
to actually call ddbc_bindings.close_pooling()
when disabling pooling, rather than just setting a flag. Additional safety checks should prevent double-cleanup scenarios.