Designing a Logging / Analytics Pipeline

A system design interview guide to building a pipeline that collects logs and events from thousands of services and lands them, without loss or duplication, in the stores that engineers and analysts actually query.

Student edition · start with the guarantee

Move structured events from applications to independent searchable, analytical, and archival sinks with a bounded loss/replay policy.

Correctness contract: Accepted records have a documented durability boundary. Each sink advances its own checkpoint only after its required durable writes; overload and quarantine are visible.

Basic HTTP, databases, and programming are assumed. Specialized terms are explained below. Named products motivate the problem; this is an educational design, not a claim about their current internals. Capacity numbers are illustrative decimal payload estimates, not benchmarks. Add indexes, protocol overhead, replicas, headroom, and failure capacity before provisioning.

Almost every company eventually arrives at the same realization: the data describing how the system behaves — request logs, error traces, business events — is at least as valuable as the data the system was built to serve. A logging or analytics pipeline is the plumbing that captures that exhaust from every running process and delivers it to the places people query it: a search store for debugging an incident at 3 a.m., a warehouse for the analyst building a dashboard, and cheap archival storage for the auditor who needs last year's records. The naive approach — have each service write straight to the destination — collapses the moment you have many producers, bursty traffic, or more than one consumer. This guide builds the pipeline up the way it actually has to be built: collect at the edge, buffer in the middle, process the stream, and fan out to several sinks, with delivery guarantees and ordering handled deliberately at each step.

Contents

  1. Requirements and Clarifying Questions
  2. Back-of-the-Envelope Estimation
  3. Sources of Data
  4. Record Format and Interfaces
  5. The Collection Agent
  6. End-to-End Architecture
  7. The Durable Buffer
  8. Stream Processing
  9. Sinks and Fan-Out
  10. Delivery Guarantees
  11. Partitioning and Backpressure
  12. Schema Management
  13. Failure Modes
  14. Tradeoffs and Alternatives
  15. A 45-Minute Interview Plan
  16. Common Mistakes
  17. Summary
  18. Check Yourself
  19. Key Terms
  20. Concepts to understand first
  21. Worked example
  22. Runnable Python model

Concepts to understand first

Each definition includes a concrete example and the condition under which the mechanism is useful. You can read this guide without opening another guide.

Pub/sub and durable queues

Publish/subscribe routes an event to interested subscribers; it may be ephemeral or durable. A queue/log with retained records and offsets allows replay after a consumer is disconnected.

Example. An online gateway misses a comment while restarting. Ephemeral Redis Pub/Sub cannot replay it; a durable comment log can fetch after the gateway's last sequence.

Limit. Pub/sub alone does not promise persistence, global order, or exactly-once effects. Broker choice, acknowledgements, retry horizon, and client catch-up protocol define those guarantees.

Offsets and checkpoints

An offset is a position in one log partition. A checkpoint is a recoverable, consistent record of input positions and processing state. On restart, state must correspond exactly to the input that is considered processed.

Example. If a batch increments a total and crashes before saving its offset, replay increments twice. Commit the total, dedup state, and partition position together, or use a compatible transactional/idempotent sink.

Limit. Saving an offset after a write avoids that specific loss window but can still duplicate; framework checkpoints alone do not make arbitrary external effects transactional.

Write-ahead log and fsync

A WAL records updates before data pages are considered committed. Recovery replays it after a crash. fsync (or an equivalent storage contract) requests that buffered bytes reach durable storage rather than only process/OS memory.

Example. A server acknowledges after its WAL commit and required replica writes are durably accepted. A process crash can then reconstruct the committed update.

Limit. Appending to a file alone does not guarantee power-loss durability. Device behavior, filesystem ordering, replicas, corruption, and the declared fault budget still matter.

Backpressure and load shedding

Backpressure tells an upstream producer that the downstream stage cannot accept more work. Buffering delays that signal; shedding deliberately rejects or drops lower-priority work when a bound is reached.

Example. A sink accepts 100 events/s while input is 150/s. A 1000-event queue fills in 20 seconds. The system must then block, reject, or apply its documented drop policy.

Limit. A finite queue cannot absorb an infinite outage. Prioritized messages still need hard quotas, deadlines, and bounded client queues; otherwise an exemption can exhaust the system.

Deduplication

Recognizing multiple deliveries of one logical event using a stable identity. The same logical event must keep its ID across retries, and claiming the ID must be atomic with the counted or stored effect.

