Push vs pull
0-day streakReview

Your progress

Level 1

0 XP100 XP to level 2
0day streak
0best streak
0topics started
0cards reviewed
    Review cards

    Progress is stored only in this browser. No account is required.

    Layer 4 · Full breakdown · 18 min · 5 figures

    Push vs pull architecture

    Push sends data the moment it exists; pull lets the consumer ask when it is ready. The trade-off at every layer, from polling and WebSockets to Kafka, feeds, Prometheus and webhooks, with the arithmetic and the hybrids real systems use.

    In a push architecture the side that has the data starts the transfer: a server sends an update the moment it exists. In a pull architecture the side that needs the data starts it: a client or consumer asks when it is ready. The real difference is who controls timing and rate, and the same choice appears at every layer, from a live score on your phone to Kafka consumers, news feeds, Prometheus and payment webhooks.

    Picture it first: the newspaper and the news stall

    Every morning a hawker drops the newspaper at your door. You never ask; it arrives as soon as it is printed. That is push. It is fast and effortless for you, but the hawker must keep a list of subscribers and walk every lane, and when you are away the papers pile up on the doormat.

    Your neighbour prefers the stall at the corner. She walks down when she has time and buys whatever is latest. That is pull. The stall keeps no list and never worries about who is travelling. She decides when news reaches her, but some days she walks down for nothing, and she always reads it later than you.

    Your phone does both. A WhatsApp message lights up the screen the moment it is sent: push. An email app set to fetch every 15 minutes is pull, and so is dragging a feed down to refresh.

    Map it back: the hawker is a server pushing over a held connection or a notification service, the subscriber list is connection and subscription state, the pile on the doormat is the backlog for offline consumers, the walk to the stall is a poll, and the wasted walk is an empty response.

    Push and pull, precisely

    Strip away the technology and one question remains: who starts each transfer?

    • Push: the producer (a server, broker or publisher) initiates. When new data exists, it sends it to the consumers it knows about. The producer decides when data moves and how fast.
    • Pull: the consumer initiates. It asks “anything new since X?” when it is ready, and the producer answers with what it has, possibly nothing. The consumer decides when and how fast.

    Everything else follows. The initiator must know where the other side is, so a pusher keeps a list of subscribers and their connections or URLs, while a puller only needs the producer’s address. And the side that does not set the rate is the one that can be overwhelmed: with push, a slow consumer gets swamped; with pull, it simply falls behind while the backlog waits at the producer.

    The same updates delivered both ways. In push the producer starts every transfer; in pull the consumer does.Notice The push burst lands while the consumer is busy, whereas the pull consumer waits until it is free and collects the backlog as one batch. It pays with an empty poll first.

    Three clarifications prevent most confusion:

    • You choose per hop, not per system. Kafka producers push to brokers and consumers pull from them; Kafka’s design notes call this the traditional design shared by most messaging systems.
    • It is not synchronous versus asynchronous. That pair is about whether the caller waits for the result (synchronous vs asynchronous communication). A poll is a synchronous request that pulls; a webhook is a synchronous HTTP call that pushes.
    • The transport is not the pattern. Long polling sends pull-shaped requests but delivers with push-like latency. A WebSocket is just a pipe on which either side can push.

    What each side gains and what it pays

    Push Pull
    Latency As soon as data exists, plus one network transit Half the polling interval on average, a full interval at worst
    Wasted work None when nothing changes, apart from keep-alives Every empty poll still pays headers, auth and a lookup
    Who sets the rate Producer; the consumer needs flow control (prefetch, credits) Consumer; a slow one falls behind instead of falling over
    State on the server Subscriber list, open connections, delivery status per consumer Little or none; the consumer remembers its position
    Offline consumers Producer must buffer, retry or drop Data waits; the consumer catches up from its last position
    Replay Needs a separate store and resend logic Re-read from an older offset or cursor
    Reachability Producer must reach the consumer: hard through NAT, firewalls, sleeping phones Consumer reaches out from anywhere that can make a request
    Load grows with Change rate × subscribers; bursty under fan-out Consumers × poll frequency, even when nothing changes
    Failure detection Silence is ambiguous: dead, or nothing to say? A failed request is a clear signal
    Push versus pull, property by property. Each row is something you gain on one side and pay on the other.

    Read the table as one rule: push wins when data changes less often than it is checked and freshness matters; pull wins when the consumer must control its rate, may be offline, or needs to replay.

    The arithmetic: what polling really costs

    Take a food-delivery app with 1 million users who have it open, each checking for order updates every 30 seconds.

    • Load: 1,000,000 ÷ 30 = 33,333 requests/s.
    • Waste: if each user’s data changes about once every 10 minutes, that is 600 ÷ 30 = 20 polls per change, so 19 of 20 come back empty: 95% wasted, about 31,700 requests a second that each carry headers, a token check and a lookup.
    • Delay: an update lands at a random moment in the 30-second gap, so the average wait is 15 s and the worst is 30 s. To bring the average to 1 s you must poll every 2 s: 1,000,000 ÷ 2 = 500,000 requests/s, 15 times the load for the same information. RFC 6202 states the general law: with short polling, server and network cost depend strongly on the latency you accept.

    Now push the same updates over held connections.

    • Connections: 1M held open. Assume about 20 KB each for TLS state and buffers (measure your own stack): 1M × 20 KB ≈ 20 GB of memory across the gateway fleet. At 50,000 connections per gateway that is 20 gateways, plus headroom for reconnect storms after a deploy.
    • Messages: only on change: 1,000,000 ÷ 600 ≈ 1,667 messages/s.
    • Keep-alives: idle connections still need a ping to detect dead peers and stop proxies and NATs from dropping them; the SSE standard suggests a comment line every 15 seconds or so. One ping every 30 s is 33,333 frames a second, the same count as the polls, but a few bytes each with no auth and no database.

    Long polling sits between. Each client holds one request and the server answers when data arrives or the request times out. RFC 6202 says average latency is close to one network transit, and that 30 s is the safer timeout because proxies cut longer requests (experiments reached 120 s). Notice the catch: with updates every 10 minutes and a 30 s timeout, most requests still end in an empty timeout, so you make about 33,333 requests/s again. Long polling buys latency, not fewer requests, unless you can stretch the timeout; at 120 s it drops to about 8,333 a second.

    One minute, two updates, three ways to hear about them: short polling every 15 s, long polling with a 30 s timeout, and a pushed server-sent-events stream.Notice Short polling made five requests, three of them empty, and still delivered the updates 5 and 13 seconds late. Long polling delivered both almost at once but spent a request on an empty timeout.

    Compare the change rate with the check rate

    If data changes less often than consumers check, push wins, because most checks would find nothing. If data changes faster than consumers can use it (per-second metrics, logs, clickstreams), pull wins: the consumer takes a batch whenever it is ready, and nothing is sent that it can’t absorb.

    The same choice at eight layers

    Clients and servers: polling, long polling, SSE, WebSockets, mobile push

    Short polling is pure pull. Long polling is pull that behaves like push. Server-sent events push over one never-ending HTTP response and resume after a drop: the browser reconnects after a server-tunable retry delay and sends the last event’s ID in a Last-Event-ID header. WebSockets push both ways on one socket. Mobile push through APNs or FCM reaches an app that isn’t even running, but carries at most 4 KB. The pipe-by-pipe detail is in long polling, SSE and WebSockets and the patterns in real-time updates. Here the point is the trade: a live score needs push, while checking an IRCTC-style PNR status once is a perfectly good pull.

    Brokers: Kafka consumers pull, RabbitMQ pushes

    Kafka’s designers explain their choice directly. In a push system the broker controls the rate, so a consumer slower than the producers is overwhelmed, “a denial of service attack, in essence”; with pull, “the consumer simply falls behind and catches up when it can.” Pull also batches for free, because a consumer takes everything after its offset in one fetch. A naive pull loop busy-waits on an empty topic, so the fetch is a long poll: the broker holds it until fetch.min.bytes of data exists (default 1 byte) or fetch.max.wait.ms passes (default 500 ms). Raise the minimum to 64 KB and you trade up to half a second of latency at low traffic for bigger batches.

    RabbitMQ is push-first: registered consumers receive deliveries, and the prefetch count (basic.qos) caps unacknowledged messages, which is push with explicit flow control. See publish/subscribe for push and pull subscriptions, and backpressure for why Kafka lag is the healthy form of overload.

    Feeds: fan-out on write versus fan-out on read

    Fan-out on write pushes a new post’s ID into every follower’s home timeline, so opening the app is one list read. Fan-out on read pulls at read time: gather recent posts from every account you follow and merge them. The costs land in different places:

    • Push pays per follower. A post from an account with 10M followers is 10M timeline writes; at a fleet-wide 1M writes/s that is 10 s of all fan-out capacity for one post.
    • Pull pays per view. A reader following 300 accounts costs 300 lookups and a merge per open; at 100,000 feed opens a second that is 30M reads/s.

    So real feeds split by author: push for ordinary accounts, pull for the few with huge followings. The news feed design works through the cost model and Design Twitter the read-time merge.

    The celebrity hybrid. An ordinary author is pushed into followers’ home timelines at write time; a huge account is written once to its outbox and pulled in when a reader opens the feed.Notice The feed open does both: one pushed list read plus about ten pulled outboxes. Each author is paid for on the side where it is cheaper.

    Monitoring: Prometheus scrapes, short jobs push

    Prometheus pulls. It discovers targets and fetches each one’s /metrics page every scrape interval (1 minute by default; 15 s is common). Its FAQ gives three reasons: you can start extra monitoring instances, say on a laptop, without touching targets; you can tell more reliably when a target is down; and you can open a target’s metrics in a browser. Every scrape writes up = 1 or up = 0, so a dead target is a visible fact, not a silence. The project is candid that pull is only “slightly better” and not a major point.

    The exception is work that may finish before the next scrape. The Pushgateway guidance limits pushing to service-level batch jobs, because the gateway becomes a single point of failure and bottleneck, you lose up, and it keeps pushed series forever unless you delete them. Push-based agents (StatsD over UDP, OpenTelemetry’s OTLP exporters) suit short-lived processes and hosts behind NAT, but the collector must absorb whatever arrives. The arithmetic stays tame: 10,000 hosts scraped every 15 s is 10,000 ÷ 15 ≈ 667 scrapes/s. More in metrics, monitoring and alerting; heartbeats is the same pair for liveness.

    Pull and push metrics collection. Prometheus scrapes the targets it discovered; a short batch job pushes to a Pushgateway that Prometheus then scrapes; push agents send to a collector.Notice The pull side turns a dead target into a visible up = 0, while on the push side a dead host and an idle host look the same: silence.

    Integrations: webhooks versus polling an API

    A webhook pushes an event to your HTTPS endpoint the moment it happens. Polling the provider’s API pulls. Webhooks are fast and cheap, but delivery is at-least-once and unordered: Stripe retries for up to three days with exponential backoff, doesn’t guarantee order, may deliver the same event twice, and asks you to return 2xx quickly and process afterwards. So the handler must be idempotent on the event ID, and something must catch what never arrived. Polling works without a public endpoint and is simple, but costs latency and quota. The usual answer is both; see the hybrids below and designing a webhook delivery service for the sender’s side.

    Deployment and config: GitOps agents pull

    In push-style CD, the pipeline holds cluster credentials and applies changes. In GitOps, an agent inside each cluster pulls desired state from Git and keeps reconciling; the OpenGitOps principles name “pulled automatically” as one of four. You gain no inbound access or cluster credentials in CI, and drift is undone on the next pass. You pay in delay: Argo CD polls Git every three minutes by default, so it accepts a webhook to skip the wait. Its high-availability guide also warns that many applications refreshing on the same timer spike the repo server, and adds jitter. See designing a CI/CD deployment system and control plane vs data plane.

    CDNs: push zones and pull zones

    A pull CDN fetches from your origin on the first miss at each edge and caches by TTL: nothing to manage, only popular files occupy edges, but first viewers wait and a new release can stampede the origin. A push CDN has you upload files ahead of time: no origin hits and predictable launch-day behaviour, but you manage uploads, pay to store everything, and must delete or overwrite stale copies yourself. See content delivery networks.

    Caches: write-through versus cache-aside

    Write-through pushes each new value into the cache on write: reads hit and are fresh, but you pay to cache data nobody reads. Cache-aside pulls on a miss: the cache holds only what is read, but first reads are slow, values are stale until a TTL or invalidation, and a hot key can stampede. Details in caching strategies.

    Hybrids: how real systems combine them

    At scale, pure push and pure pull are rare. The common shape is: push a small signal, pull the data, and run a periodic pull as a safety net.

    • Tickle, then fetch. A notification says “order 812 changed”; the app fetches the order. Mobile payloads are capped (4 KB on APNs, 5 KB for VoIP; 4,096 bytes on FCM), and FCM’s docs describe content-free “ping” messages that tell an app to sync. Pushes can also be lost or merged: FCM keeps only four collapse keys per device, and stores at most 100 non-collapsible messages for an offline Android device; past that it discards them all and signals the app to do a full sync. Apple treats background notifications as low priority, doesn’t guarantee delivery and advises no more than two or three an hour. So the app must also pull on open.
    • Thin webhooks. Stripe now recommends “thin” events that carry only the event type and object ID; your code fetches the current object. Duplicates and reordering stop mattering, because you always act on the latest state.
    • Webhooks plus reconciliation. A job pulls “payments still pending after 5 minutes” from the gateway and fixes any the webhook missed. Stripe’s own recovery advice for an outage is to list events after the last one you processed: a pull.
    • Long polls that act like push. Browser long polling and Kafka’s fetch both hold a pull request open until data arrives.
    • Streams that resume like pulls. SSE events carry an id; on reconnect the browser sends Last-Event-ID, and the server replays from there.
    • Push to most, pull from a few. The celebrity split in feeds, above.
    • Poll, with a push shortcut. Argo CD polls every three minutes and treats a Git webhook as “look now”.
    Two hybrids in one food-order flow. Pushes (teal) only say that something changed; pulls (plain arrows) fetch the truth; a periodic pull catches anything the pushes missed.Notice No push carries the order itself. If the webhook or the notification is lost, the app still shows the right status on its next open, and the reconciliation job repairs the backend within five minutes.

    In the interview

    One sentence covers most hybrids: “The push is a hint; the pull is the truth.” Then name the safety net: “and a periodic pull reconciles anything the pushes missed.”

    Choosing: the decision guide

    Ask these six questions, in order:

    1. How fresh must it be? Seconds point to push or long polling; minutes make plain polling fine.
    2. Which is faster, the change rate or the check rate? Rare changes, frequent checks: push. Constant changes the consumer samples: pull.
    3. Can the producer reach the consumer? A public endpoint, an open connection, a phone that may be asleep, a host behind NAT.
    4. Who must control the rate? If a burst could swamp the consumer, pull, or push with prefetch or credits.
    5. What happens to a missed update? If it must not be lost, keep it durable and let the consumer pull from its last position.
    6. How many consumers, and how many per producer? Big fan-out needs a fan-out tier for push, or caching for pull.
    If… Pick Example
    Millions need the same update within seconds Push over SSE or WebSockets via a fan-out tier Live cricket score to 20M viewers
    The user must hear about a change while the app is closed Push a notification as a hint, pull on open Food order “out for delivery”
    A partner system must learn about an event Webhook push plus a reconciliation pull Payment status from a gateway
    High-volume consumers work at their own pace and may replay Pull from a durable log Log and clickstream pipeline on Kafka
    You monitor thousands of long-lived hosts Pull (scrape) Metrics from 10,000 hosts with Prometheus
    A job may end before the next scrape Push Nightly batch job to a Pushgateway
    Minutes of staleness are fine Plain polling, cached Dashboard refreshed every 5 minutes
    Thousands of agents must converge on one desired state Pull with jitter, optionally a push hint Config rollout to 50,000 agents
    Typical authors have hundreds of active followers Push at write time Home timelines
    One author has millions of followers Pull at read time for that author Celebrity posts in a feed
    Both sides talk, with low latency Two-way push on a WebSocket Chat, a chess move, live driver location
    Decision guide: if this is your situation, pick this, as in this example.

    Pick push for the live score, chat messages in a WhatsApp-style app, the driver’s position during a ride, an OTP or order alert, a payment webhook and a brokerage price ticker.

    Pick pull for Kafka consumers in a log pipeline, Prometheus scrapes, GitOps agents, a BI dashboard, an app refreshing its catalogue on launch, a one-off PNR status check and cache-aside product pages.

    Use the hybrid for food-order status (tickle plus fetch), feeds (push to most, pull from celebrities), payments (webhook plus reconciliation) and GitOps (poll plus webhook shortcut). This is the default for anything user-facing at scale; the notification system design builds it out.

    Going deeper: traps and failure modes

    “Push is real-time, so it is always better”

    Push is faster only when the consumer is connected and keeping up. It costs held connections, heartbeats, a subscriber registry and a fan-out tier, and it is no help when nobody is watching: pushing every like to 1M idle tabs wastes more than polling once a minute would. The live comments design shows the fan-out tier push needs at scale.

    Forgetting the offline client

    A push sent to a closed laptop, a phone in a tunnel or a webhook endpoint mid-deploy is lost unless someone stores it. Every push design needs an answer to “what if they weren’t there?”: a durable backlog, a sequence number the client resumes from, and a pull on reconnect.

    Push without backpressure

    A pusher that ignores the consumer’s pace turns a slow consumer into a crash, then a retry storm. Bound it with prefetch, credit windows or a per-subscriber buffer that drops or disconnects when full; see backpressure and load shedding.

    Synchronised polling herds

    Pull spreads load only if clients poll at random moments. After a deploy, an outage or a cron schedule, they line up: 50,000 agents polling every 60 s average 833 requests a second, but if all fire on the minute, 50,000 arrive in one second, 60× the average. Add random jitter to every interval and to every retry (backoff with jitter), and return 304 Not Modified via ETags so an unchanged answer is cheap.

    Cheat sheet

    Term Remember it as
    Push Producer initiates; lowest latency; must track subscribers and pace them
    Pull Consumer initiates; consumer sets rate; easy replay; delay ≈ interval ÷ 2
    Polling cost Clients ÷ interval: 1M every 30 s = 33,333 req/s, ~95% empty if data changes every 10 min
    Long polling Pull that acts like push; one held request; ~30 s timeout
    SSE resume Event id, then Last-Event-ID on reconnect
    Kafka fetch Pull with a long poll: fetch.min.bytes 1 B, fetch.max.wait.ms 500 ms
    Prometheus Pulls and records up; Pushgateway only for batch jobs
    Fan-out Write pays per follower, read pays per followee; split by author
    Mobile push 4 KB payload, may be dropped or throttled; a hint, not a record
    Default hybrid Push a signal, pull the data, reconcile with a periodic pull

    How to say it in the interview

    “The core question is who starts the transfer. For the live score I’d push over server-sent events through a fan-out tier, because 20 million clients polling every 5 seconds is 4 million requests a second, almost all empty. Each event carries a sequence number, so a reconnecting client resumes with Last-Event-ID. For order status I’d push a small notification and have the app pull the order on open, because notifications can be dropped or throttled. Internally, consumers pull from Kafka, so a slow one builds lag instead of falling over. I pay for push with held connections and heartbeats, which is fine because messages only flow on change.”

    Why did Kafka choose pull for consumers? With push the broker sets the rate, so a consumer slower than the producers is overwhelmed; with pull it falls behind and catches up, and takes everything since its offset as one batch. To avoid busy-waiting, the fetch is a long poll that waits for fetch.min.bytes or fetch.max.wait.ms.

    Is long polling push or pull? Mechanically pull: the client sends every request. Behaviourally push: the server answers as soon as data exists, so average latency is about one network transit. It still pays a full HTTP request per delivery or timeout, and its worst case is over three transits.

    How does a user whose phone was off for a day see their updates? Not from the push. Notifications are hints that can be collapsed, dropped or throttled. State lives on the server; on open the app pulls everything after its last cursor, or does a full sync if it has been away too long.

    10,000 agents poll for config every minute. What goes wrong? After a restart they line up and hit together: 10,000 requests in one second instead of 167 a second on average. Add jitter, serve config from a cache with ETags so unchanged polls are cheap, and push a “new version” hint if you want faster rollouts without polling more often.

    Where to go next

    References and further reading

    1. Apache Kafka documentation: Design, “Push vs. pull”kafka.apache.orgWhy consumers pull: a push broker overwhelms consumers slower than producers, pull falls behind and catches up, batches aggressively, and uses a long poll to avoid busy-waiting.
    2. Apache Kafka documentation: Consumer configskafka.apache.orgfetch.min.bytes (default 1 byte) and fetch.max.wait.ms (default 500 ms), the two knobs of the fetch long poll.
    3. Prometheus FAQ: Why do you pull rather than push?prometheus.ioPull lets you start extra monitoring instances, tell reliably when a target is down and inspect targets by hand; the project calls pull only “slightly better”.
    4. Prometheus: When to use the Pushgatewayprometheus.ioPush only for service-level batch jobs; the Pushgateway is a single point of failure, loses the up metric and never forgets pushed series.
    5. RFC 6202: Known Issues and Best Practices for the Use of Long Polling and Streaming in Bidirectional HTTPrfc-editor.orgShort polling cost grows as acceptable latency shrinks; long polling averages one network transit, peaks above three, and should time out around 30 seconds.
    6. WHATWG HTML Standard: Server-sent eventshtml.spec.whatwg.orgThe EventSource reconnection time, the retry field, the Last-Event-ID header for resuming, and a comment line every 15 seconds or so to keep proxies from dropping the stream.
    7. Firebase Cloud Messaging: Non-collapsible and collapsible messagesfirebase.google.comContent-free “ping” messages that tell an app to sync, four collapse keys per device, and the 100-message offline limit that triggers a full sync.
    8. Apple Developer: Generating a remote notificationdeveloper.apple.comAPNs rejects payloads over 4 KB (5 KB for VoIP notifications).
    9. Stripe documentation: Receive Stripe events in your webhook endpointdocs.stripe.comRetries for up to three days with exponential backoff, no ordering guarantee, duplicate deliveries, and returning 2xx quickly.
    10. Argo CD documentation: Git webhook configurationargo-cd.readthedocs.ioArgo CD polls Git every three minutes by default; a webhook removes the polling delay.