A distributed timestamp oracle for Rust — highly available and fault-tolerant, issuing strictly monotonic integer timestamps over gRPC, replicated via openraft or OmniPaxos (or your own log) with pluggable consensus.
- 🔢 Strictly monotonic — every issued timestamp is strictly greater than every previously issued one; no duplicates and no regression, ever. The packed integer space is not dense (logical resets when
physical_msadvances, so some integer values are unused), but the issued sequence is total-ordered and unique. - 🛡️ Crash-safe — window state is fsync'd before any timestamp in that window is handed out, so a restart never rewinds.
- 🔌 Pluggable consensus, openraft + OmniPaxos included —
tsoracle-driver-openraftandtsoracle-driver-paxosship production-ready replicated drivers; implement one trait (ConsensusDriver) to back tsoracle with raft-rs, etcd, or your own replicated log instead. Seedocs/consensus-integration.mdfor picking between drivers. - 📦 gRPC client included —
tsoracle-clienthandles leader discovery, request coalescing, and reconnection for you. - 📈 Operational metrics — enable the
metricsfeature ontsoracle-serverto emit allocator, leader, and request metrics through themetricsfacade. - 🧪 Hardened — coverage-guided fuzzing on the postcard decoders, failpoint-driven crash tests, and a stress harness covering single-process and multi-process raft topologies.
- 🧩 Embeddable — host the server inside your own binary with a few lines of Rust, or run the standalone CLI.
Install the standalone binary and start the server:
cargo install tsoracle
tsoracle serveThe server listens on 127.0.0.1:50551 and persists state under ./tsoracle-data/.
Then call it from Rust:
use tsoracle_client::Client;
let client = Client::connect(vec!["http://127.0.0.1:50551".into()]).await?;
let ts = client.get_ts().await?; // a strictly increasing Timestamp
let batch = client.get_ts_batch(64).await?; // amortize RPC cost across many IDsEmbed the server in your own binary instead of running the CLI:
use tsoracle_server::Server;
use tsoracle_driver_file::FileDriver;
let driver = FileDriver::open_or_init("./tsoracle-data")?;
let server = Server::builder()
.consensus_driver(driver)
.build()?;
server
.serve_with_shutdown("127.0.0.1:50551".parse()?, async {
let _ = tokio::signal::ctrl_c().await;
})
.await?;A complete, runnable version lives in examples/embedded-server. Want to mount tsoracle inside an existing tonic server? See Server::into_router.
A timestamp oracle is a service that hands out strictly increasing integer IDs which order events across a distributed system. You reach for one when:
- You're building a database with snapshot isolation or MVCC (Spanner, CockroachDB, FoundationDB all use a TSO internally).
- You need to merge change-data from many shards into one globally ordered stream.
- You want audit logs with a real "happens-before" relation across machines.
- Per-host clocks aren't monotonic or accurate enough, and database sequences don't scale to your workload.
tsoracle is a small, embeddable Rust implementation. The consensus layer is left as a trait, so you can run it single-node behind one fsync (the default), or wire it into a replicated log of your choice.
- DeepWiki — prose documentation covering the window allocator, the
ConsensusDrivercontract, and operational topics (fsync cost, leader handoff, deployment topologies). - docs.rs/tsoracle-server — generated API reference.
- examples/embedded-server — embed
tsoracle-serverwith the file driver in your own binary, with graceful shutdown. - examples/failover-demo — pedagogy: watch the failover fence keep timestamps strictly monotonic across simulated leadership changes, in-process, no openraft.
- examples/openraft-standalone — HA: three-node multi-process cluster on a dedicated openraft, wired through
tsoracle-driver-openraftwith a tonic peer transport and follower-redirect viaLeaderHint. - examples/openraft-piggyback — HA: in-process three-node demo of the envelope pattern, where your service's existing openraft carries both your
AppDataand the tsoracleHighWaterCommandon a single log, with one snapshot covering both halves. - examples/paxos-standalone — HA: three-node multi-process cluster on a dedicated OmniPaxos, wired through
tsoracle-driver-paxoswith a tonic peer transport and follower-redirect viaLeaderHint. - examples/paxos-piggyback — HA: in-process three-node demo of the envelope pattern on OmniPaxos, where your service's existing paxos log carries both your
AppDataand the tsoracleHighWaterCommand, with one snapshot covering both halves. - examples/paxos-embedded — HA: single-process 3-node OmniPaxos cluster (OmniPaxos rejects single-node configs, so even "embedded" runs all three nodes in-memory). The closest paxos equivalent to
embedded-server. - examples/metrics-prometheus — embed
tsoracle-serverwithmetrics-exporter-prometheusinstalled before the server starts, exposing/metricson a separate port for Prometheus to scrape; swap recorders with a one-line change.
Each example is its own crate. Build with cargo run -p example-<name>.
Issues and pull requests are welcome. See CONTRIBUTING.md for local setup, the checks CI will run on your PR, commit message conventions, and the release process.
Made with contrib.rocks.
Licensed under Apache-2.0.