delivery semantics
Delivery Semantics
Learn how at-most-once, at-least-once, and exactly-once delivery semantics describe message behaviour during failures, retries, acknowledgments, offset commits, and uncertain outcomes. Understand why broker delivery is different from a business result, how idempotency creates effectively-once outcomes, and how Kafka, RabbitMQ, and Amazon SQS apply these concepts.
Introduction
Producers and consumers communicate across networks and independent processes. Any part of this communication can fail.
Producer
|
v
Messaging system
|
v
Consumer
|
v
Database or external service
Possible failures include:
- The producer sends a message but does not receive confirmation.
- The broker stores a message but the acknowledgment is lost.
- The consumer receives a message and then crashes.
- The business operation succeeds but the consumer acknowledgment fails.
- The consumer commits its progress before completing the operation.
- A network timeout leaves the caller uncertain about the result.
Delivery semantics define how a messaging system and its applications handle these uncertain situations.
The three commonly discussed delivery semantics are:
- At-most-once
- At-least-once
- Exactly-once
Core idea: Delivery semantics describe whether a message can be lost, repeated, or processed once within a defined system boundary. Delivery semantics do not automatically guarantee that an external business operation happens exactly once.
Prerequisites
| # | Prerequisite | Why It Is Needed |
|---|---|---|
| 1 | Queues vs logs | Queues use acknowledgments, while logs commonly track consumer positions using offsets. |
| 2 | Kafka, RabbitMQ, and Amazon SQS | Each platform implements message progress and retries differently. |
| 3 | Consumer groups | Kafka consumer groups maintain independent partition offsets. |
| 4 | Database transactions | Business state and deduplication records often need one atomic commit. |
| 5 | Idempotency | At-least-once delivery can repeat a message. |
| 6 | Retries and backoff | Temporary failures require controlled redelivery. |
| 7 | Transactional outbox | Reliable publication must be coordinated with business database changes. |
What Are Delivery Semantics?
Delivery semantics describe the observable behaviour of a message when producers, brokers, consumers, networks, or downstream systems fail.
A complete messaging path contains several separate boundaries:
1. Producer creates message
2. Producer sends message to broker
3. Broker stores message
4. Broker delivers message to consumer
5. Consumer processes message
6. Consumer updates business state
7. Consumer records message progress
8. Broker considers processing complete
A system can provide a strong guarantee at one boundary and a weaker guarantee at another.
Delivery Semantics Comparison
| Semantic | Possible Delivery Count | Data-loss Risk | Duplicate Risk |
|---|---|---|---|
| At-most-once | Zero or one | Yes | No intentional redelivery |
| At-least-once | One or more | Reduced through retry | Yes |
| Exactly-once | One within the defined transactional boundary | Controlled within that boundary | Controlled within that boundary |
| Effectively-once business outcome | Delivery can repeat, but the intended business effect occurs once | Controlled through durable retry | Absorbed through idempotency or deduplication |
At-Most-Once Delivery
At-most-once delivery means a message is processed zero or one time. The system avoids redelivery, but a failure can cause the message or business operation to be lost.
Consumer receives message
|
v
Consumer records progress
or acknowledges immediately
|
v
Consumer starts processing
|
v
Consumer crashes
|
v
Message is not delivered again
|
v
Business operation is lost.
Advantages
- No duplicate processing caused by messaging retries
- Simple consumer behaviour
- Can reduce acknowledgment and retry overhead
- Suitable when occasional loss is acceptable
Limitations
- A message can be lost during failure
- A business operation can be skipped permanently
- Not appropriate for critical financial or transactional work
- Recovery can require another authoritative source
Possible Use Cases
- Noncritical telemetry samples
- Approximate metrics
- High-frequency signals where later values supersede earlier values
- Optional diagnostic data
At-most-once rule: Use at-most-once only when the business can explicitly tolerate missing messages or reconstruct the missing result from another source.
At-Least-Once Delivery
At-least-once delivery retries a message when successful delivery or processing cannot be confirmed.
Consumer receives message
|
v
Consumer performs business update
|
v
Business update commits
|
v
Consumer crashes before
acknowledgment or offset commit
|
v
Message is delivered again.
The message is less likely to be silently lost, but duplicate processing can occur.
Advantages
- Retries protect against transient failures
- Required work is less likely to disappear silently
- Suitable for many production workflows
- Works well with idempotent consumers
Limitations
- The same logical message can be processed more than once
- External side effects can be repeated
- Consumers require idempotency or deduplication
- Poison messages require bounded retry handling
Common Use Cases
- Database projection updates
- Order-processing workflows
- Search-index updates
- Report-generation tasks
- Webhook delivery
- Change Data Capture pipelines
Exactly-Once Semantics
Exactly-once semantics means that a record produces one result inside a clearly defined processing boundary, even when failures and retries occur.
The phrase must always include the boundary.
Possible exactly-once boundary:
Kafka input topic
|
v
Kafka transaction
|
+-- Consume input records
+-- Produce output records
+-- Commit consumed offsets
|
v
Kafka output topic
Kafka can coordinate transactional record production and consumed-offset progress for supported Kafka processing flows. This does not automatically make an unrelated database, payment provider, email service, or HTTP API part of the same transaction.
Kafka transaction:
Exactly-once processing
inside Kafka boundary
External payment API:
Separate failure boundary
External database:
Separate transaction boundary
Exactly-once rule: Never state “exactly once” without naming the systems and operations covered by the guarantee.
Effectively-Once Business Outcome
Effectively-once processing accepts that delivery can happen more than once while ensuring that repeated deliveries do not create repeated business effects.
Message delivered twice
|
v
Consumer checks stable message ID
|
+-- First delivery:
| apply business operation
| record message ID
|
+-- Repeated delivery:
detect existing message ID
return previous outcome
This pattern is commonly more practical when a consumer writes to a database or calls an external service that is outside the broker's transaction.
Exactly-Once vs Effectively-Once
| Area | Exactly-Once Semantics | Effectively-Once Outcome |
|---|---|---|
| Primary mechanism | Transactional processing inside a supported boundary | Idempotency, deduplication, uniqueness, or conditional updates |
| Physical delivery | Handled according to the transactional messaging model | Can occur more than once |
| Business effect | Committed once within the supported transaction | Repeated deliveries resolve to one intended outcome |
| External systems | Included only when explicitly part of the transaction | Protected using external idempotency or local coordination |
| Typical complexity | Requires platform-specific transactional configuration | Requires business-specific duplicate protection |
Delivery Has Multiple Boundaries
A messaging workflow should be analyzed one boundary at a time.
| Boundary | Confirmation Mechanism | Uncertain Outcome |
|---|---|---|
| Producer to broker | Producer acknowledgment or publisher confirm | The broker can store the message while the producer misses the confirmation |
| Broker to consumer | Delivery state, acknowledgment, or visibility | The consumer can receive the message and fail before confirming it |
| Consumer to business database | Database transaction commit | The database can commit while the consumer fails before recording messaging progress |
| Consumer to external API | External response and idempotency contract | The external operation can succeed while its response is lost |
Producer-side Uncertainty
Producer sends message
|
v
Broker stores message
|
v
Network fails before
acknowledgment reaches producer
|
v
Producer cannot know whether
the message was stored.
The producer has two broad choices:
- Do not retry, accepting possible message loss
- Retry, accepting possible duplication unless the broker or producer prevents it
Consumer-side Uncertainty
Consumer receives event
|
v
Consumer updates database
|
v
Database commits
|
v
Consumer crashes before
acknowledgment or offset commit
|
v
Event is delivered again.
At-least-once systems need consumer-side duplicate protection because this failure window cannot be eliminated by retry alone.
Kafka Delivery Semantics
Kafka delivery behaviour depends on producer acknowledgments and retries, consumer offset timing, idempotent production, and transactional processing.
Kafka At-Most-Once Consumer
Poll Kafka records
|
v
Commit offsets
|
v
Process records
|
v
Failure after commit
|
v
Records are skipped after restart.
Kafka At-Least-Once Consumer
Poll Kafka records
|
v
Process records
|
v
Commit business state
|
v
Commit offsets
|
v
Failure before offset commit
can repeat records.
Kafka Transactional Processing
Consume input records
|
v
Begin Kafka transaction
|
+-- Produce output records
+-- Include consumed offsets
|
v
Commit Kafka transaction
This model can provide exactly-once processing for supported Kafka-to-Kafka pipelines. An external database or HTTP service remains a separate boundary unless another coordination mechanism is used.
Kafka Consumer Configurations
kafkaConsumer:
groupId: learning-progress-projection
offsetManagement:
automaticCommit: false
commitAfterDurableProcessing: true
processing:
idempotencyRequired: true
boundedBatchSize: true
retries:
boundedAttempts: true
backoff: exponential-with-jitter
deadLetterPath: approved-failure-topic
observability:
consumerLag: enabled
commitFailures: enabled
duplicateDetections: enabled
processingFailures: enabled
RabbitMQ Delivery Semantics
RabbitMQ separates producer-to-broker confirmation from consumer-to-broker processing confirmation.
Publisher Confirm
Publisher sends message
|
v
RabbitMQ accepts publication
according to configured policy
|
v
RabbitMQ sends publisher confirm
A publisher confirm does not prove that any consumer completed the business operation.
Consumer Acknowledgment
RabbitMQ delivers message
|
v
Consumer processes message
|
+-- Success:
| acknowledge
|
+-- Temporary failure:
| reject or negatively acknowledge
| using approved retry behaviour
|
+-- Permanent failure:
route to controlled
dead-letter handling
Automatic Acknowledgment Risk
Message delivered
|
v
Broker treats message
as acknowledged
|
v
Consumer begins processing
|
v
Consumer crashes
|
v
Work can be lost.
Manual Acknowledgment and Redelivery
Consumer processes message
|
v
Business transaction commits
|
v
Connection fails before acknowledgment
|
v
RabbitMQ can redeliver message.
Manual acknowledgment after durable processing commonly produces at-least-once behaviour and therefore requires idempotency.
Amazon SQS Delivery Semantics
Amazon SQS uses receive, visibility timeout, and delete operations to control message processing.
SQS Standard Queue
Standard queues use at-least-once delivery. A message can be received more than once, so consumers must be idempotent.
Consumer receives message
|
v
Message becomes temporarily invisible
|
v
Consumer processes message
|
+-- Success:
| delete message
|
+-- Failure or no delete:
visibility expires
message becomes available again
SQS FIFO Queue
FIFO queues provide FIFO processing and duplicate-suppression features within the documented SQS FIFO model. Message groups provide separate ordered sequences.
Message Group:
learner-1042
Messages:
RegisterAccount
EnrollInCourse
CompleteCourse
Application-side idempotency remains valuable because a business operation can succeed while the message deletion or external response remains uncertain.
SQS rule: The queue's duplicate-suppression behaviour and the consumer's external business side effect are different boundaries. Protect the business operation independently.
Platform Comparison
| Area | Kafka | RabbitMQ | Amazon SQS |
|---|---|---|---|
| Consumer progress | Offsets | Acknowledgments | Message deletion after processing |
| In-progress state | Partition ownership and consumer processing | Unacknowledged delivery | Visibility timeout |
| Redelivery trigger | Restart or reassignment from committed offset | Missing acknowledgment, rejection, or connection loss according to policy | Visibility timeout expires without deletion |
| Duplicate protection | Idempotent producer, transactions, and idempotent consumer | Idempotent consumer and publisher deduplication strategy | Idempotent consumer; FIFO also provides queue-level deduplication features |
| At-least-once support | Process before committing offsets | Manual acknowledgment after processing | Standard queue delivery model |
| Exactly-once scope | Supported for defined Kafka transactional processing flows | Business effectively-once behaviour generally requires consumer design | FIFO queue semantics do not automatically include external business systems |
Idempotency with a Database
A common approach records the message ID and the business update in one database transaction.
Begin database transaction
|
v
Check processed-message record
|
+-- Already processed:
| return existing result
|
+-- New message:
apply business update
insert processed-message record
|
v
Commit transaction
|
v
Acknowledge, commit offset,
or delete queue message
Deduplication Table
CREATE TABLE processed_messages
(
consumer_name VARCHAR(150) NOT NULL,
message_id VARCHAR(150) NOT NULL,
processed_at TIMESTAMP NOT NULL,
PRIMARY KEY
(
consumer_name,
message_id
)
);
Version-aware Business Table
CREATE TABLE learner_progress
(
tenant_id BIGINT NOT NULL,
learner_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
progress_percentage DECIMAL(5, 2) NOT NULL,
source_version BIGINT NOT NULL,
PRIMARY KEY
(
tenant_id,
learner_id,
course_id
)
);
Deduplication prevents the same message from being applied twice. Version checks can additionally prevent an older replay from overwriting newer state.
Conceptual Idempotent Consumer
public void process(Message message) {
database.transaction(() -> {
boolean alreadyProcessed =
processedMessages.exists(
"progress-projection",
message.getMessageId()
);
if (alreadyProcessed) {
return;
}
progressRepository.applyIfNewer(
message.getTenantId(),
message.getLearnerId(),
message.getCourseId(),
message.getProgressPercentage(),
message.getSourceVersion()
);
processedMessages.record(
"progress-projection",
message.getMessageId()
);
});
acknowledgeMessagingProgress(message);
}
This is an educational example. Production processing requires safe transaction boundaries, schema validation, exception classification, authorization, retries, shutdown handling, and platform-specific message progress logic.
External Side Effects
Calling an external system introduces another uncertain boundary.
Consumer calls payment API
|
v
Payment provider completes charge
|
v
Network response is lost
|
v
Consumer does not know whether
the charge succeeded.
Retrying without protection can create a duplicate charge.
Possible controls include:
- An idempotency key accepted by the external service
- A stable business operation identifier
- A status-query API before retry
- A local state machine for uncertain outcomes
- A reconciliation process
Email and Notification Side Effects
Consumer sends email
|
v
Email service accepts request
|
v
Consumer crashes before
recording success
|
v
Message is retried
|
v
Recipient can receive
the email again.
Use a stable notification identifier and a provider or application-side deduplication policy when duplicate notifications are unacceptable.
Transactional Outbox
A transactional outbox coordinates a local business update with recording the message that must be published.
Begin local transaction
|
+-- Update business data
+-- Insert outbox message
|
v
Commit once
|
v
Outbox publisher sends message
|
v
Publication can be retried safely
Outbox Table
CREATE TABLE message_outbox
(
message_id VARCHAR(150) PRIMARY KEY,
aggregate_type VARCHAR(100) NOT NULL,
aggregate_id VARCHAR(150) NOT NULL,
message_type VARCHAR(150) NOT NULL,
payload TEXT NOT NULL,
created_at TIMESTAMP NOT NULL,
published_at TIMESTAMP NULL
);
The outbox prevents a local transaction from committing without leaving a durable publication record. The publisher and consumer still require duplicate-safe behaviour.
Retries
Retries are required for temporary faults but should remain bounded and classified.
Processing failure
|
v
Classify failure
|
+-- Temporary:
| retry with backoff
|
+-- Permanent:
| dead-letter or reject
|
+-- Unknown:
preserve evidence
use controlled handling
Use:
- Maximum attempt limits
- Exponential backoff
- Randomized jitter
- Dead-letter destinations
- Operator-visible failure reasons
- Controlled redrive
Poison Messages
A poison message fails consistently and cannot succeed through immediate retry.
Invalid message
|
v
Consumer fails
|
v
Immediate retry
|
v
Consumer fails again
|
v
Useful processing is delayed.
Infinite retry is not a reliability strategy. Move terminal failures to a controlled failure path while preserving the data needed for diagnosis.
Ordering and Delivery Semantics
Ordering and delivery count are separate guarantees.
| Guarantee | Question Answered |
|---|---|
| Ordering | In what sequence can related messages be observed? |
| Delivery semantics | Can a message be lost or delivered more than once? |
| Durability | What survives a broker or storage failure? |
| Processing semantics | How are message consumption and output committed? |
| Business idempotency | Can repeated processing create repeated real-world effects? |
Deduplication Is Not Ordering
Event Version 5 arrives
Event Version 4 arrives later
Both event IDs are unique.
Deduplication alone:
Does not detect that Version 4
is older than Version 5.
Use version checks, sequence numbers, logical timestamps, or conditional updates when older events must not overwrite newer state.
Exactly-Once Does Not Mean Error-Free
Exactly-once processing does not guarantee that:
- The business logic is correct
- The event schema contains correct data
- The partition key is correct
- The external API behaves idempotently
- A user receives one notification
- A database outside the transaction receives one update
- An operator never replays the event manually
Exactly-once semantics solve a defined processing problem, not every source of application duplication or incorrectness.
Learning-platform Examples
| Workflow | Recommended Direction | Reasoning |
|---|---|---|
| Optional click telemetry | At-most-once can be considered if loss is acceptable | Occasional missing samples might not affect correctness |
| Learner-progress projection | At-least-once with idempotent version-aware updates | Progress events should not be silently lost |
| Certificate generation | At-least-once with a unique certificate constraint | A repeated command must not produce duplicate certificates |
| Enrollment email | At-least-once with notification deduplication | A repeated queue delivery must not send uncontrolled duplicate email |
| Kafka projection transformation | Kafka transactional processing where the complete flow remains inside the supported Kafka boundary | Consumed offsets and produced output can be coordinated |
| Payment-backed enrollment | At-least-once workflow with stable payment idempotency key and reconciliation | The payment provider is outside the broker transaction |
Conceptual Delivery Policy
deliveryPolicy:
semantic: at-least-once
producer:
stableMessageId: required
retry:
enabled: true
maximumAttempts: approved-limit
backoff: exponential-with-jitter
consumer:
progressAfterDurableProcessing: true
idempotency: required
schemaValidation: required
businessState:
deduplication:
storage: transactional-database
uniqueConstraint: required
versionCheck:
enabled: true
externalEffects:
idempotencyKey: required-when-supported
reconciliation: required-for-uncertain-results
poisonMessages:
boundedRetries: true
deadLetterPath: approved-destination
observability:
duplicateDetection: enabled
retryCount: enabled
deadLetterCount: enabled
uncertainOutcomes: enabled
This configuration is conceptual. Exact acknowledgments, transactions, visibility, offset, and deduplication behaviour must be verified against the selected broker and client version.
Observability
Useful delivery-semantics metrics include:
- Messages published
- Producer acknowledgment failures
- Producer retry count
- Consumer delivery count
- Duplicate-message detections
- Consumer acknowledgment failures
- Offset-commit failures
- Visibility-timeout expirations
- Messages processed successfully
- Business-operation failures
- Retry attempts by failure category
- Dead-letter messages
- Uncertain external outcomes
- Reconciliation mismatches
- Oldest unprocessed-message age
Structured Delivery Event
{
"messageId": "stable-message-id",
"consumer": "certificate-generator",
"deliveryAttempt": 2,
"processingResult": "duplicate-absorbed",
"businessOutcome": "already-completed",
"deliverySemantic": "at-least-once"
}
Avoid including reusable credentials, complete private payloads, payment details, or sensitive personal data in routine delivery logs.
Alert Conditions
Alert when:
- Producer retries increase unexpectedly
- Publisher acknowledgments or confirms repeatedly fail
- Consumer redelivery increases
- Duplicate-message detections increase
- Offset commits or queue acknowledgments repeatedly fail
- SQS messages repeatedly return after visibility timeout
- Dead-letter volume grows
- Poison messages block useful processing
- Business outcomes remain uncertain after external timeouts
- Reconciliation detects missing or duplicated operations
- Oldest-message age exceeds the processing objective
- Retry traffic threatens downstream-system capacity
Troubleshooting Workflow
- Identify the message ID and business operation ID.
- Identify the intended delivery semantic.
- Define the producer, broker, consumer, and external-system boundaries.
- Check whether the broker accepted the publication.
- Check producer acknowledgment or confirm results.
- Check the consumer's delivery attempts.
- Check acknowledgment, offset, or deletion timing.
- Check whether the business database transaction committed.
- Check the processed-message or idempotency record.
- Check version or sequence information.
- Check retries and dead-letter handling.
- Check external-service status using the stable operation ID.
- Reconcile missing or duplicate effects through the approved procedure.
Common Delivery-semantics Mistakes
Saying “Exactly Once” without Defining the Boundary
The messaging platform can coordinate one boundary while an external database or API remains outside it.
Committing Progress before Processing
A failure can cause the message to be skipped permanently.
Processing before Commit without Idempotency
A failure after the business commit can cause the entire operation to be repeated.
Confusing Publisher Confirmation with Consumer Completion
Broker acceptance does not prove that downstream business processing succeeded.
Deleting an SQS Message before Durable Success
The queue can lose the task before its business outcome is committed.
Using an Unstable Deduplication Key
Retries generate different identifiers and bypass duplicate detection.
Check-then-act without a Transaction
Concurrent consumers can both observe that a message is absent and then apply the operation twice.
Assuming Deduplication Preserves Order
Two unique events can still arrive in the wrong business order.
Retrying a Permanent Failure
Repeated invalid input consumes processing capacity without creating progress.
Ignoring the External-system Boundary
A payment, email, or HTTP operation can succeed even when its response is lost.
Publishing after Database Commit without an Outbox
The business transaction can commit while message publication fails.
Testing Only the Successful Path
Duplicate, loss, and uncertainty problems appear primarily around failure boundaries.
Recommended Test Cases
| Test | Expected Evidence |
|---|---|
| Producer timeout after broker write | A retry does not create an uncontrolled duplicate outcome |
| Consumer failure before processing | The message is retried under at-least-once policy |
| Consumer failure after processing | Idempotency absorbs the repeated delivery |
| Offset committed before processing | The test demonstrates the possible skipped business operation |
| Kafka transactional pipeline | Input offsets and output records commit together inside the defined Kafka boundary |
| RabbitMQ lost acknowledgment | Redelivery occurs without creating a duplicate business result |
| SQS visibility timeout | The message becomes available again when it is not deleted |
| SQS duplicate delivery | The consumer returns the previously recorded outcome safely |
| External API response timeout | The consumer uses an idempotency key or reconciliation before retrying |
| Poison message | Bounded retry sends the message to the approved failure path |
| Outbox publisher failure | The committed outbox message remains available for retry |
| Concurrent duplicate deliveries | A unique constraint prevents both consumers from applying the effect |
| Older event arrives later | Version-aware processing prevents stale state from replacing newer state |
Delivery-semantics Best Practices
Recommended Practices
- Define delivery semantics for every important workflow.
- Name the exact boundary covered by each guarantee.
- Use at-most-once only when loss is explicitly acceptable.
- Use at-least-once with idempotent consumers for durable business workflows.
- Use stable message and business-operation identifiers.
- Commit consumer progress only after durable processing.
- Store deduplication and business updates in one transaction where possible.
- Use database uniqueness constraints as a final duplicate guard.
- Use version-aware writes for out-of-order events.
- Do not confuse broker acknowledgment with business completion.
- Use external idempotency keys for payments and HTTP side effects.
- Use a transactional outbox for reliable publication.
- Classify failures as retryable, permanent, or uncertain.
- Use bounded retries with exponential backoff and jitter.
- Move poison messages to a controlled failure workflow.
- Monitor duplicate detections and uncertain outcomes.
- Reconcile external side effects when responses are lost.
- Test crashes before and after every commit boundary.
- Document the point at which a message is considered complete.
- Verify platform-specific guarantees against official documentation.
Practice Exercise
Design delivery semantics for asynchronous workflows in your online learning platform.
Requirements
- Classify click telemetry as critical or loss-tolerant.
- Define delivery semantics for learner-progress events.
- Create a stable message ID for every event.
- Store progress updates and processed-message IDs transactionally.
- Prevent older progress events from overwriting newer progress.
- Define SQS message deletion after durable report creation.
- Define RabbitMQ acknowledgment after certificate generation.
- Define Kafka offset commit after projection persistence.
- Use Kafka transactions for one Kafka-to-Kafka transformation.
- Document the exact boundary of the Kafka guarantee.
- Add an idempotency key to an external notification request.
- Use an outbox for enrollment-event publication.
- Create bounded retry and dead-letter policies.
- Simulate failure before processing.
- Simulate failure after the business commit.
- Verify that duplicate delivery creates one business outcome.
Design Template
| Workflow | Delivery Semantic | Duplicate Protection | Progress Boundary |
|---|---|---|---|
| Optional telemetry | At-most-once where approved | Not required if loss and omission are acceptable | Delivery without retry |
| Learner-progress projection | At-least-once | Event ID and source-version check | Offset commit after database transaction |
| Certificate generation | At-least-once | Unique learner-course certificate constraint | Acknowledge after durable certificate record |
| Report-generation queue | At-least-once | Stable report-request ID | Delete queue message after report persistence |
| Kafka stream transformation | Exactly-once within Kafka transaction boundary | Kafka transactional processing | Input offsets and output records committed together |
| External payment operation | At-least-once workflow with reconciliation | Provider idempotency key | Record confirmed or reconciled provider result |
Frequently Asked Questions
What are delivery semantics?
Delivery semantics describe whether a message can be lost, delivered more than once, or processed once within a defined boundary.
What is at-most-once delivery?
At-most-once means a message is processed zero or one time. Failures can cause message loss, but messaging retries do not intentionally create duplicates.
What is at-least-once delivery?
At-least-once means a message is retried until successful progress is confirmed, so duplicate processing can occur.
What is exactly-once delivery?
Exactly-once means one committed processing result inside a specific transactional system boundary.
What is effectively-once processing?
Effectively-once processing allows repeated delivery while using idempotency or deduplication to produce one intended business outcome.
Why do duplicates happen?
The business operation can succeed while the acknowledgment, offset commit, deletion request, or network response fails.
Why is committing before processing dangerous?
A consumer can fail after reporting progress but before completing the business operation, causing the message to be skipped.
Why is committing after processing safer?
The message is not silently skipped, but redelivery becomes possible, so the consumer must be idempotent.
Does Kafka exactly-once processing include my database?
Not automatically. Kafka transactional guarantees cover supported Kafka operations. An external database requires its own transactional, idempotency, or coordination design.
Does SQS FIFO eliminate the need for idempotency?
No. The queue can provide FIFO and duplicate-suppression features, but an external business operation can still have an uncertain outcome.
What is the safest common production approach?
At-least-once delivery with stable message IDs, transactional deduplication, idempotent business logic, bounded retries, and reconciliation is a practical approach for many workflows.
How should delivery semantics be selected?
Select them from the cost of message loss, cost of duplication, supported transaction boundary, external side effects, latency, operational complexity, and recovery requirements.
Key Takeaway
Delivery semantics define what can happen to a message around failures. At-most-once avoids messaging redelivery but can lose work. At-least-once protects against silent loss through retry but can repeat processing. Exactly-once coordinates one processing result inside a clearly defined transactional boundary. It does not automatically include unrelated databases, payment systems, email providers, or HTTP APIs. For many production workflows, the practical design is at-least-once delivery with an effectively-once business outcome. Use stable message identifiers, transactional deduplication, database constraints, version-aware writes, external idempotency keys, bounded retries, dead-letter handling, and reconciliation. Commit offsets, acknowledge RabbitMQ deliveries, or delete SQS messages only after the intended durable operation succeeds. Most importantly, document exactly which boundary each delivery guarantee covers and test failures before and after every commit point.