rqlite scopes freshness checks to local reads
rqlite 5.1.1 corrects how freshness interacts with consistency levels. The implementation now applies its leader-contact threshold only to reads using level none.
A read option should apply to the consistency mode whose behavior it describes. rqlite 5.1.1 fixes an interaction between its freshness threshold and read consistency selection. The March 28 release announcement calls it a bug related to stale reads and freshness checking. Its implementation shows a specific correction, adding the consistency-level condition to the freshness test.
The freshness feature arrived with non-voting read replicas. It allows a request reading local SQLite state to reject a node that has gone too long without contact with the leader. This is useful for local reads, whose contract otherwise permits a node to answer without establishing leadership. The check has a different meaning from the leadership check used by another consistency mode.
I value rqlite's explicit per-request consistency choices. Those choices are useful only when their options compose according to the documented rules. This patch makes the implementation's admission test agree more closely with the level the caller selected.
The previous condition reached weak reads
The 5.1.0 query implementation handles strong reads first and returns from that path. It next rejects a weak read when the node is not leader. After those branches, it applies the freshness test whenever the supplied duration is positive, before calling the local database.
That last condition does not ask whether the request selected none. A weak read accepted by the leader check can consequently reach the leader-contact freshness test too. The interaction is visible in the control flow without assuming that every possible stale-read problem has the same cause. The issue is which requests receive this particular test.
In the 5.1.1 implementation, the condition includes qr.Lvl == None. The remaining checks still require a positive duration and compare elapsed time since LastContact() with the requested threshold. An excessive interval returns ErrStaleRead. The local database query follows only after applicable admission checks pass.
This is a narrow correction. It does not replace freshness with a quorum read or introduce a new consistency level. It limits an existing condition to the mode for which the documentation defines it. Strong reads retain their earlier separate path, and weak reads retain their local leadership check.
Freshness measures leader contact
The consistency documentation in this release says none reads query the node's local SQLite database without checking whether it is leader. The freshness option adds a check of how long the node has been out of contact with the leader. A zero duration disables the check.
An illustrative query can request a local read with a one-second contact threshold. This assumes the selected node has a table named settings.
curl -G 'http://localhost:4001/db/query?level=none&freshness=1s' \
--data-urlencode 'q=SELECT * FROM settings'
The threshold does not inspect a timestamp in each row. Its input is the node's recent leader contact. That distinction matters because an old application record can be perfectly valid current state, while a recently contacted replica can still need to apply updates. The option should be interpreted through the measurement it actually performs.
A non-voting replica can use this level to serve local reads. Asking it for a leader-oriented level instead causes redirection toward the leader, according to the documentation. The endpoint's host alone therefore does not determine the read contract. The explicit level and its applicable options describe what the server should do with the request.
Weak and strong retain different coordination
Weak consistency is the default. It checks local leader state before querying SQLite, and the documentation describes a small possibility of stale data around a leadership change. Strong consistency sends the query through Raft and involves contacting at least a quorum. Its stronger coordination has a response-time cost.
The corrected freshness condition preserves those distinctions. A weak read with a freshness argument no longer receives a test documented for none merely because both paths eventually query local SQLite. Their shared execution destination does not imply that they share every admission rule. The selected mode still determines which checks belong on the path.
I prefer this explicit branch structure to treating a freshness value as a universal measure of read correctness. The threshold is useful for rejecting a disconnected local replica, while consensus is a different way to establish a read's coordination with the cluster. Applying the right test requires knowing what the caller requested.
The tagged changelog credits PR 636 and contributor wangfenjin for checking consistency before the freshness check. The actual added predicate explains the practical result of that description. A caller can continue choosing local reads with a contact-age threshold, while weak and strong requests follow their own documented consistency rules instead of inheriting that local-read restriction accidentally.