November 12, 2025·4 min read·engineeringrqliteclioperationsraft

rqlite 9.3.0 makes the shell's execute routing explicit

The shell can send entered SQL to the execute endpoint through .forcewrites. The same release avoids leadership stepdown for a standalone node.

An interactive SQL shell hides transport decisions until a user needs control over one of them. rqlite 9.3.0, released on 12 November, adds .forcewrites to choose the execute endpoint for ordinary entered SQL. The switch exposes a routing decision that the shell otherwise makes through its unified request path.

I like rqlite's small operating model, and an explicit shell setting is useful when investigating behavior at an API boundary. The name needs a precise explanation, though. The implementation changes the endpoint. It does not rewrite the entered SQL to turn a read into a data modification.

The default path uses the unified endpoint

PR #2385 introduces a forceWrites variable in the shell. It starts as false. Ordinary SQL reaches a request helper that selects db/request unless that variable is enabled.

When force-writes mode is true, the helper selects db/execute instead. It joins the chosen path with the configured client prefix and builds the request URL. The existing timing option still contributes its query parameter independently.

That is the concrete change delivered by the new command. It gives a shell session an alternative destination for entered statements. A user debugging an execute-specific behavior can reach that handler without building a separate HTTP request by hand.

The switch belongs to shell state. The patch creates and changes a local boolean in the interactive client. It does not send a request that permanently changes server configuration or select a cluster-wide policy for other clients.

The command supports inspection and toggling

The shell help lists the new command with on and off arguments. Calling it without an argument prints its current state. This makes it possible to inspect a session before attributing a result to one of the endpoint paths.

A short interaction can enable the mode, inspect it and return to the default routing. These are shell commands, not SQL statements.

.forcewrites on
.forcewrites
.forcewrites off

The same patch adds no-argument state reporting to .timer. That is a related usability improvement. A setting that affects subsequent requests is easier to reason about when its current value is available directly.

The scope of .forcewrites is narrower than a literal reading of all statements might suggest. The shell's command dispatcher handles dot commands before its ordinary-SQL branch. Commands such as .tables and .schema keep their dedicated query calls in the diff.

A user should therefore distinguish typed SQL from shell inspection commands. Enabling the setting changes the request helper used by the default branch. It does not replace every internal database access performed anywhere in the shell.

A routing choice is not an SQL transformation

The helper receives the original statement and changes the URL path. Nothing in this patch alters the SQL text based on the command name. That distinction matters because execute routing can be useful for diagnosis even when the statement's relational effect is unchanged.

The mode also does not provide a new transaction syntax, introduce a consistency level, or establish a performance result. Those would require changes beyond selecting a different handler. The release notes describe endpoint control, and the implementation supports that description.

My preference is for this limited mechanism. A diagnostic switch should expose the operation it changes without silently adding unrelated policy. Here, the session setting and the selected URL give a direct explanation of what the shell sends.

Standalone shutdown avoids an inapplicable stepdown

The release also changes shutdown behavior through PR #2384. Previously, enabling stepdown-on-shutdown led the server to attempt stepdown, even when the node was alone. The new condition requires the option, a non-standalone store and the leader role before calling stepdown.

The supporting IsStandalone method asks for the store's nodes. It returns true only when the list contains one node and that node's identity matches the local Raft identity. If the node list cannot be obtained, it returns false.

That is more specific than asking whether a node currently has reachable peers. Standalone status here describes the returned membership list and local identity. A multi-node configuration does not become standalone merely because another member is unavailable.

The tests check the positive single-node case and ensure multi-node stores do not report standalone. Together with the leader check, the shutdown condition limits leadership transfer work to the circumstances where this code intends to perform it.

Session control and membership control stay separate

The changelog groups the shell option with help improvements and dependency updates. The standalone change is another operational adjustment in the same release. It does not depend on the shell's force-writes mode.

For an interactive user, the visible addition is an inspectable choice between ordinary unified request routing and execute routing. For a standalone server, shutdown avoids attempting the configured leadership stepdown path. Both changes expose their conditions directly in the released implementation.