Getting started
This page runs start to finish against a real server. Everything after “Run the server” uses the
tephra-client crate.
Run the server
Section titled “Run the server”The server is a synchronous, thread-per-connection TCP server. Start it from a checkout of the workspace:
cargo run -p tephra-serverIt binds 127.0.0.1:9000 and writes its log and index segments under tephra-data in the
working directory. Both are configurable by flag, environment variable, or a TOML file: see
Operations for the full surface. The defaults, including the 256 MiB segment
size, are the server’s, not the library’s.
Add the client
Section titled “Add the client”In the project that will talk to the server:
cargo add tephra-clientConnect
Section titled “Connect”Client::connect takes anything that resolves to a socket address and opens one TCP connection.
A Client is not shared between threads: give each thread its own.
let mut client = Client::connect(addr)?;Append
Section titled “Append”An event is a type, a set of tags, and an opaque payload. append takes an iterator of events
and an optional condition; here the condition is None, so the write is unconditional. It
returns the position range the batch landed at.
let event = Event::new( "CourseOpened", &["course:c1"], br#"{"course":"c1","seats":30}"#.to_vec(),)?;let result = client.append([event], None)?;println!("recorded positions {} to {}", result.first, result.last);The client’s Event is a friendly owned type (built from &str, &[&str], and bytes). The
engine has a second, packed Event used only when you embed the engine directly; the two do not
interconvert, and the client type is the one you want here. See Embedded for the
other one.
read and read_all take a query and an after position. Position::ZERO means “from the
start of the log”. Results come back in ascending position order.
let query = Query::item(QueryItem::with_tags(Tags::new([Tag::new("course:c1")?])?));let (events, _watermark) = client.read_all(query, Position::ZERO)?;for seq in &events { println!("{} {}", seq.position(), seq.event().event_type());}Because the writer publishes each commit before the append reply returns, a client sees its own writes immediately: the read above returns the event the append above just recorded.
Subscribe
Section titled “Subscribe”A subscription catches up on history and then tails the live edge, as one loop. It yields events
in order, then a CaughtUp marker each time it reaches the live edge. The marker is where a read
model can report that it is current.
// Subscribe from the start: catch up on history, then a CaughtUp marker at the live edge.let query = Query::item(QueryItem::with_tags(Tags::new([Tag::new("course:c1")?])?));let (mut stream, cancel) = client.subscribe(query, Position::ZERO)?;for item in &mut stream { match item? { SubEvent::Event(seq) => { println!("live {} {}", seq.position(), seq.event().event_type()); } // Reached the live edge. A long-running consumer keeps going; this example stops. SubEvent::CaughtUp(_) => break, }}cancel.cancel();A subscription does not end on its own: drop the stream, or call cancel on the paired
SubscribeCancel, to close it.
Core concepts is the precise definition of events, tags, positions, and query semantics. Guides builds a real decision model and a read model on top of what is here.