microsoft / microsoft/mssql-python

Deadlock under concurrent multithreaded use when DEBUG logging is enabled via setup_logging()

Aperta
#671 2 commenti 0 reazioni 1 assegnatario Vedi su GitHub

@bewithgaurav ci sta già lavorando.

Dal 10/7/2026.

area: performance bug inADO triage done under development
Lingua principale
Python
Stelle
473
Fork
60
Merge medio
2g 11h
PR unite (30g)
36

Descrizione

Describe the bug

When driver logging is enabled via mssql_python.setup_logging(output="file") (DEBUG level) and multiple threads concurrently open connections and execute statements (for example a ThreadPoolExecutor with 4 workers), the process permanently deadlocks. It sits at 0% CPU — fully frozen, not merely slow.

The same code with setup_logging not called does not deadlock. It also does not deadlock with a single worker thread. The trigger is therefore the combination of DEBUG logging + concurrency (workers > 1).

Environment
  • mssql-python version: 1.10.0
  • Python version: 3.12.11
  • Operating system: macOS (arm64)
  • Authentication: ActiveDirectoryDefault (Entra ID) against a Microsoft Fabric Warehouse. The deadlock is in the logging path and is very likely authentication-independent.
Observed thread stacks

Captured with Python's faulthandler. The dumps were identical across 7 dumps over roughly 7 minutes, which confirms a permanent deadlock rather than slowness.

Thread A (opening a connection):

File ".../mssql_python/connection.py", line 571 in setautocommit
File ".../mssql_python/connection.py", line 410 in __init__
File ".../mssql_python/db_connection.py", line 55 in connect
-> (calls into Python logging) ...
File ".../logging/handlers.py", line 75 in emit
File ".../logging/__init__.py", line 1144 in flush   # blocked here

Thread B (executing a statement, a COPY INTO):

File ".../mssql_python/cursor.py", line 1545 in execute   # blocked in native execute
Server-side check

While the client is frozen, the database server shows no query running for this session. This confirms the block is entirely client-side and is not waiting on the server.

Hypothesized mechanism

mssql-python emits DEBUG log records from within native code paths (for example setautocommit during connect, and cursor.execute) that hold the GIL / internal locks. Under concurrency this creates a lock-acquisition cycle with Python's logging Handler locks across threads:

  • Thread A holds the logging handler lock and is blocked in flush.
  • Thread B is blocked in native execute and cannot proceed.

This is a classic logging-from-native-under-the-GIL deadlock. It only reproduces with workers > 1.

Steps to reproduce (minimal sketch)
  1. Call mssql_python.setup_logging(output="file") to enable DEBUG logging.
  2. From N >= 2 threads, concurrently open connections (autocommit=True) and run statements against the same server.

The process deadlocks within seconds to a couple of minutes.

Expected behavior

Enabling DEBUG logging should not affect correctness. Concurrent, multithreaded workloads should run to completion whether or not setup_logging is enabled.

Workaround

Do not enable setup_logging for concurrent / multithreaded workloads.

Impact

This makes the DEBUG logging feature unusable for any multithreaded application (for example parallel data-loading), which is precisely the scenario where enabling DEBUG logging is most valuable for diagnosis.

Possibly related issues

The following existing issues appear related but are distinct from this report:

  • #540 — GIL held for the entire duration of cursor.execute() (describes the native-call-holds-GIL behaviour that is part of the hypothesized mechanism here, but is about starvation rather than a permanent deadlock and does not involve logging).
  • #321 — multithreaded hang/deadlock with 3+ threads (does not involve logging; closed as non-reproducible).
  • #421 — hang with setup_logging enabled (single-threaded, diagnosed as a driver-load hang on Linux rather than a concurrency logging-lock deadlock).

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.