Skip to main content

Module time_store

Module time_store 

Source
Expand description

Saving/loading timestamps to disk

Storing timestamps on disk is not so straightforward. We need to use wall clock time in order to survive restarts. But wall clocks can be wrong, so we need at least to apply some sanity checks.

This module encapsulates those checks, and some error handling choices. It allows Instants to be used while the system is running, with bespoke types for loading/saving. See Loading::load_future for the load/save guarantees provided.

The initial entrypoints are Storing::start and Loading::start.

Granularity is 1 second and the precise rounding behaviour is not specified.

ยงData model

To mitigate clock skew, we store the wall clock time at which each timestamp was saved to disk (Reference) and the offset from now to that timestamp (FutureTimestamp).

The same storage time can be used for multiple timestamps that are stored together.

ยงExample

use serde::{Serialize, Deserialize};
use tor_rtcompat::{PreferredRuntime, SleepProvider as _};
use web_time_compat::{Duration, Instant};

use crate::time_store;

let runtime = PreferredRuntime::create().unwrap();

#[derive(Serialize, Deserialize, Debug)]
struct Stored {
    time_ref: time_store::Reference,
    t0: time_store::FutureTimestamp,
}

let t0: Instant = runtime.now() + Duration::from_secs(60);

let storing = time_store::Storing::start(&runtime);
let data = Stored {
    time_ref: storing.store_ref(),
    t0: storing.store_future(t0),
};

let json = serde_json::to_string(&data).unwrap();

// later:

let data: Stored = serde_json::from_str(&json).unwrap();
let loading = time_store::Loading::start(&runtime, data.time_ref);
let t0: Instant = loading.load_future(data.t0);

assert!(t0 - runtime.now() <= Duration::from_secs(60));

ยงTime arithmetic overflows and stupid system time settings

Arithmetic is done with signed 64-bit numbers of seconds. So overflow cannot occur unless the clock is completely ludicrous. If the clock is ludicrous, time calculations are going to be a mess. We treat this as clock skew, using saturating arithmetic, rather than returning errors. Reasonable operation will resume when the clock becomes sane.

Macrosยง

derive_deftly_template_RawConversions ๐Ÿ”’
Define as_raw and from_raw methods (for a struct with a single field)
derive_deftly_template_SerdeStringOrTransparent ๐Ÿ”’
Define Serialize and Deserialize via string rep or transparently, depending

Structsยง

DeserializeFutureTimestampStringVisitor ๐Ÿ”’
Visitor for deserializing from a string
DeserializeReferenceStringVisitor ๐Ÿ”’
Visitor for deserializing from a string
FutureTimestamp
Representation of an absolute time, in the future, suitable for storing to disk
Loading
Context for loading Instants from disk
Now ๐Ÿ”’
The two notions of the current time, for internal use
ParseError
Error parsing a timestamp or reference
Reference
On-disk representation of a reference time, used as context for stored timestamps
Storing
Context for storing Instants to disk

Functionsยง

system_time_max ๐Ÿ”’
Maximum value of SystemTime
system_time_min ๐Ÿ”’
Minimum value of SystemTime
system_time_to_time_t ๐Ÿ”’
Convert a SystemTime to an i64 time_t
time_t_to_system_time ๐Ÿ”’
Convert a SystemTime to an i64 time_t