What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Preventing duplicate or missing chat messages requires protecting every handoff: durable acceptance, publication to the fan-out stream, processing for each recipient, storage, and client catch-up. Use retries to recover from transient failures, but assume a retry can repeat work. Give each logical message a stable ID, make repeat effects idempotent, track progress safely, and provide a way to replay gaps. Kafka can coordinate certain writes and offsets inside Kafka; it cannot by itself guarantee exactly-once delivery to a database, push service, or user’s device.

Where duplicates and gaps enter the fan-out path

A chat message may pass through several independently failing components: an API or service accepts it, a broker records an event, workers route it to recipients, storage records recipient-visible state, and clients synchronize. A success at one boundary does not prove that every later boundary succeeded. For example, the broker may have accepted a publish even though the producer never received its acknowledgement; a recipient worker may have stored a message but crashed before recording its progress; or a client may disconnect after storage succeeds but before it receives the update.

Define what “accepted” means in your system. A useful contract is that the service acknowledges a send only after the authoritative message state is durable according to the service’s configured policy. Broker acknowledgements and replication settings matter to the durability of a Kafka record; do not treat “the producer got success” as a deployment-independent durability guarantee.

Keep the logical chat message distinct from each delivery attempt. One message can have many attempts and recipient-specific outcomes, but should retain the same logical identity throughout. This distinction is the basis for safe retries and later repair.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall

Choose delivery semantics with the failure trade-off in view

Approach Loss and duplicate behavior Where it fits
At-most-once Can avoid replay, but a crash after progress is committed and before processing completes can leave work missing. Only when occasional loss is acceptable and replay is not required.
At-least-once Retries reduce silent loss, but a crash after the effect and before progress is committed can cause the work to run again. A common choice when effects can be made idempotent and gaps can be repaired.
Idempotent Kafka producer Deduplicates qualifying producer retries inside Kafka’s documented scope; it does not deduplicate arbitrary downstream effects. Kafka publishing where the producer may retry after an uncertain acknowledgement.
Kafka transaction Can atomically commit Kafka output records and consumed Kafka offsets for a consume-transform-produce workflow. It does not atomically include an unrelated database or client. Kafka-to-Kafka processing that needs coordinated output and input progress.

Apache Kafka 4.1 Design documentation says, “Otherwise, Kafka guarantees at-least-once delivery by default.” The surrounding qualification matters: at-most-once can be implemented by disabling retries and committing offsets before processing, with the corresponding risk that a failure skips work. Prefer at-least-once plus idempotent effects for chat unless the product explicitly accepts missing updates.

Assign one stable identity to each logical message

Create the event ID once at the authoritative write boundary, then preserve it through broker retries, fan-out, recipient persistence, and client synchronization. A retry must reuse the original ID rather than minting a new one. At each retryable boundary, record whether that ID has already produced the intended effect, or use the ID as a uniqueness key where the destination supports it.

  • At the source: identify the accepted chat message independently of transport attempts.
  • At the consumer: make handling the same event again safe, such as by recording its effect under a unique event key.
  • At recipient storage: ensure a repeated delivery does not create a second visible copy of the same logical message.
  • At the client: reconcile by stable message identity so a replay can fill a gap without rendering an extra message.

Kafka’s producer idempotence uses producer identity and per-partition sequence numbers to deduplicate internal retry records and preserve their order. Those broker-level identifiers are not a substitute for an application event ID shared by independent services or producer sessions.

Close the gap between source acceptance and publication

If the service first saves a message in an application database and separately publishes a fan-out event, there is a failure window between those operations. A crash after the database commit but before publication leaves an accepted message that workers may never see; publishing first can instead expose an event for a source write that fails. Kafka transactions coordinate Kafka records and Kafka offsets, not an arbitrary application database transaction.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A common application design is to persist the chat message and a publication record together in the source database transaction, then have a publisher retry unsent records. The publisher can publish more than once if it crashes after Kafka accepts an event but before it records successful publication, so downstream processing still needs stable IDs and idempotency. Treat this as an application-level coordination pattern, not a guarantee supplied by Kafka.

