Can we avoid sharing state by moving data instead?

Queues, Channels & Message Passing

Producer/consumer as the flagship pattern, bounded versus unbounded queues, backpressure as the conversation between a fast producer and a slow consumer, channels, message passing and the actor model — coordination without shared memory.

Producer / Consumer▶ lab

The flagship pattern of the domain: one side creates work, a queue holds it, another side runs it. Everything interesting is in the queue — how big it is, what happens when it is full or empty, and who is allowed to notice that the producer has stopped.

Q · When one part of a system creates work faster than another can run it, what exactly must the two sides agree on?

Bounded vs Unbounded Queues▶ lab

An unbounded queue is not a queue without backpressure. It is a queue whose backpressure mechanism is the OOM killer, whose signal is a process restart, and whose latency is unbounded long before memory runs out.

Q · If the producer is faster than the consumer and the queue has no limit, what actually stops it — and how does that failure present at 3 a.m.?

Backpressure▶ lab

The conversation a slow component has to have with a fast one. Without it the chain is: producer rate exceeds consumer rate, queue grows, memory grows, latency rises, something dies. Backpressure is the design question of how the slow side says "not so fast" in a way the fast side is forced to hear.

Q · When a downstream stage cannot keep up, how does that fact travel back to whatever is producing the work — and what does the producer do about it?

Channels

A typed conduit with send, receive and — the part people get wrong — close. Capacity decides whether a send is a rendezvous or a buffered handoff; closing is how a consumer learns that no more data is coming, and getting it wrong is how pipelines hang on shutdown.

Q · What does a channel guarantee that a shared queue does not, and how does a receiver learn that the sender is finished?

Message Passing

Instead of synchronizing access to shared state, transfer the data and let exactly one party own it at a time. You give up zero-copy sharing and a single global ordering; you get a system with no shared mutable state to protect, which is a different and much smaller problem.

Q · Can we make the synchronization unnecessary by moving the data instead of protecting it — and what does that trade cost?

The Actor Model

Private state, a mailbox, and strictly sequential message processing. Inside an actor there is no concurrency at all, which is why there are no locks — and the costs are message overhead, ordering that is weaker than it looks, and the fact that remote actors turn this into a distributed-systems problem.

Q · What do you get by making a piece of state single-threaded by construction, and what does the mailbox cost you?

Concurrent Queues

Before you pick a queue implementation, write down the four requirements: thread safety under how many producers and consumers, what ordering you actually need, whether operations block, and what the capacity is. Most queue bugs are a requirement nobody stated, not an implementation that was wrong.

Q · What are the actual requirements on the queue between these two stages, and which of them is the one nobody wrote down?

Draining a Pipeline

Stop accepting, finish what is in flight, signal the consumers, wait for the workers, then exit. Every step is a decision — and what happens to the work still sitting in the queue is a policy you choose, not an accident you discover during a deploy.

Q · When this process is told to stop, what happens to the work in flight, the work in the queue, and the workers blocked waiting for more?