R3.05.2 · Blocking vs polling — dive 2
Blocking pop.
A blocking pop parks the connection until an element arrives or the timeout elapses — no poll interval, no wasted round-trips. The cost is the connection, which a dedicated lane keeps off the work.
Grounding: the source's Blocking Variant · the real Elixir
EchoMQ.Consumer.park/1 parks on BLPOP emq:{queue}:wake <beat> on a dedicated
connector lane (the moduledoc: "a dedicated connector — blocking verbs get their own lane"),
echo/apps/echo_mq.
Static verdicts — parked → 1 round trip outstanding, zero traffic until a wake · returned → the wake closes the BLPOP and the drain begins.
The blocking primitives
reliable-queue.md.txt · Blocking VariantThe source's reliable-queue loop replaces its pop/sleep spin with a blocking move:
BLMOVE work_queue processing:worker1 RIGHT LEFT 30
BLMOVE atomically moves an element from the work list to the processing list, parking the
connection for up to thirty seconds if the work list is empty. BRPOPLPUSH is the older form of
the same move. BLPOP is the simplest blocking pop: it removes and returns the first element of a
list, parking until one is pushed or the timeout fires. All three share the property that matters: no
poll interval and no empty round-trips — the call returns the instant there is work, or once
on the timeout.
- BLMOVE
- The modern blocking move; atomically transfers an element work → processing, parks if empty.
- BRPOPLPUSH
- The older blocking move
BLMOVEreplaces; same transfer, fixed directions. - BLPOP
- The simplest blocking pop; removes and returns the first element, or parks until one is pushed.
A blocking primitive parks the connection until an element arrives or the timeout elapses — no interval, no empty round-trips, by construction.
The cost is the connection
one shared lane vs a dedicated oneA blocking call occupies its connection for the whole park. A command sent on the same connection waits behind the block, so a worker that parks on its only connection cannot also fetch or acknowledge. The discipline is a dedicated connection for the block: the blocking connection parks on the wake key and does nothing else; the command connection drains the ring, runs the claim, and settles the job. The two never contend, so a long park never stalls a fetch.
Choose a connection layout below; the readout runs a settle command while a block is parked and reports whether it stalls.
Static verdicts — shared → the settle waits behind the park · dedicated → the settle runs at once on the free lane.
A blocking call ties up its connection for the park; a dedicated blocking lane keeps the drain and settle lane free, so a long park never stalls the work.
The park, in EchoMQ
consumer.ex · park/1The consumer's loop is reap → promote → drain → park, repeating. The park is one blocking call with the beat as its timeout, on the consumer's dedicated connector lane:
# EchoMQ.Consumer.park/1 — park on the wake key for one beat, then loop (real, Elixir) defp park(s) do secs = :erlang.float_to_binary(s.beat_ms / 1000, decimals: 3) wake = Keyspace.queue_key(s.queue, "wake") # emq:{queue}:wake _ = Connector.command(s.conn, ["BLPOP", wake, secs], s.beat_ms + 2_000) :ok end
The BLPOP returns one of two ways: a wake token was pushed (work was admitted — drain
it), or the beat elapsed (run the reap/promote pump, then park again). Either way the connection spent the
idle time parked, not polling. The beat (:beat_ms, default 1000) is the timeout, so the loop
always re-runs its pump within one beat and is never parked forever; the lease the claim holds is bounded by
:lease_ms (default 30 000).
Notes on ValkeyBLPOP is the blocking variant of
LPOP: when the list is empty it parks the client until another client pushes an element, or the
timeout elapses; the engine serves exactly one of several blocked clients per push, in park order —
valkey.io/commands/blpop.
The dedicated connector lane
The connector pool, the blocking lane discipline (Appendix B), and the full reap-promote-drain-park loop are the /echomq/queue Queue pillar — "door, not depth."
A blocking pop with a timeout parks the connection until work arrives — no interval, no wasted round-trips — at the cost of a tied-up connection, kept off the working one by a dedicated lane.
EchoMQ.Consumer.park/1 runs BLPOP emq:{queue}:wake <beat> on a dedicated
connector lane; the command lane stays free to claim and settle. The beat bounds the park; a wake returns
it early.
EchoMQ parks on BLPOP with the beat as the timeout on a dedicated lane; the next dive
walks the wake handshake that rings the doorbell.
Recap — park, do not spin
next → R3.05.3A blocking pop removes the interval and the empty round-trips: the connection parks until work arrives or the
timeout fires. The cost is the tied-up connection, kept off the drain lane by a dedicated blocking lane.
EchoMQ parks on BLPOP emq:{queue}:wake with the beat as the timeout. The next dive shows the
producer side that rings the wake key.
References
sources · relatedSources
- Valkey — BLPOP — block until an element is pushed to a list; the park EchoMQ's consumer makes.
- Redis — BLMOVE — the modern blocking reliable-queue pop the source uses.
- Redis — BRPOPLPUSH — the older blocking reliable-queue pop, the move BLMOVE replaces.
- Redis — Documentation — blocking commands and why each blocking call needs its own connection.
Related in this course
- R3.05 · Blocking vs pollingthe module hub
- R3.05.1 · The busy-poll costthe previous dive: the cost this removes
- R3.05.3 · The wake-up doorbellthe next dive: ringing the wake key
- R3 · Reliable Queuesthe chapter
- /echomq/queuethe Queue pillar: the dedicated connector lane in depth