Transport¶
Scope: the module overview already introduced Aeron Transport as "a very fast pipe — a reliable, ordered stream over UDP/IPC" and contrasted it with Cluster. This page goes one layer deeper on how Transport gets that speed and reliability without TCP.
TL;DR — in 30 seconds:
- The Media Driver runs as a separate process from your application; the two talk through shared memory, not a socket call, so offering a message doesn't need a syscall on the hot path.
- A channel (URI) + stream ID names a stream — pick IPC for same-machine delivery, since it skips the kernel network stack entirely, unlike loopback UDP.
- Aeron gets reliability without TCP by having the subscriber NAK just the missing range from a term
buffer;
offer()never blocks — a full subscriber makes it return "not accepted" instead of stalling the publisher.
1. The media driver: a separate process, not a library call¶
Aeron splits into two halves that run as separate processes: your application, which holds Publication and Subscription handles, and the Media Driver, which does the actual sending and receiving. Your application never touches a socket directly.
The two halves talk to each other through shared memory (memory-mapped files), not a normal inter-process call. That matters because it means offering a message doesn't require a syscall or a kernel context switch on the hot path — the application thread writes into a shared buffer, and the Media Driver (running its own thread, polling that buffer) picks up the write and pushes it onto the wire. This is the same mechanical-sympathy trade-off as the memory-hierarchy page's locality argument, applied to inter-process communication instead of cache lines: avoid the expensive path (kernel transition) whenever a cheaper one (shared memory) will do.
flowchart LR
subgraph APP["Publisher application"]
P["Publication.offer(message)"]
end
subgraph MDP["Media Driver — publisher side"]
MD1["reads shared memory,<br/>writes to term buffer,<br/>sends over the wire"]
end
subgraph MDS["Media Driver — subscriber side"]
MD2["receives, writes to<br/>term buffer, exposes<br/>via shared memory"]
end
subgraph SUB["Subscriber application"]
S["Subscription.poll(handler)"]
end
P -->|shared memory| MD1
MD1 -->|UDP or IPC| MD2
MD2 -->|shared memory| S
2. Publications and subscriptions: naming a stream¶
A Publication is the handle your code uses to offer messages onto a stream. A Subscription is the
matching handle on the receiving side. The two are wired together by a channel (a URI describing the
transport — a UDP endpoint, or aeron:ipc for same-machine delivery) plus a stream ID (an integer
that lets several logical streams share one channel).
| Channel media | When you'd pick it |
|---|---|
| UDP unicast | Publisher and subscriber(s) on different machines, point-to-point or fan-out |
| UDP multicast | Many subscribers on the same network segment need the same stream, without per-subscriber copies |
IPC (aeron:ipc) |
Publisher and subscriber are processes on the same machine — skips the network stack entirely and hands data across shared memory only |
Choosing IPC over loopback UDP for same-host communication is itself a mechanical-sympathy decision: loopback still walks the kernel's network stack, while IPC does not.
3. Reliable delivery without TCP: term buffers and NAKs¶
Aeron gets reliability over UDP — a protocol with no built-in delivery guarantee — without TCP's approach. The core difference is what gets retransmitted when something is lost.
Publications write into fixed-size term buffers; each piece of data has a well-defined position within the stream. A subscriber tracks the positions it has received, so a gap (a piece of the stream that never arrived) is easy to detect. Where TCP has the sender wait on a moving acknowledgment window, Aeron flips the trigger: a gap makes the subscriber send a NAK (negative acknowledgment) naming the missing range, and the sender retransmits only that range from its term buffer.
Note: the practical effect is that loss on a healthy stream is cheap to recover from — one small retransmission for the missing piece — rather than resetting a whole congestion window the way TCP would after a loss. Full wire format: Aeron Transport Protocol Specification.
4. Backpressure: what happens when a subscriber can't keep up¶
offer() never blocks the publisher waiting for a slow subscriber. If a subscriber isn't consuming fast
enough, offer() returns a status telling the caller it was not accepted this time — the application
is expected to check that result and decide whether to retry, drop, or apply its own backoff. This is a
deliberate design choice: a Publication that could silently block would let one slow subscriber stall a
fast publisher, which defeats the reason you reached for Aeron Transport in the first place.
5. How this connects¶
- Cluster builds on exactly this layer. The commit path the module overview describes — leader appends, replicates, quorum acks — is Raft consensus running on top of the Publications/Subscriptions and reliable delivery this page describes. Transport gives you a fast, reliable pipe; Cluster adds agreement about what state that pipe has delivered.
- The shared-memory hand-off is mechanical sympathy in practice. The module recommends Mechanical Sympathy (90) before or alongside Aeron for exactly this reason — the media-driver split, the shared-memory buffers, and the avoid-a-syscall design all come from the same "know what the hardware and kernel actually cost you" instinct that module teaches in general.
- Backpressure is a single-writer-adjacent idea. A term buffer has exactly one writer (the Media Driver on the publish side), which is why Aeron can track position and detect gaps cheaply — the same single-writer reasoning the concurrency page uses to avoid contention shows up here as a design choice in the transport itself.
Common gotchas
- Assuming Aeron's application code talks to the network directly. Fix: it never does — the Media Driver is a separate process, and the application only writes into a shared-memory buffer.
- Choosing loopback UDP for same-host communication. Fix: use IPC (
aeron:ipc) instead — loopback still walks the kernel's network stack, while IPC skips it entirely via shared memory. - Assuming
offer()blocks until a slow subscriber catches up. Fix: it never blocks — it returns a "not accepted" status, and the application decides whether to retry, drop, or back off. - Assuming Aeron's reliability works like TCP's. Fix: on loss, the subscriber NAKs only the missing range from the sender's term buffer — the sender doesn't reset a whole congestion window the way TCP would after a loss.
Check yourself — why can offering a message avoid a syscall on the hot path, even though the message eventually leaves the machine over UDP?
Because the application only writes into a shared-memory buffer — the Media Driver, running as its own process/thread, is the one that reads the buffer and performs the actual send, so the application thread itself never makes a syscall.
Check yourself — two processes on the same machine want to exchange messages. Why is IPC a mechanical-sympathy win over loopback UDP?
Loopback UDP still walks the kernel's network stack even though the data never leaves the machine; IPC hands the data across shared memory only, skipping that stack entirely.
Check yourself — a subscriber is falling behind a fast publisher. What does offer() do, and why is that the right design?
It returns a "not accepted" status rather than blocking — a Publication that could silently block would let one slow subscriber stall a fast publisher, defeating the reason you reached for Aeron Transport.
Check yourself — a subscriber detects a gap in the stream. What does it do, and how does that differ from TCP's approach?
It sends a NAK naming the missing range, and the sender retransmits only that range from its term buffer — cheaper than TCP, which resets its whole congestion window after a loss.
You can defend this when you can explain why the Media Driver runs as a separate process instead of
being a library call inside your application; say what a channel + stream ID identifies and why IPC beats
loopback UDP on the same host; explain how a NAK-based retransmission differs from TCP's approach to loss;
and say why offer() returning a "not accepted" status instead of blocking is the right shape for a
publisher that must never stall on a slow subscriber.