Example. Events e1,e2,e1 should yield a count of 2. An exact seen set plus the increment in one transaction makes the second e1 a no-op.

Limit. A TTL limits the dedup horizon; retries after expiry may count again. Approximate filters may drop new events, and changing the ID or routing a retry to another local seen set defeats dedup.

Transactional outbox

A business transaction writes its state change and an event row to the same database. A relay publishes committed event rows later, retrying until the broker durably accepts them.

Example. An order and its OrderCreated outbox row commit together. A relay crash after publish but before marking sent publishes the same event ID again on restart.

Limit. The relay is at-least-once, not magically exactly-once. Consumers dedup transactionally; preserve per-entity sequence when order matters. CDC is change data capture: reading committed database changes.

1. Requirements and Clarifying Questions

FunctionalNon-functional
Collect logs and events from every host without app changes.Sustain 2 M lines/s (600 MB/s) with headroom for spikes.
Full-text search over recent logs.Search freshness under 30 s; queries in seconds.
Deliver structured events to the warehouse.No loss of events; duplicates removed.
Archive everything cheaply; replay on demand.Applications never block on the pipeline.

2. Back-of-the-Envelope Estimation

Assume 20,000 hosts × 100 lines/s, 300 bytes per line, 5× compression, Kafka replication factor 3 with 3 days of retention, and a search store that indexes 20% of the data (warnings and above, plus sampled info).

StepWorkingResult
Lines per second20,000 hosts × 1002 M /s
Raw bytes per second2 M × 300 B600 MB/s
Raw bytes per day600 MB/s × 86,400 s≈ 51.8 TB /day
Compressed per day51.8 TB ÷ 5≈ 10.4 TB /day
Kafka disk10.4 TB × 3 days × RF 3≈ 93 TB
Kafka partitions600 MB/s ÷ ~10 MB/s per consumer60 → use 128
Search store51.8 TB × 20% × 7 days × 2 copies≈ 145 TB
6-hour outage backlog600 MB/s × 21,600 s (raw)≈ 13 TB
Archive growth10.4 TB × 365≈ 3.8 PB /year

Two things stand out. The search store is the expensive sink, which is why routing and sampling matter. And a 6-hour sink outage builds a 13 TB backlog; with consumers able to run at 2× the input rate, the extra capacity (600 MB/s) drains it in about 6 more hours. That fits easily inside 3 days of retention.

3. Sources of Data

The first thing to be precise about is what is actually flowing into the pipeline, because the two main kinds of data have different shapes and different downstream uses. Lumping them together as "logs" hides design decisions you will later wish you had made on purpose.

KindExampleShapePrimarily for
Application logs"connection refused, retrying", a stack traceOften free-form text lines, sometimes with a severity and timestampDebugging and incident response
Structured events"checkout_completed" with amount, user, currencyA typed record with named fields, ideally schema-validatedAnalytics, metrics, billing

Application logs are what a human reads when something breaks; they are forgiving in format but enormous in volume. Structured events are what a query engine aggregates; they need consistent field names and types or every downstream analysis becomes a guessing game. A good pipeline accepts both but nudges producers toward structure: even a log line is far more useful when it carries a service name, a timestamp, a severity, and a trace id as proper fields rather than buried in a string. The earlier you impose a little structure, the less parsing and guesswork everything downstream has to do.

A useful interview framing: you are not building "a place to put logs," you are building a transport for two related-but-different data streams — high-volume diagnostic text and typed analytic events — that share one ingestion path but diverge at the sinks. Keeping that distinction in mind explains many later choices.

4. Record Format and Interfaces

A pipeline's "API" is mostly its record envelope. Agree on it early; every stage depends on it.

{
  "event_id":   "01J8Z3K4Q6…",          # unique, set once at the source (dedup key)
  "ts":         "2026-09-25T14:03:07.412Z",  # event time
  "service":    "checkout",             # also the partition key
  "host":       "web-4411", "region": "us-east-1",
  "severity":   "ERROR",
  "trace_id":   "4bf92f35…",            # links logs to traces
  "schema":     "checkout_completed/v3",# structured events only
  "body":       { … }                   # text line or typed fields
}
InterfaceShapeNotes
App → agentWrite to a local file or local socketNever a network call from the app.
Agent → brokerBatched, compressed produce to topic logs.<service> or events.<type>acks=all for events; acks=1 is acceptable for debug logs.
Search queryGET /search?q=service:checkout AND severity:ERROR&from=-15mHot data only.
WarehousePartitioned tables, e.g. events.checkout_completed by dt, hourColumnar files (Parquet) in the lake.

