Module C4.12 · lab
Lab: build a branded CHAMP store
The chapter built the parts in isolation. The lab wires them into one working queue you can keep.
This is the chapter's hands-on lab. The persistent Map and Set
of C4.04–C4.06 become a queue's registry; the branded JOB id of C4.07–C4.08 is the key,
time-ordered so it serves itself in mint order; the MailboxProcessor of C4.09 is the single
writer. Assembled, they are a mini echo_fs — the F# port of the EchoMQ bus. Three dives
build it: the data model the queue needs, the actor that owns it, and the
assembled queue run end to end. Every F# snippet on these pages was verified with
dotnet fsi.
What the lab assembles
Nothing in the lab is new. Each piece is an earlier lesson, named once and wired in. The registry is a
persistent map keyed by branded id (C4.04–C4.06); the key is a branded JOB Snowflake
that is time-ordered and dates itself (C4.07–C4.08); the writer is an F# MailboxProcessor
(C4.09). The lab's job is to put them together into a queue with three operations — admit a job, claim the
oldest, complete it — and to run it.
The result mirrors the bus. The echo_fs solution at the repo root is the F# port of
the Elixir echo/apps/echo_mq bus; its in-memory core — a Store
and a single-writer Broker — is built and tested. The lab rebuilds that core in
miniature. The one part genuinely still ahead is the Valkey transport, which the port names
EchoMQ.Connector and marks coming soon.
The lifecycle · step a job through the queue
Step the queue. Each press applies one pure transition to a fixed three-job dataset: an
enqueue writes a row and adds the id to Pending; a
claim pops the oldest pending id (mint order) into flight; a
complete retires it. The readout names the step, its verdict, and the counts.
Three operations, one writer, one persistent store. That is the whole queue — and every piece is a lesson you have already met.
The three dives
Read them in order; together they build the queue. Select one for detail, then open it.
The three dives · select one
The data the queue needs: a Job record and a QueueState — a Rows map keyed by branded id plus a Pending set of ids — with the pure transitions enqueue, claim, and complete. It is EchoMQ.Store and EchoMQ.Job in miniature.
Open this dive · or pick another above
The model
A Job record and a QueueState — a Rows map plus a Pending set — with the pure enqueue / claim / complete transitions.
- the datathe two types the queue is built on
The actor
Wrap the model in a MailboxProcessor — F#'s actor — that owns the state and serializes every transition through one mailbox: the single writer.
- the writerone mailbox, one writer, no lock
Ship it
The assembled queue, run end to end: enqueue a few jobs, claim the oldest first, complete it. The lab is a mini echo_fs — built and tested.
- the queuethe capstone run, enqueue to complete
EchoMQ in F#
Status — echo_fs is an in-memory model. The EchoMQ protocol — the Lua transitions executing on a Valkey wire — is not implemented (there is no Lua and no Valkey client in the code); that is the coming-soon transport (EchoMQ.Connector). This block shows how the model uses the structure, not a running bus.
The lab is not a teaching sketch alongside the bus — it is the bus's core, in miniature.
runs today echo_fs/EchoMQ/Store.fs · Broker.fs · Job.fs · README.md
The echo_fs solution ships an in-memory EchoMQ.Store (a
QueueState of a Rows map plus a Pending
set) and a single-writer EchoMQ.Broker (a MailboxProcessor) —
both built and under test. The lab rebuilds exactly this: the same two types, the same
enqueue / claim / complete
transitions, the same mailbox-serialized writer. By the end of ship it you have, in miniature, what
echo_fs is today.
coming soon The echo_fs README states the in-memory
Store "stands in for the branded CHAMP / Valkey rows behind the same
enqueue/claim/complete/… surface, so the durable backing swaps in with no caller
change." The Valkey transport — EchoMQ.Connector — is the one part the README marks
coming soon; it is what turns the lab's in-memory queue into the production bus.
The store is a Map / Set today, not a CHAMP and not
Valkey. The branded CHAMP (C4.06) and the Valkey wire are the production forms the same Store
surface admits later — taught as the target, not claimed as built.
References
Sources
- Microsoft — F# documentation — records, discriminated unions, and the
MailboxProcessor. - FSharp.Core API reference —
Map,Set, andMailboxProcessor. - The F# Software Foundation — F# on the .NET runtime.
- Microsoft — .NET documentation — the thread pool the agent's loop runs on.
Related in this course
- Elixir — F4.12 · Lab: build a branded CHAMP store — the parity sibling; the same lab, assembled in Elixir.
- C4.09 — Branded CHAMP maps & MailboxProcessor — the actor module the lab assembles.
- C4 — Algorithms & Data Structures — the chapter this lab closes.
- C3 — F# for Elixir developers — the
MailboxProcessorbeside the BEAM process.