Transaction timeout behavior during client-side session closure
0 reputation · 15 Jun 2021, 06:18 UTC
0 reputation · 15 Jun 2021, 06:18 UTC
Neo4j manages transaction lifecycles through dbms.transaction.timeout configurations and driver-level settings. While the system is designed to terminate long-running operations, there is uncertainty regarding how the engine handles resource cleanup when a client abruptly closes a session versus a natural timeout expiration.
When a client-side session is closed or the network drops, the driver is intended to signal the server to stop execution of the current query. However, if the query is performing a complex join or a resource-intensive write, the server may continue processing the task until the global transaction timeout limit is reached or the connection heartbeat failure is detected.
How does Neo4j distinguish between a graceful client-initiated cancellation and a server-side timeout in terms of resource reclamation? Is there a mechanism to ensure that server-side threads are released immediately upon session closure without waiting for the timeout?
34775 reputation · 15 Jun 2021, 06:51 UTC
Neo4j distinguishes between a graceful cancellation and a timeout primarily through the state of the Bolt connection. When a client explicitly closes a session or the driver sends a cancellation signal, the server attempts to interrupt the executing thread immediately. However, if the session is closed abruptly (e.g., a network drop or process crash), the server cannot distinguish this from a silent network lag until a connection failure is detected.
In the event of an abrupt client-side closure, the server-side thread typically continues processing until one of three events occurs:
dbms.transaction.timeout limit is reached, forcing a server-side termination regardless of connection state.To minimize the window where orphaned threads consume resources, you should implement a combination of driver-level and server-level constraints. There is no single "magic switch" to make the server instantly aware of a TCP drop without waiting for a timeout or I/O failure, but the following settings reduce the lag:
dbms.transaction.timeout to a value that reflects your maximum acceptable query duration to prevent "zombie" queries from running indefinitely.withTimeout() or similar session configurations to ensure the client does not hang, though this does not guarantee immediate server-side cleanup.To verify how your specific version of Neo4j handles these closures, perform the following test:
# 1. Start a long-running query (e.g., a complex Cartesian product) via Cypher Shell
# 2. Force-kill the Cypher Shell process (kill -9)
# 3. Immediately run the following query in a separate session to check active transactions:
CALL dbms.listTransactions()
If the transaction remains in the list after the client is dead, the server is relying on the global timeout or heartbeat failure rather than immediate connection detection.
Missing Diagnostic: Are you using a load balancer or connection pooler (like HAProxy) between the client and the server? These intermediaries often mask abrupt client closures from the Neo4j engine, forcing the server to rely entirely on the dbms.transaction.timeout.
Use comments to ask for clarification. Post a solution as an answer.
5,930 reputation · 15 Jun 2021, 17:06 UTC
Neo4j does not poll the transaction timeout continuously; it evaluates the limit only at defined safe points – typically after each Cypher operator yields control or when a procedure call returns. If a long‑running query is stuck in a tight loop (e.g., a complex graph algorithm or an APOC procedure that does not yield), the server will not notice that dbms.transaction.timeout has been exceeded until it reaches the next safe point, at which point it aborts the transaction and releases the thread. Similarly, when the driver sends a RESET or DISCARD ALL message on session close, the server attempts to interrupt the executing thread only at those same safe points. Consequently, immediate thread release is not guaranteed for non‑yielding workloads; you must rely on the global timeout or enable dbms.transaction.timeout.enabled=true to enforce abort after the configured interval regardless of yield points, or configure procedural calls to yield periodically.