5. The Collection Agent

Once you accept that data is generated on thousands of hosts, the question becomes how to get it off those hosts reliably without coupling every application to the pipeline. The standard answer is a collection agent: a small, separate process that runs on each host (or as a sidecar next to each container), tails the application's log files or receives events over a local socket, and forwards them onward.

Running collection as a separate agent rather than inside the application buys a clean separation of concerns. The application's only job is to emit a line or an event locally and move on; it never blocks on the network and never needs to know where the data eventually goes. The agent handles the messy realities of shipping data: batching many small records into efficient payloads, compressing them, retrying when the next hop is unavailable, and buffering to local disk so that a brief outage downstream does not lose data or stall the application. Because the agent is uniform across the fleet, you can also use it to attach common metadata — host name, region, environment, service version — to every record without touching application code.

# agent runs on every host, tails sources and forwards in batches
function agent_loop():
  buffer = []
  for record in tail(log_files) + recv(local_socket):
    record = enrich(record, host=hostname(), region=REGION)
    buffer.append(record)
    if buffer.full() or timer.expired():
      try:
        broker.send(compress(buffer))   # one batched, compressed call
        buffer.clear()
      except Unavailable:
        spill_to_disk(buffer)           # survive a downstream outage
        buffer.clear()                  # spilled batches are resent by replay_spill()

function replay_spill():                # background thread
  for batch in oldest_first(spill_dir):
    broker.send(batch)                  # retry with backoff until it succeeds
    delete(batch)                       # only after the broker acknowledged it
Local disk is finite too

The spill directory has a size cap. If the broker is down longer than the disk can cover, the agent must choose: drop the oldest, drop low-severity lines, or sample. Decide this on purpose and emit a metric for every dropped record, so loss is visible instead of silent. Also resend spilled batches with their original event ids, so a batch that was actually delivered before the error is deduplicated downstream.

6. End-to-End Architecture

With sources and agents established, the central design is the path a record takes from the host where it is born to the store where it is finally queried. The defining move is to insert a durable buffer between the producers and the consumers, so that the side generating data and the side reading it are completely decoupled and can fail, scale, or slow down independently.

Logging pipeline architecture from host to sink
Sources (application logs and structured events) are picked up by a collection agent running on each host. The agent forwards batches to a durable, partitioned broker such as Kafka, which decouples producers from consumers and absorbs spikes. Stream processors consume from the broker to parse, enrich, and route records, then write them to the sinks — a search store, a data warehouse, and cold archival storage.

Reading the flow left to right:

The single most important property of this shape is that the broker breaks the system into two halves that can be reasoned about — and operated — separately. A spike in producers, a slow sink, or a redeploy of the processors no longer ripples across the whole pipeline.

7. The Durable Buffer

The broker deserves its own section because it is what turns a fragile chain into a resilient system. Without it, every producer is directly coupled to every consumer: if the warehouse is slow, the applications writing to it slow down too, and a traffic spike that the sinks cannot absorb is simply lost. Putting a durable, append-only log in the middle changes all of that.

Healthy: lag < retention retained (e.g. 3 days) deleted consumer newest lag Broken: lag > retention never read consumer newest
Kafka deletes old segments on a timer, whether or not anyone has read them. While the consumer's lag stays inside the retention window, a slow sink only adds latency. Once lag passes retention, the oldest unread records are deleted: that is silent data loss.
The broker is what converts "we have many producers and several fragile sinks" into "everyone talks to one durable log." It is the structural reason a slow warehouse becomes a growing backlog you can watch and drain, rather than data silently dropped at the edge.

8. Stream Processing

Raw records as they leave the agent are rarely in the form the sinks want. The stream processing layer is where they are transformed on the fly, record by record, as they flow from the broker to the sinks. Three jobs dominate this stage.

function process(record):
  record = parse(record)                  # text -> typed fields
  record = enrich(record, lookup_region)  # add context
  if is_noise(record):
    return                                 # drop, don't store
  for sink in route(record):              # fan out to chosen sinks
    sink.write(record)

