jonnify·redis /redis-patterns/queues/blocking-vs-polling/blocking-pop

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.

The blocking connection's life — idle, then a park, then work. Hover or select a segment to read what the connection is doing.
blocking connection · one cycle idle parked (BLPOP wake) drain wake at the park's start of work
readout · Select a segment. The connection is idle, then parked on the wake key, then draining the returned job.

Static verdicts — parked → 1 round trip outstanding, zero traffic until a wake · returned → the wake closes the BLPOP and the drain begins.

§1

The blocking primitives

reliable-queue.md.txt · Blocking Variant

The 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 BLMOVE replaces; 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.

§2

The cost is the connection

one shared lane vs a dedicated one

A 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.

One shared connection, or two lanes. Select a layout; the readout settles a job while a block is parked.

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.

§3

The park, in EchoMQ

consumer.ex · park/1

The 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.

door · the consumer loop in depth

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."

the pattern

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.

its EchoMQ application

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.

§4

Recap — park, do not spin

next → R3.05.3

A 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.

§5

References

sources · related

Sources

Related in this course