March 31, 2025·4 min read·engineeringrqlitesqlitediagnostics

rqlite 8.36.15 reports transaction state at the SQLite connection

New database statistics report transaction state for read and write connections. Their implementation defines what those booleans can establish.

A transaction statistic is useful only when its scope is clear. rqlite 8.36.15, released on 31 March, adds transaction status to database statistics. The implementation reads SQLite connection state. It does not report the progress of a distributed write, the outcome of a client request, or agreement among replicas.

That distinction fits what I appreciate about rqlite. The system combines a local relational engine with replication, but those mechanisms still have separate states. Diagnostics become more useful when they expose the relevant local fact without implying a stronger distributed guarantee.

Two connection classes get a transaction flag

PR #2058 adds a transaction entry to the database statistics map. Its helper builds a map with rw and ro entries corresponding to the database's read-write and read-only handles. For each handle, it obtains a connection, accesses the underlying SQLite driver connection, and calls AutoCommit.

The result stored in the statistics is the negation of that call. Autocommit enabled produces a false transaction flag. Autocommit disabled produces a true flag. Reading the inversion matters because the raw driver method and the public diagnostic describe opposite conditions.

An illustrative fragment of the added statistics is therefore straightforward. These values represent a possible observation, not a promised result for every running node.

{
  "transaction": {
    "rw": false,
    "ro": false
  }
}

The implementation associates each value with a connection obtained through the corresponding handle. That detail sets a limit on interpretation. The helper does not enumerate every live connection and return a separate record for each one. It produces one boolean per named handle after obtaining a connection through that handle.

The changelog describes transaction status for each connection, but the code gives the more precise operational description. A reader should resist treating this compact map as a complete transaction inventory for all requests. It contains neither client identifiers nor SQL text.

Collecting the statistic uses database connections

The helper obtains connections with a background context and defers closing them. The driver access occurs through the connection's Raw callback. Errors acquiring a connection or reading through that callback propagate back to the statistics method.

This makes the statistic different from a counter copied from an independent atomic variable. Collecting it participates in connection acquisition. The code does not specify a timeout in the context used by this helper, so a diagnostic consumer cannot infer a bounded collection time merely from the fact that the returned value is a boolean.

These observations follow directly from the patch. They do not establish that collection is slow or that any particular workload will block it. They explain what the collector does and why the meaning of its output depends on how it obtains the connection.

The placement inside Stats also matters. The helper runs before the final statistics map is assembled. If it returns an error, the method returns that error instead of the completed map. A failure to collect transaction state can therefore affect the database statistics result, rather than appearing as an isolated missing optional field.

Local state and leader state remain distinct

The same release includes PR #2057, which consolidates HTTP leader checks behind a service method. That method asks the store for its known leader address. It returns the original error when lookup fails and returns ErrLeaderNotFound when the address is empty.

Several handlers previously performed that empty-address check themselves. Moving it into a helper makes the distinction reusable. A missing leader and an error while obtaining the address remain different cases, with handlers using service-unavailable responses for the former and internal-server-error responses for other lookup failures.

Queued execution shows a small behavioral distinction in the diff. Previously it grouped a lookup error and an empty leader address into the same service-unavailable response. The new code distinguishes the missing-leader error from other lookup errors. Calling the whole patch a pure refactor would miss that changed classification.

Neither leader lookup nor the new transaction flag can substitute for the other. One describes the node's knowledge of a Raft leader. The other comes from the local SQLite driver connection. An operator investigating a request needs to know which layer supplied the observation before using it to explain the failure.

A small diagnostic with a specific meaning

The release changelog also records dependency updates. It does not announce a new transaction API or a new consistency level. The transaction addition belongs to database diagnostics, and its value is the visibility of a state that was previously absent from that map.

My preference is to read this field together with its acquisition path. A boolean without a scope invites explanations that the implementation cannot support. Here, the source makes the scope inspectable and keeps the mechanism compact enough to understand without reconstructing the rest of the server.

For clients consuming these statistics, the new transaction object adds the rw and ro observations. Their interpretation should remain tied to SQLite autocommit state on the connections obtained during collection. Nothing in this patch turns those observations into a record of replicated transaction completion.