apache / apache/iotdb-client-rust

No client-side time bound after the TCP handshake (RPC reads, Drop teardown, and TLS handshake can block forever)

Aperta
#4 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
bug
Lingua principale
Rust
Stelle
1
Fork
0
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

## Summary

After `TcpStream::connect_timeout` succeeds, the client has **no time bound on any socket operation**. `connect_timeout` only covers the TCP handshake (`connection/mod.rs` `connect_stream`), and there is no `set_read_timeout` / `set_write_timeout` / `SO_KEEPALIVE` anywhere in the crate. `SessionConfig::query_timeout_ms` is a request-body field enforced server-side and does not cover `openSession`, `closeSession`, or the wire read itself. Every Thrift read after a successful handshake is therefore an unbounded blocking read.

This is one root cause with three manifestations (fix points in different places, one fix: a socket-level I/O timeout set before the TLS handshake and every RPC read):

### F1 — ordinary RPCs hang forever
Reproduced with a local listener that completes the TCP handshake and then never replies (equivalent to a long JVM GC pause, a silently-dropping firewall, or an accepting-but-not-forwarding LB):
- `Session::open()` with `connect_timeout = 200ms` was still blocked after 12s (the block is in the `openSession` read, past the connect timeout's scope).
- With a fake server that answers `openSession` + `requestStatementId` and then goes silent, `execute_non_query` blocked >8s with `enable_auto_reconnect = true` and `max_reconnect_attempts = 3`. `with_retry` only reacts to errors, and a wedged read produces none, so auto-reconnect never fires against exactly the failure it is meant to cover.
- Control: with the port closed, the same call returned in 176us.

### F2 — `Drop` becomes non-recoverable
Four destructors send RPCs that wait for a response: `Drop for Session` (`close()` → `closeSession`), `Drop for SessionPool` (serial `entry.session.close()` for every idle session), `Drop for SessionDataSet` (`close_query` → `closeOperation`), and the pool-closed branch of `PooledSession::release`. A destructor cannot return an error, be cancelled, or be given a timeout, and the failures are swallowed (`let _ =` / `log::debug!`). Reproduced against the same silent server:
1. `Session::open()` succeeds, then `drop(s)` blocks >12s.
2. `SessionPool::new(min_size = 1)` then `drop(pool)` blocks >12s; with 8 idle sessions only the first close is attempted and `notify_all()` is never reached.
3. `pool.close()` followed by a guard going out of scope blocks >12s in the closed branch of `release()`.
4. A panic unwind through a bare `Session`/`SessionPool` never completes.

The best-effort RPC-on-drop semantics match the C#/Node SDKs and should stay; they just need the socket-level bound to become best-effort again.

### F3 — TLS handshake has no deadline
`tls_handshake` runs rustls `complete_io` on a blocking socket with no read timeout. Reproduced with `use_ssl = true`, `connect_timeout = 200ms` pointed at a **non-TLS** IoTDB RPC port: the client sends ClientHello and waits for ServerHello; the plain framed transport misreads the record bytes as a ~369MB frame length and both sides wait forever. `Session::open()` was still blocked after 15s. The existing TLS tests use listeners that close the connection, so the client escapes on EOF and this shape is not covered.

## Proposed fix

- Add a **socket I/O timeout** (read + write) to `ConnectionOptions`/`SessionConfig`, applied to the socket immediately after connect and **before** the TLS handshake, so every read/write — TLS handshake, `openSession`, all RPCs, `closeSession` on drop — is bounded by it.
- With a timeout in place, wedged reads surface as `Error::Thrift`, so `with_retry`/reconnect actually engages, and the drop paths become bounded best-effort.

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

Start in connection/mod.rs at connect_stream and trace where the socket reaches tls_handshake, then inspect ConnectionOptions and SessionConfig. Review the Drop paths for Session, SessionPool, SessionDataSet, and PooledSession::release, along with the existing TLS tests. Done means socket operations, TLS setup, RPC reads, and best-effort drop RPCs are bounded and the silent-server cases no longer block indefinitely.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
rust
Ambito
networking
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
56/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.