January 30, 2017·4 min read·engineeringrqliteraftconfiguration

rqlite exposes the timeout passed to Raft Apply

rqlite 3.10.0 replaces a fixed ten-second Raft Apply argument with an operator-supplied duration. The same setting covers writes, strong reads and replicated API-peer metadata.

A timeout named after Raft can describe several different waits. Detecting an absent leader, submitting a command and waiting for an HTTP response are different operations. rqlite 3.10.0, published on 30 January, makes one specific value configurable. Operators can now select the duration that the store passes to Raft's Apply method.

The default remains ten seconds. The implementing patch in pull request 261 replaces a fixed store constant at each Apply call with s.ApplyTimeout. It also adds a command-line option and includes the selected duration in store statistics. This is a focused operational change, with its scope visible in the affected calls.

I like configuration that names the mechanism it controls. rqlite supplies a small distributed database, but that does not make every deployment's timing requirements identical. Exposing the existing Apply argument lets an operator change that policy without maintaining a source patch or changing an unrelated heartbeat setting.

The old value becomes the default

The released server entry point adds -raftapplytimeout, with a default string of 10s. Startup parses the supplied value with time.ParseDuration and assigns it to the store. A parsing failure stops startup with a message identifying the invalid Apply timeout.

rqlited -raftapplytimeout 20s ~/node.1

This invocation selects twenty seconds as the argument supplied at the affected Apply calls. It does not establish a twenty-second deadline for every SQL query or every HTTP request. The exact scope is the store's use of the Raft interface, which is narrower than an application-wide request deadline.

The heartbeat control remains -rafttimeout, defaulting to one second in the same source file. That option populates HeartbeatTimeout, while the new option populates ApplyTimeout. Their separate variables and parsing paths make clear that changing one does not change the other.

The store constructor also initializes the field to ten seconds. The default is therefore present for a store created by code, in addition to the command-line default used by the server executable. This matters because the store is a Go component with callers beyond the argument parser.

Writes use the configurable argument

The 3.10.0 store implementation shows the execution path. It first rejects an execute request if the local node is not the leader. It then builds a command containing the SQL queries, transaction choice and timing choice, serializes that command and calls s.raft.Apply with the configured duration.

The method checks the returned future for an error before reading its response. A Raft not-leader error becomes the store's ErrNotLeader; other errors propagate. The new setting changes an argument to this existing sequence. It does not replace the future or invent a new successful-execution condition.

This ordering is useful when interpreting the configuration. Being the leader at the initial check is not the only possible outcome of the request. The Apply future can still return an error, and the caller must still process it. A larger duration is not evidence that a command succeeded, just as a smaller one does not describe the contents of the SQL result.

Strong reads share the same path

The patch also changes the Apply call used by strong-consistency queries. That query path serializes a query command and submits it through Raft before returning rows. Consequently, the setting has an effect beyond modifying statements.

The store code distinguishes this from weaker query paths. A weak read requires the local node to be leader and then queries the local SQLite database. Other local reads do not use this same Apply submission. The consistency level therefore determines whether a read encounters the newly configurable argument.

I find this detail important because a setting described casually as a write timeout would conceal its effect on strong reads. In rqlite, the consistency choice changes how the read reaches SQLite. Operational configuration should describe that actual path instead of classifying every SELECT as the same kind of request.

The third changed call updates the cluster-wide map of API peers. That operation also constructs a command and submits it through Apply. The selected duration is thus shared by SQL execution, strong query commands and this replicated metadata operation. It is not a per-endpoint override.

Statistics expose the selected value

Store statistics now include apply_timeout, formatted as a duration string. That makes the running value inspectable through the existing status machinery. A startup argument describes intent, while the statistics describe the value assigned to the store.

The release also logs GOOS and GOARCH at startup, according to its tagged changelog. This adds runtime-platform context alongside the new configuration visibility. Neither change supplies a benchmark or a recommended duration for every network. Their purpose is to make the deployment's chosen behavior and build target more explicit.

The release page is dated 30 January, while the changelog labels 3.10.0 as 29 January. This post follows the public release date. The substantive change is the same in both sources, a fixed ten-second Apply argument becomes a configurable store field, and the status output reports its effective value.