Set the send acknowledgement contract to match the product: if the API reports success before the source state is durable, a later replay cannot recover a message that was never durably accepted. Conversely, if the source is authoritative and the fan-out event is delayed, clients and recipients need a way to catch up from that source or its durable event history.

Use Kafka idempotence for producer retry ambiguity

A producer can time out after Kafka has accepted a record but before the acknowledgement reaches it. Retrying without idempotence can append a duplicate. Kafka’s idempotent producer feature associates records with a producer identity and sequence numbers, allowing Kafka to recognize internal retries within its supported scope.

KIP-98 describes idempotent producer behavior as scoped to a producer session. If recovery across producer restarts is required, use a stable transactional.id; Kafka can use it to recover transactional state and fence an older producer instance. This protects broker-side publishing behavior, but application IDs are still needed to recognize the same chat event across separate producers or downstream systems.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Producer idempotence does not make effects outside Kafka exactly once. A push notification provider, recipient database, or WebSocket client may receive repeated attempts unless that destination cooperates or the application makes repeats harmless.

Coordinate Kafka output and consumed progress with transactions

For Kafka-to-Kafka consume-transform-produce work, the key failure is disagreement between output and input progress. Committing the input offset before producing output can skip work after a crash. Producing output and then crashing before committing the offset can cause the input to be processed again. Kafka transactions can atomically commit the output records and consumed offsets, so either both become committed or the transaction is aborted.

  1. Configure the producer for transactions and initialize it with a stable transactional.id when restart recovery and producer fencing are required.
  2. Disable automatic offset commits with enable.auto.commit=false for this direct transactional consumer/producer pattern.
  3. Process the input and publish output inside a transaction, including the consumed offsets in that transaction rather than committing them separately.
  4. Set downstream consumers that must hide aborted output to isolation.level=read_committed.
  5. On abort, restore or seek the consumer to the last committed input position and retry the input, rather than treating the failed attempt as completed.

Kafka transactions can publish atomically across Kafka partitions, but that does not create global ordering: Kafka ordering is within a topic partition. Nor should a transaction be treated as a guarantee that every consumer always reads every record in that transaction as one indivisible batch. Consumer position, participating partitions, and retention or compaction can affect what a consumer sees.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Make recipient delivery recoverable, not just fast

Fan-out is often recipient-specific: the same accepted chat event may be routed to several users or devices, and one recipient’s delivery can fail while others succeed. Track durable recipient progress separately from the producer’s publish result. A broker offset says where a consumer is in a partition; it does not prove that each intended recipient’s storage or device has caught up.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Retain enough durable history to let a recipient resume from its last acknowledged cursor. Use a conversation sequence or equivalent ordering marker to detect discontinuities, then let the client request replay of the missing range or obtain a snapshot when replay is no longer available. Make replay use the same stable message IDs so that already received messages are not displayed twice.

Do not use a socket write or push-provider acceptance as the sole record of delivery. Those acknowledgements describe a transport boundary, not necessarily durable display or storage on the recipient’s device. Define what each acknowledgement means—queued, persisted, received, or displayed—and use the level the product actually promises.

Diagnose failures by the boundary that stopped progressing

  • Source message exists, but no fan-out event appears: inspect the source-to-publication handoff and its retry or pending-publication records.
  • Kafka has the event, but a worker appears to process it repeatedly: inspect consumer progress versus effect completion; repeats are expected if processing succeeds but progress does not commit.
  • One recipient is missing a message while others have it: compare that recipient’s durable cursor with the conversation sequence and request replay or snapshot repair.
  • Recipients see duplicates after a producer timeout: confirm retries retained the original event ID and that the destination uses it to make effects idempotent; Kafka producer idempotence only covers its documented producer scope.
  • Aborted transactional output is visible: verify consumers that require transactional visibility use isolation.level=read_committed.
  • Messages are missing after a broker failure: review the actual replica availability, acknowledgements, retention, and deployment policy rather than inferring durability from a successful application call alone.

For Kafka deployments, validate settings against the exact client and broker versions in use. The Kafka guarantees described here do not automatically specify database commit behavior, recipient routing, WebSocket or mobile delivery, or the client’s recovery protocol.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.