Fix CancelledError handling in connection pool to prevent connection leaks
Problem: High-concurrency streaming requests with client disconnects trigger asyncio.CancelledError (BaseException in Python 3.8+), but exception handlers only caught Exception, causing: - yielded_sqlor.used permanently stays True - sqlor.exit() never called - Connection slots permanently consumed, eventual pool exhaustion Fix: 1. SqlorPool.context(): Use try/finally instead of try/except to always reset yielded_sqlor.used=False, even on CancelledError 2. DBPools.sqlorContext(): Change except Exception to except BaseException, add finally block to always call sqlor.exit(), suppress CancelledError logging noise Verification: Ad-hoc tests confirm connections properly reset on cancellation and can be reused. Normal exceptions still propagate correctly.
This commit is contained in:
parent
a2ef1bc48a
commit
627b955897
@ -126,13 +126,10 @@ class SqlorPool:
|
|||||||
yielded_sqlor.use_at = time.time()
|
yielded_sqlor.use_at = time.time()
|
||||||
try:
|
try:
|
||||||
yield yielded_sqlor.sqlor
|
yield yielded_sqlor.sqlor
|
||||||
|
finally:
|
||||||
|
# Always reset state, even on CancelledError (BaseException in Python 3.8+)
|
||||||
yielded_sqlor.used = False
|
yielded_sqlor.used = False
|
||||||
yielded_sqlor.use_at = time.time()
|
yielded_sqlor.use_at = time.time()
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
yielded_sqlor.used = False
|
|
||||||
yielded_sqlor.use_at = time.time()
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
@SingletonDecorator
|
@SingletonDecorator
|
||||||
@ -178,17 +175,23 @@ class DBPools(EventDispatcher):
|
|||||||
yield sqlor
|
yield sqlor
|
||||||
if sqlor and sqlor.dataChanged:
|
if sqlor and sqlor.dataChanged:
|
||||||
await sqlor.commit()
|
await sqlor.commit()
|
||||||
await sqlor.exit()
|
except BaseException as e:
|
||||||
except Exception as e:
|
|
||||||
self.e_except = e
|
self.e_except = e
|
||||||
cb = format_exc()
|
if not isinstance(e, asyncio.CancelledError):
|
||||||
exception(f'sqlorContext():EXCEPTION{e}, {cb}')
|
cb = format_exc()
|
||||||
|
exception(f'sqlorContext():EXCEPTION{e}, {cb}')
|
||||||
try:
|
try:
|
||||||
await sqlor.rollback()
|
await sqlor.rollback()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
await sqlor.exit()
|
raise
|
||||||
raise e
|
finally:
|
||||||
|
# Always call exit(), even on CancelledError (BaseException in Python 3.8+)
|
||||||
|
if sqlor:
|
||||||
|
try:
|
||||||
|
await sqlor.exit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_exception(self):
|
def get_exception(self):
|
||||||
return self.e_except
|
return self.e_except
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user