
About this workshop
You are comfortable with Rust: ownership, traits, Arc, and what Send means. Async is the part you are less sure of, whether you have written an async fn and left it there or watched a Tokio service in production do something other than what you expected. This workshop answers the async questions that come after the syntax: who owns your state, what happens when a future is dropped halfway through, and what your server does when it is asked for more than it can deliver.
You will turn the example minidb, a small in-memory key-value store, into a networked service. By the end of the workshop, you will have added a length-prefixed protocol over TCP, handling several requests in flight per connection, an architecture where one task owns the data and everything else asks it, durability pluggable behind a trait, and tests that run the whole server on a simulated network. With that setup, a slow client cannot starve a fast one, an overloaded server says so instead of falling over, and a restart picks up where the last one left off.
The workshop is designed for developers who are comfortable writing Rust and want to stop guessing when it comes to async. If you are new to Rust instead, start with Learn Rust, starting from scratch.
Bookable for teams
Syllabus
The Tokio runtime
We will cover what Tokio adds on top of the language, why a future does nothing until something polls it, and why.awaitis a suspension point rather than a call, along with as much ofFuture,poll, andPinas reasoning about the runtime demands; a later chapter returns to them at the poll level.Tasks
tokio::spawnturns a future into a task, which is not a thread and does not behave like one. We will cover theSend + 'staticrequirement, running work concurrently withjoin!andJoinSet, and moving work that cannot yield ontospawn_blockingso that it does not occupy the runtime's worker threads.Building the server
You will turnminidbinto a TCP server that speaks a line protocol, covering the accept loop, framing withBufReaderandlines(), and one task per connection. We will also cover which errors should end a single connection and which should not reach the accept loop.Who owns the state
Every connection is a task, and each of them needs access to the same store. You will build both answers,Arc<Mutex<Store>>and an actor task that owns the data outright, and we will cover which mutex to use and where its guard may not be held. The section ends with a criterion benchmark comparing the two approaches under contention.Cancellation
In async Rust, cancelling work means dropping a future, which can happen at any suspension point. We will covertimeout,select!, andCancellationToken, the state a dropped future leaves behind, and cancel safety: whynext_linecan be dropped safely and a hand-written read loop cannot.Backpressure
An unbounded queue grows until the process runs out of memory. We will cover boundedmpscchannels, the difference between waiting, refusing, and dropping work when a queue is full, and limiting concurrent connections with aSemaphore, where the point at which the permit is acquired determines what the limit protects.Shutdown and supervision
Without a shutdown path, a deploy terminates requests that are still in flight. We will cover graceful shutdown withCancellationTokenandTaskTracker, the ordering that drains connections before the store task, and how to decide what should happen when a task fails without being asked to stop.Testing async code
We will cover Tokio's paused clock, which makes a thirty second timeout testable in microseconds,tokio::io::duplexfor exercising a connection handler without opening a socket, and asserting on properties rather than on scheduling order. You will also instrument the server withtracingand test the resulting spans as structured data.Durability across restarts
You will add a write-ahead log, recording every mutating request before it is applied, batching the syncs into a group commit so that a busy server does not make one trip to the disk per request, and replaying the log on startup so that a restart preserves the data.AsyncRead and AsyncWrite
We will go underneath Tokio's IO, to the traits it is built on. We will cover thepoll_readandpoll_writecontract as well asReadBuf, and you will write an IO wrapper of your own that counts every byte passing through the server, dropping into the connection handler without changing a signature.Codecs and framing
You will replace the hand-rolled line loop with a length-prefixed frame, implementingDecoderandEncoderand driving them withFramed. The same codec then carries the write-ahead log, and a restriction the old framing forced on values disappears. This also closes the loop on cancel safety: a partial frame lives in the codec's buffer, not on a dropped future's stack.Streams, sinks, and pipelining
We will cover theStreamandSinktraits and splitting a connection into independently owned halves. You will then let one connection have several requests in flight at once, choosing the in-flight bound and keeping the responses in order deliberately rather than by accident.Async functions in traits
You will make the store's durability pluggable behind a trait with async methods, and meet the compile error that madeasync fnin traits famous: the spawned actor demandsSendfutures the trait does not promise. We will coverimpl Futurein return position as the fix, and whatdyndoes and does not allow.Diagnosing a stuck runtime
A blocked worker thread does not announce itself. You will find one withtokio-console, and we will cover the runtime's metrics as well as the bridges between sync and async code,block_in_placeandHandle::block_on, and the ways they go wrong quietly.Testing against a hostile network
You will run the whole server on a simulated network withturmoil: injected latency, dropped connections, and partitions, all deterministic and all inside a unit test, using the same abstractions that made the store pluggable.
Who we are
Mainmatter has been active in the Rust ecosystem for years, creating the 100 Exercises to Learn Rust course, the C to Rust Migration Book organizing EuroRust, and contributing to open source.
We help teams adopt and build on Rust with confidence via trainings and workshops, hands-on team reinforcement, codebase assessments, and more.
We have trained developers at
Bookable for teams

