Skip to content

Session.reconnect() drops session Configuration (sql_dialect, db, version); sessions silently revert to server defaults after a server restart #177

Description

@taka-2628

Summary

When a Session loses its transport (e.g. the server it is connected to restarts) and the client transparently reconnects, the reconnected session is opened without the sql_dialect, db, and version values the original Open() negotiated. The session silently reverts to server defaults. For table-model sessions this is severe: every subsequent statement fails to parse, because the session is now speaking the tree dialect while the caller keeps sending table-dialect SQL. It also affects any session that relied on a bound database (db).

Affected versions

Present on current main, and on released v2.0.3-1 and v2.0.8. The code path is unchanged across all three.

Root cause

The session-open request is hand-built in three places, and only two populate TSOpenSessionReq.Configuration:

  • Session.Open() — sets Configuration["sql_dialect"], ["version"], and (when non-empty) ["db"] (client/session.go, the block after the TSOpenSessionReq literal in Open).
  • Session.OpenCluster() — sets the same three.
  • Session.initClusterConn() — builds TSOpenSessionReq with no Configuration map at all.

reconnect() is written purely in terms of initClusterConn(), and it is the sole transparent-reconnect path — invoked from every insert/query/execute method on a transport-level error (err != nil && resp == nil), ~29 call sites. So any session, single-host or cluster, that survives a server restart and then issues a statement reconnects through initClusterConn() and loses its dialect/db/version. The values needed to rebuild Configuration are already on the session (s.config) at reconnect time — they are simply not used.

The reference (Java) client does this correctly

In apache/iotdb, SessionConnection.reconnect() routes through init(), which re-sends the configuration on every (re)open — putToConfiguration("version", ...), ("sql_dialect", ...), ("db", ...). The Go client's omission looks like an oversight (one of three copies of the same block was missed), not an intentional difference.

Why it's hard to diagnose

  • The failure surfaces as a valid server response (TSStatus code 700 SqlParseError, or 701 SemanticError "database is not specified"), converted into an *ExecutionError — not a Go transport error.
  • TableSessionPool therefore keeps the poisoned session: isConnectionError returns false for *ExecutionError/*BatchError, so the session is never dropped and Close() returns it to the pool. Every subsequent caller that draws it fails the same way until the process is restarted.
  • It only manifests when a server actually restarts mid-session — invisible to unit tests and to any deployment that doesn't bounce a server under load.

Reproduction (verified against a live server)

Server: apache/iotdb:2.0.3-standalone. Client: iotdb-client-go/v2 v2.0.8, TableSessionPool (size 1), session bound to a database.

  1. Open a table-model session bound to a database; create a table; INSERT a row (succeeds).
  2. Return the session to the pool.
  3. Restart the server (docker restart) so the socket drops but the endpoint returns.
  4. GetSession() (reuses the pooled session, which reconnects) and INSERT again.

Expected: the client reconnects transparently and the insert succeeds.
Actual (stock v2.0.8):

error code: 700, message: Error occurred while parsing SQL to physical plan: line 1:12 mismatched input 't' expecting ROOT

The reconnected session reverted to the tree dialect, so the tree parser expects a root.-prefixed path and rejects the table name. Every subsequent statement on that session keeps failing. (A session relying on a bound db with the dialect intact would instead see code 701, "database is not specified".)

Suggested fix

Populate TSOpenSessionReq.Configuration on the reconnect path the same way Open()/OpenCluster() do, from s.config. Because the same block is now hand-duplicated across all three open paths — and this bug exists precisely because one copy was missed — a clean fix extracts a single helper and wires all three sites through it. I verified locally that adding the Configuration map to initClusterConn makes the reproduction above succeed after the restart, with no other change. A PR follows.

A smaller, related gap for maintainers' awareness: initClusterConn also never sets s.timeFactor via getTimeFactor(resp), which Open/OpenCluster do — a reconnected session keeps a stale timeFactor. Benign on homogeneous clusters; happy to include it if wanted.

Environment

  • iotdb-client-go: main (also reproduced on v2.0.8, v2.0.3-1)
  • IoTDB server: 2.0.3-standalone
  • Go: 1.25

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions