Lesson 4 of 7
Idempotency and duplicates
10 min
Listen to this lesson
Retries create duplicates. A message is retried because the flow did not see a success, but "did not see a success" is not the same as "nothing happened". CMP may have posted the IDoc and created the order a moment before the connection timed out. The retry then posts it again. Partners resend too: Great Lakes' own system resends an order when it misses an acknowledgment. Any interface that retries must therefore be idempotent: processing the same message twice must have the same effect as processing it once.
Pick a business key
Idempotency starts with a key that identifies the business document, not the technical message. Cloud Integration's message ID changes on every resend, so it cannot spot a partner resend. Contoso uses Great Lakes' purchase order number (for sales order 1000473 it was GLFS-4471), combined with a change counter if the partner sends changes under the same number. CMC_GLFS_Order_Receive copies the PO number into a header at the start, so every later step can use it.
The Idempotent Process Call step
Cloud Integration's Idempotent Process Call (palette: Call > Local Call > Idempotent Process Call) wraps a local integration process. Its Message ID field takes an expression such as ${header.glfsPoNumber}. The step stores IDs that were processed successfully in an idempotent repository. With Skip Process Call for Duplicates selected (the default), a repeated ID skips the local process. Deselect it and the process always runs, with the property CamelDuplicateMessage set so you can route on ${property.CamelDuplicateMessage}='true', for example to answer the partner with the original order number.
| Behavior | What SAP Help says | What it means for Contoso |
|---|---|---|
| When an ID counts as done | Only when the whole called subprocess succeeded | Keep the local process minimal: ideally just the IDoc call to CMP |
| How long IDs are kept | Deleted by default after 90 days | A resend after 90 days is not detected; the S/4HANA check below covers it |
| Scope of the history | Bound to that flow step instance | Deleting and remodeling the step starts with an empty history |
| Limit | Exactly-once needs an idempotent receiver; the step is "second-best" | If CMP posts and the call times out, the ID is not stored, so a retry can still duplicate |
Idempotent Process Call facts that shape the design.
The last row is the one that matters. The step protects against the partner sending the same order twice. It cannot fully protect against the lost-response case in the sequence above, because the call never reported success, so the ID was never stored. That is why SAP says exactly-once needs an idempotent receiver.
Make the receiver idempotent too
In CMP, the receiver is the sales order creation from the inbound ORDERS05 IDoc. Make it recognize the customer reference: the sales document type's customizing has a check on the customer's purchase order number, and where that check only warns, Kenji's team adds a check in the inbound processing so that a second order with reference GLFS-4471 is stopped. Because the IDoc is monitored in SAP Application Interface Framework, the repeat then shows up there as an error for Maria, not as a second sales order. As a detective control, add a duplicate check after posting: a report or SAP Fiori analysis that groups sales documents with the same sold-to party, customer reference and net value, so Maria can reject any duplicate that got through. Check the SAP Fiori apps reference library for the duplicate-handling apps available in your release before you name one in a test case. Agree with the process owner what a duplicate should do (reject, warn or update) and test it (lesson 7).
Data stores as your own duplicate check
You can also build the check yourself with a data store: a Get on the Entry ID before posting and a Write after success. Two cautions. A Data Store Write on an existing Entry ID without Overwrite Existing Message raises a DuplicateEntryException that the exception subprocess cannot catch, because the transaction rolls back. And entries expire (by default after 30 days, at most 180). SAP's guideline "Idempotent Process Call Handles Duplicates (with JMS and Data Store Operations)" shows how the step, JMS and data stores combine; prefer the standard step unless you need a check it does not offer.
Decide what the partner hears when a duplicate arrives. Silently skipping it is safe for the posting but can confuse Great Lakes if its system waits for a confirmation. SAP's guideline "Idempotent Process Call Handles Duplicates (With Alternative Response)" shows the other option: deselect Skip Process Call for Duplicates, detect the repeat with CamelDuplicateMessage and send back a response that says the order is already known. Contoso sets the custom status Duplicate on such messages, so a spike in resends is visible in monitoring and can be raised with the partner.
What would you do?
During a CMP restart, 14 Great Lakes orders time out and are retried. Afterwards Maria finds two sales orders for PO GLFS-4502. The delivery flow has an Idempotent Process Call keyed on the PO number. Why did it not prevent the duplicate, and what is the lasting fix?
- Retries and resends make duplicates; every retrying interface must be idempotent.
- Key on a business identifier such as the customer PO number, not the technical message ID.
- Idempotent Process Call: Message ID expression, Skip Process Call for Duplicates, CamelDuplicateMessage, IDs kept 90 days.
- The step cannot see a success that was never reported; exactly-once needs an idempotent receiver.
- Data Store Write duplicates raise an exception the subprocess cannot catch; prefer the standard step.
Check your understanding · not graded
Lesson check
1. Why is the Cloud Integration message ID a poor key for spotting partner resends?
2. Skip Process Call for Duplicates is deselected. How do you detect a duplicate in the local process?
3. By default, how long does the idempotent repository keep processed message IDs?
4. Why does SAP call the Idempotent Process Call the second-best solution for exactly-once?