Because processing runs as a horizontally scalable pool of consumers reading from the broker's partitions, it scales with traffic simply by adding more instances — up to the number of partitions, which is why partition count, discussed below, is a capacity decision and not just a correctness one.

9. Sinks and Fan-Out

The same processed record usually needs to live in more than one place, because no single store is good at everything. This is the fan-out: one logical stream is written to several sinks, each chosen for a different access pattern, cost profile, and retention.

One processed stream fanned out to search store, data warehouse, and cold storage
A single processed stream is fanned out to three sinks. A search / observability store serves interactive debugging, a data warehouse or lake serves analytics and BI, and cold object storage holds a cheap long-term archive. Each receives the same data but keeps it differently — different retention, cost, and query pattern.
SinkOptimized forRetentionCost
Search / observability storeFull-text search, recent data, low-latency debuggingDays to weeks (hot)High per GB
Data warehouse / lakeAggregations, joins, dashboards, ad-hoc analyticsMonths to yearsModerate
Cold object storageCheap, durable archive; compliance and replayYearsVery low per GB

Treating these as separate sinks rather than trying to force one store to do all three is what keeps each one affordable and fast at its job. The search store stays small and fast because it only holds recent hot data; the warehouse holds the structured events analysts care about; the archive holds everything cheaply for as long as policy requires. Routing decisions made in the processing layer determine which records reach which sink, so you are not paying search-store prices to store a year of debug logs.

10. Delivery Guarantees

At-least-once processing advances a sink's input position only after the required write is durably accepted. It permits redelivery after uncertain acknowledgements or crashes. It does not promise survival of arbitrary outages, retention expiry, source loss, or loss of all durable copies. At-most-once may be a deliberate policy for low-value debug telemetry, not for accepted audit events.

Kafka idempotent producers deduplicate supported producer retries, not arbitrary re-emission as a fresh logical event. Kafka transactions can atomically publish output records and input offsets inside Kafka; consumers must use the appropriate committed isolation. An external database/object/search sink needs its own transactional or idempotent publication contract.

Use stable source event IDs, explicit per-sink consumer groups/checkpoints, and a raw archive for longer replay. Approximate/windowed dedup is acceptable only when the consumer permits its undercount/duplicate horizon. The collection agent must persist source position and spill state in crash-safe order; a partially sent unacknowledged batch needs idempotent replay, not silent deletion.

11. Partitioning and Backpressure

Two operational mechanisms make the pipeline both fast and stable: partitioning, which buys ordering and parallelism, and backpressure, which keeps a fast producer from drowning a slow consumer.

Partitioning. The broker splits each topic into partitions. Records are assigned to a partition by hashing a key — for example the service name or the user id. This gives two properties at once: records that share a key always land in the same partition and are therefore consumed in order, while different keys spread across partitions and are consumed in parallel. The number of partitions is the ceiling on how many consumers can work a topic simultaneously, so it is the main lever for throughput.

Partitioning records by key for ordered, parallel consumption
A producer assigns each record to a partition by hashing a key (here, the service id). Records with the same key always land in the same partition, preserving their order, while different keys spread across partitions so that one consumer per partition can process them in parallel. More partitions means more parallel consumers.

Backpressure. Because the broker decouples the two sides, a slow sink does not stall producers; instead the consumer's lag — how far its committed position trails the newest record — grows. That lag is the system's primary health signal. Rising lag tells you a sink or the processor pool is the bottleneck, and it gives you a graceful place to react: add consumers, shed or sample low-value records, or let the broker's retention hold the backlog until the burst passes. The pipeline turns overload into measurable latency rather than silent data loss — until lag grows past retention, at which point Kafka deletes records nobody has read yet (see the diagram above). So alert on lag measured in time ("the consumer is 9 hours behind") and compare it with retention.

Strictly speaking, this is buffering. True backpressure means the slow side pushes back on the fast side: if the brokers themselves are overloaded, producer sends block or time out, the agents' buffers fill, and the agents must spill, sample, or drop. A good design says which of those happens, where, and how it is measured.

Partition count is a decision you make early and regret changing late: it caps parallelism, and re-partitioning a live topic disturbs key-to-partition ordering. Size it for peak throughput with headroom, and pick a partition key that spreads load evenly without splitting records that must stay ordered together.

12. Schema Management

