August 9, 2017·4 min read·engineeringrqlitehttpcorrectness

rqlite preserves request parameters when redirecting to the leader

rqlite 4.0.2 includes the original query string in leader redirects. That preserves SQL queries, consistency choices and transaction flags carried in the URL.

A redirect can reach the correct server and still lose the operation the client asked it to perform. In rqlite, the URL query string can contain SQL, a consistency level or a transaction flag. Version 4.0.2, published on 9 August, fixes leader redirects that omitted those parameters from the destination URL.

The release-tagged changelog names pull request 337 as its single new change. The earlier code built a destination from the protocol, leader address and request path. It did not include RawQuery. The corrected helper appends that query string when one exists.

I regard this as a correctness fix in the client-facing protocol. A cluster contact is useful only if forwarding preserves the requested operation. Reaching the leader with a different query or different execution options does not complete the original request correctly.

The SQL can be part of the URL

The implementing patch includes an updated example of a follower redirect. A GET request to /db/query carries pretty, timings and a URL-encoded SQL statement in q. Before the fix, Location named only the leader's /db/query path.

The corrected destination retains those parameters. Changing the authority in the URL no longer discards the query text. This is particularly visible for GET queries because the SQL does not live in a request body that a custom client might independently retain.

For a request such as the following, the initial node needs to preserve both level and the encoded q value when it directs the caller elsewhere.

curl -G 'http://localhost:4003/db/query?level=strong' --data-urlencode 'q=SELECT * FROM foo'

This command demonstrates the initial request, rather than automatically following its response. It assumes the table exists and the addressed node serves the API. The point is what the leader destination must retain if that node returns a redirect.

Options also carry database semantics

The released HTTP service reads the transaction and timing choices before calling the store. Its query handler also determines the consistency level. These choices are part of the request the client made, independently of the SQL statement's text.

Dropping a formatting parameter may alter the representation a client receives. A missing consistency parameter can alter the read path. The transaction flag determines how a supplied batch executes, so omitting it also changes the operation. The helper's generic preservation of the query string addresses all those URL parameters rather than fixing one named option at a time.

The implementation copies RawQuery directly. It adds a question mark only when the string is nonempty, then appends it after the path. It does not decode parameters and rebuild them from an enumerated list. This retains the original encoded query representation and avoids needing the redirect helper to understand every endpoint's parameter vocabulary.

I like that division because redirect construction has a narrow responsibility. It replaces the contact address while retaining the rest of the request URL that the endpoint needs. The individual handler still owns the interpretation of level, transaction and the other parameters.

Several endpoints share the helper

The service calls FormRedirect when a store operation reports ErrNotLeader. Its execution and query handlers obtain the leader's API address from the store's peer information. If that address is unavailable, they return service unavailable instead of inventing a destination.

The join and load handlers also use the same redirect helper. Centralizing URL construction means the query-string repair applies wherever that helper supplies a Location value. The patch changes the shared construction step instead of duplicating parameter handling across handlers.

This remains separate from preserving a POST body. The server's destination URL cannot itself force every HTTP client to resend the original method and body. A client must still implement leader redirection appropriately. Version 4.0.2 repairs the server-provided URL, which is one necessary part of retaining the operation.

Regression tests check address changes

The patch adds tests for simple URLs and URLs containing parameters. One parameterized test constructs /db/query?x=y and checks the destination with the same host and with a different host. Both must retain the path and query string.

That test choice isolates the property under repair. The helper does not need a working cluster to demonstrate that changing a host must not remove ?x=y. Separate simple-URL tests ensure that preserving query strings does not add an unnecessary suffix when the input has none.

The documentation example also changes alongside the code. Its Location header now shows the encoded SQL, so the published protocol example agrees with the behavior checked by the new tests.

The changelog labels the patch 8 August, while the public release page records 9 August. This post follows the release publication. The change is small in code, but its effect is precise, a leader redirect retains the URL parameters that define the original database request.