rqlite backup timestamps and the lifetime of a clock value
rqlite 8.24.0 adds timestamped automatic backup names. Its first implementation captures a time value in a method, which limits the promised history of recovery points.
Overwriting one backup object cannot preserve the state from before a damaging application write. rqlite issue 1771, opened on May 2, asks for dynamic backup names to retain earlier database copies. The request also raises a permissions concern. The reporter wants the backup account to create and view backups while a separate storage lifecycle controls deletion. Distinct names would avoid repeatedly targeting the same stored object.
rqlite 8.24.0, released on May 3, introduces an optional timestamp on automatically uploaded backup names. The release describes this as support for point-in-time backups. Reading the implementation reveals a narrower result. The name receives a timestamp, but the clock function retains the first upload's time for subsequent uploads through the same client.
I value rqlite's proportionate operational model, and automatic backups belong in that assessment. Retaining multiple recovery points is useful when replication faithfully distributes an unwanted write. The naming behaviour matters because a successful upload and preservation of an earlier database state are different properties.
What the option changes
The backup configuration structure adds a top-level boolean named timestamp. Startup passes that value into the S3 client's options, as pull request 1773 shows. Leaving the field absent preserves the false value. This makes the change opt-in for existing automatic backup configurations.
The S3 implementation constructs the upload key from the configured path. When timestamps are enabled, it inserts a formatted time before the final path segment. Directory prefixes remain unchanged. A configured key such as database/backup.sqlite.gz therefore becomes a key with a time-prefixed filename under database/.
The helper uses a year, month, day, hour, minute and second representation followed by an underscore. The tests show that it handles keys with several path segments as well as a simple filename. This is a fixed formatting rule. It does not evaluate arbitrary shell expressions or provide the template substitution mechanism proposed in the original issue.
That design is sufficient to express the intended naming policy if each upload receives its own current time. The problematic part appears before the helper runs, in the initialization of the client's injectable clock function. The source assigns a bound method from a time value instead of a function that obtains a new time on every invocation.
A method that remembers its receiver
The relevant implementation in this release is short enough to inspect directly. The following excerpt is from the upload method in the version-pinned S3 source.
if s.now == nil {
s.now = time.Now().UTC
}
key = TimestampedPath(key, s.now())
The call to time.Now() happens during the assignment. Accessing its UTC method produces a method value whose receiver is that particular time value. Calling the stored function later converts that retained receiver to UTC again. It does not call time.Now() again. This is an inference from the exact expression in the release source, and it follows the distinction between a function value and a bound method value.
The Go 1.22 language specification defines this behaviour. Evaluating a method value saves its receiver for later calls. The expression therefore has valid Go syntax while retaining the wrong lifetime for a clock intended to advance between uploads.
The result is that a client's first upload initializes its clock with one captured instant. Later uploads through that client use the same instant to construct their keys. With an unchanged configured path, the name remains the same even if the database has changed and another backup interval has elapsed. Timestamping the filename therefore does not establish distinct names for successive uploads within that client's lifetime.
The implementation's test injects a function returning a fixed time and verifies one upload's key. That checks formatting and propagation of the timestamp option. It does not exercise two uploads using the default clock implementation with time advancing between them. Consequently, the test's expected filename can be correct while the repeated-upload behaviour still fails the intended retention goal.
A clock callback that obtains the time when called has different semantics. The important question is when the current time is read. Capturing a value while constructing a callback and reading a value inside the callback are separate operations, even when both expressions mention the same time API. The released code performs the former.
Naming does not define backup scheduling
The automatic uploader runs on a ticker and asks its data provider for the last modification index. If that index has not advanced beyond the last successful upload, it skips the upload. Enabling timestamps does not make every interval produce another object when the database has not changed. Change detection and naming are separate parts of the implementation.
The uploader records the new index after a successful upload. It also exposes counts for successful, failed and skipped uploads, together with byte counts. Those values describe whether the upload operation ran and completed. They do not prove that the destination contains a sequence of distinct objects. The naming error can coexist with successful uploads and an advancing last-upload index.
There is another boundary around restore selection. The S3 client's download method fetches its configured key directly. The timestamp option applies to upload naming, while the download method does not scan a prefix and choose a recovery point. Retaining multiple objects would still require selecting the specific object intended for restoration. Timestamped naming alone does not supply that selection procedure.
I regard the feature's goal as useful, but the implementation is what determines the available recovery history. In this release, the opt-in configuration reaches the upload client and the filename helper produces the documented shape. The default clock callback then reuses a captured time, so successive uploads through one client can continue targeting the same timestamped object. Operators relying on retained recovery points need to account for that behaviour when interpreting the release's point-in-time backup claim.