A schema is the producer/consumer contract for field names, types, defaults, and meaning. Backward compatibility means a new reader can consume old data; forward compatibility means an old reader can consume new data. Full compatibility requires both under the selected serialization rules and version scope.

An optional field is safe only if defaults, unknown-field handling, and reader expectations allow it. Avro, Protobuf, and JSON Schema rules differ; a registry is not automatically a semantic validator. Version meanings, units, and enum behavior as well as shapes. Edge checks quarantine invalid records with access control, bounded storage, and a replay owner rather than poisoning aggregates.

13. Failure Modes

FailureWhat happensMitigation
Broker cluster unreachableAgents cannot send.Agents spill to local disk, replay later; drop policy and metrics once disk fills.
Broker node diesIts partitions fail over.RF 3, min.insync.replicas=2, acks=all for events that must not be lost.
Search store slow or downIts consumer lag grows.Other sinks keep flowing (separate consumer groups). Alert on lag in hours versus retention.
Lag exceeds retentionUnread data deleted.Scale consumers, raise retention temporarily, or backfill from the archive.
Poison record crashes the parserConsumer loops on it forever.Catch errors per record; send failures to a dead-letter topic with the error.
Producer ships a breaking schemaWarehouse columns go wrong.Registry compatibility check rejects it at produce time.
Log storm (a bug logs in a tight loop)One service floods the pipeline.Per-service rate limits and sampling in the agent; quotas on the broker.

14. Tradeoffs and Alternatives

ChoiceOption AOption BPick when
TransportAgents write straight to sinksAgents → Kafka → sinksA only for small fleets. B once there are several sinks or spikes.
DeliveryAt-most-once (fire and forget)At-least-once + idempotent sinksA for noisy debug logs under load. B for events and audit.
ParsingAt the agentIn stream processorsAgent keeps the broker structured; processors are easier to update centrally.
Search indexFull-text index everything (Elasticsearch/OpenSearch)Index labels only, grep chunks (Loki style)A for rich search; B for far cheaper storage when queries filter by service/time first.
RetentionLong in KafkaShort in Kafka, replay from archiveLonger Kafka retention makes recovery simple but costs disk.

Worked example: draining a six-hour outage

Assume raw ingress 600 MB/s, a sink outage of six hours, and recovered service capacity of 1200 MB/s.

Worked example, in orderBuild a backlog: 600×21,600=12,960,000 MB≈12.96 TB raw accumulates. Compression/replication change stored bytes, not logical backlog.. Restart at the last committed position: A sink retry may repeat a written record; an event-ID upsert or transactional contribution makes that repetition safe.. Continue serving new ingress: Net drain is 1200−600=600 MB/s, so catching up takes another six hours under the constant-rate assumption.. Protect the retention boundary: A three-day age policy is not an unconditional guarantee. Check oldest unread age, size retention, disk limits, and enough recovery headroom before old records expire.1Build a backlog2Restart at the last committed position3Continue serving new ingress4Protect the retention boundary
Read top to bottom. The table below explains the state and guarantee at each step.
StepState, calculation, and reasoning
1. Build a backlog600×21,600=12,960,000 MB≈12.96 TB raw accumulates. Compression/replication change stored bytes, not logical backlog.
2. Restart at the last committed positionA sink retry may repeat a written record; an event-ID upsert or transactional contribution makes that repetition safe.
3. Continue serving new ingressNet drain is 1200−600=600 MB/s, so catching up takes another six hours under the constant-rate assumption.
4. Protect the retention boundaryA three-day age policy is not an unconditional guarantee. Check oldest unread age, size retention, disk limits, and enough recovery headroom before old records expire.
Check your understanding: Does writing before committing the offset make loss impossible?

It closes the crash-before-write/offset gap only under retained durable input and durable sink assumptions. It can duplicate, and retention, producer loss, or sink data loss can still break the end-to-end promise.

Run a small, testable model

Download concepts.py. It uses only the Python 3.10+ standard library. From the directory where you saved it, run:

python3 concepts.py dedup
python3 concepts.py test

Use the transactional contribution example to see why replay can be safe even when delivery repeats.

The program contains deterministic assertions and failure traces. It is a learning model, not a production service: it does not implement network consensus, distributed durability, authentication, or third-party delivery. Read its contracts and try changing an input before extending it.

Primary references for these concepts

15. A 45-Minute Interview Plan

