Summary
Idle TCP RST (TCP reset: peer or middlebox abruptly closes the socket while the client still believes the session is alive) leaves FreeTDS dbdead / TinyTDS active? stale-true until the next real I/O. Rails 8 verify! then hands out a half-dead checkout. The next statement can hang for the connection’s full FreeTDS statement timeout (often minutes) before failing with something like Adaptive Server connection timed out.
This is the checkout-liveness half of #1396. Exception mapping (DBPROCESS is dead… → ConnectionNotEstablished) was addressed in #1397 / 8.0.11; detecting the dead handle before the real query still needs a bounded round-trip.
Rails / adapter context
- Postgres and Trilogy still probe in
active? today (query ";" / #ping).
- This adapter used to run
raw_connection_do "SELECT 1" in active? until #1121 (Oct 2023) switched to passive TinyTDS active?.
- A naive return to
SELECT 1 would restore probing but hang on the connection’s multi-minute FreeTDS timeout when the socket is already dead. The probe needs a short FreeTDS DBSETTIME, not Ruby Timeout (which does not reliably interrupt TinyTDS/C I/O).
Proposed split
| Layer |
Responsibility |
PR |
| tiny_tds |
Driver primitive: Client#ping(timeout:) + query_timeout= (temporary short DBSETTIME, SELECT 1, restore on success; false on failure) |
rails-sqlserver/tiny_tds#609 |
| activerecord-sqlserver-adapter |
Policy: call #ping from active? when available; :ping_timeout (default 2s, 0 disables); fallback to passive active? on older TinyTDS |
rails-sqlserver/activerecord-sqlserver-adapter#1412 |
Ask for maintainers
Please take a look at both PRs. tiny_tds appears lightly maintained right now, and without a reviewed #ping (or equivalent) the adapter cannot match Postgres/Trilogy checkout behavior without reintroducing long hangs. Adapter #1412 is ready to consume #ping once tiny_tds#609 lands (or guidance on an alternate driver API).
Happy to adjust either PR based on preferred defaults, naming, or whether ping should live only behind verify! vs every active?.
Related
Summary
Idle TCP RST (TCP reset: peer or middlebox abruptly closes the socket while the client still believes the session is alive) leaves FreeTDS
dbdead/ TinyTDSactive?stale-true until the next real I/O. Rails 8verify!then hands out a half-dead checkout. The next statement can hang for the connection’s full FreeTDS statement timeout (often minutes) before failing with something likeAdaptive Server connection timed out.This is the checkout-liveness half of #1396. Exception mapping (
DBPROCESS is dead…→ConnectionNotEstablished) was addressed in #1397 / 8.0.11; detecting the dead handle before the real query still needs a bounded round-trip.Rails / adapter context
active?today (query ";"/#ping).raw_connection_do "SELECT 1"inactive?until #1121 (Oct 2023) switched to passive TinyTDSactive?.SELECT 1would restore probing but hang on the connection’s multi-minute FreeTDS timeout when the socket is already dead. The probe needs a short FreeTDSDBSETTIME, not RubyTimeout(which does not reliably interrupt TinyTDS/C I/O).Proposed split
Client#ping(timeout:)+query_timeout=(temporary shortDBSETTIME,SELECT 1, restore on success;falseon failure)#pingfromactive?when available;:ping_timeout(default 2s,0disables); fallback to passiveactive?on older TinyTDSAsk for maintainers
Please take a look at both PRs. tiny_tds appears lightly maintained right now, and without a reviewed
#ping(or equivalent) the adapter cannot match Postgres/Trilogy checkout behavior without reintroducing long hangs. Adapter #1412 is ready to consume#pingonce tiny_tds#609 lands (or guidance on an alternate driver API).Happy to adjust either PR based on preferred defaults, naming, or whether ping should live only behind
verify!vs everyactive?.Related
dbdead/active?SELECT 1fromactive?Client#ping(timeout:)#pingfromactive?