MinutesWhat to doWhat to say or draw
0–5ClarifyVolume, readers, freshness, loss tolerance per data type, retention.
5–10Estimate2 M lines/s, 600 MB/s, ~52 TB/day raw, Kafka ~93 TB, search is the costly sink.
10–20High-level designAgent → Kafka → processors → search / warehouse / archive.
20–35Deep divesAgent buffering; partitions and keys; at-least-once + idempotent sinks; lag vs retention.
35–42Correctness over timeSchema registry, compatibility, dead-letter topic.
42–45Wrap upFailure table, cost controls (sampling, tiered retention), what you would monitor.

16. Common Mistakes

17. Summary

A logging and analytics pipeline is a decoupled transport that collects data at the edge, buffers it durably, transforms it in flight, and fans it out to purpose-built stores. Its design is a handful of reinforcing decisions:

ConcernMechanism
What flows in?Two streams — high-volume application logs and typed structured events — sharing one ingestion path.
How does data leave the hosts?A collection agent per host: batches, compresses, enriches, retries, and buffers locally.
How are producers decoupled from consumers?A durable, partitioned broker (e.g. Kafka) in the middle that persists, replicates, and absorbs spikes.
How is raw data made useful?Stream processors parse, enrich, filter, and route each record as it flows through.
Where does it land?Fan-out to several sinks: a search store, a warehouse/lake, and cold object storage, each tuned differently.
How do we avoid loss and duplicates?At-least-once delivery (commit after write) plus dedup on a stable per-record id.
How do we get ordering and parallelism?Partition by key: same key in order, different keys in parallel; partition count caps throughput.
How do we handle overload?Buffer in the broker and watch consumer lag — overload becomes measurable latency, not loss, as long as lag stays under retention.
How do we keep data correct over time?A schema registry with compatible evolution and validation at the edge.
The recurring theme: a durable buffer in the middle lets the producing and consuming halves of the system fail, scale, and evolve independently. Everything else — agents, partitioning, at-least-once plus dedup, schemas, fan-out — exists to make that decoupled flow correct, ordered, and affordable.

18. Check Yourself

  1. A consumer writes a batch to the warehouse and crashes before committing its offset. What happens after restart, and why is that acceptable?
    Answer

    It re-reads and re-writes the batch. Because writes are idempotent upserts keyed by event_id, the replayed records overwrite themselves and nothing is double counted.

  2. Kafka retention is 3 days. The search sink has been down for 4 days. What happened to the data?
    Answer

    The oldest day of unread records was deleted by retention before the search consumer read it. Recover it from the archive, and alert on lag-in-time well before it reaches retention.

  3. Your topic has 24 partitions. You add 40 consumers in one group. How many do useful work?
    Answer

    24. Each partition is read by at most one consumer in a group, so 16 sit idle. Partition count caps parallelism.

  4. Why do the search store and the warehouse use separate consumer groups?
    Answer

    Each group tracks its own offsets. A slow search store builds its own lag without holding back the warehouse.

  5. A producer renames amount to total. What stops this from breaking dashboards?
    Answer

    The schema registry's compatibility check rejects the new schema at produce time. The safe path is to add total as a new optional field, migrate readers, then retire amount.

  6. At 600 MB/s, how much backlog does a 6-hour sink outage create, and how long to drain it at 2× capacity?
    Answer

    600 MB/s × 21,600 s ≈ 13 TB. The spare capacity is 600 MB/s, so it takes about 6 more hours to catch up.

19. Key Terms

Collection agent
Per-host process that tails logs and ships batches (Fluent Bit, Vector, Filebeat).
Broker
Durable, partitioned, replicated log between producers and consumers (Kafka).
Partition
An ordered shard of a topic; the unit of parallelism.
Offset
An offset is a position in one log partition. A checkpoint is a recoverable, consistent record of input positions and processing state. On restart, state must correspond exactly to the input that is considered processed. Example and limits above.
Consumer group
Consumers sharing one set of offsets; each partition goes to one member.
Consumer lag
How far a consumer trails the newest record, in records or time.
Retention
How long the broker keeps records before deleting them, read or not.
At-least-once
No loss, possible duplicates.
Effectively-once
At-least-once delivery plus idempotent writes, so duplicates have no effect.
Dead-letter topic
Where records that fail processing are parked for inspection.
Schema registry
Central store of event schemas and compatibility rules.

Further reading


⌂