I’m facing a duplicate row issue due to concurrent API calls.
We have 2 APIs (API A and API B) running simultaneously. API A only commits at the end of the process. Because both APIs run at the same time, they sometimes insert 2 rows instead of updating the existing one.
How can I prevent duplicate inserts and ensure that only one row is created while the other process updates the existing record?
Hello @cvnh,
Question here: is this API committing/inserting data into a local entity in your system, or is the API itself performing the insert operation into an external database?
If it is a local entity meaning you created the entity yourself and are using the Create Database Action then you can use the CreateOrUpdate approach directly. It automatically detects whether the record already exists: if it does, it updates it; otherwise, it creates a new record.
However, if the API itself is handling the insert logic on the external side, then there is probably nothing you can do from your side. In that case, the external system/team would need to handle the duplication logic.
What you can do from your side is use a temporary table/local entity. For example, if you have a GET API which retrieves this data you inserted, you can store the response in a temporary local entity first, then query it afterward so you can select only distinct records.
I can confirm that I am using a local entity and the CreateOrUpdate action within the module.
The reason we are still seeing double rows is a race condition. Because API A and API B are triggered simultaneously, they both perform their 'check' at the same time. Since neither has finished its transaction yet, they both see that the record doesn't exist and proceed to insert a new row.
Would it be acceptable to separate them into different transactions? For example, let API A finish first, followed by API B, which would then check whether the record already exists or if it is a new one.
There is also another option, although it is a bit more complex. You could use BPT and use Wait element, treating it as a lock until one transaction finishes before the other starts.
In other words, make the process synchronous instead of asynchronous, because it seems the async execution is causing the duplicate rows, especially since the records are not restricted by a business rule or unique constraint. Another thing mentioned by Gourav is using Unique index which can solve it but the problem here it would trigger an exception and stop the whole operation.
Hello @cvnh
The best solution is:
This way, even if both APIs try to insert simultaneously, OutSystems/database will prevent duplicate rows automatically.
Hi CVNH,
"We have 2 APIs (API A and API B) running simultaneously."
It seems like that last word is the problem. Why are you doing them simultaneously?
Gourav's solution will work, but you'll need to catch the error and instead fetch the record and update it, which sounds like it will be a performance hit if it happens often.
Kind Regards
Nathan Hobbs
Hi @cvnh,
I do not think modifying code or calling apis sequentially would be the best solution to the scenario.
This is a classic database race condition: both API A and API B do the “does it exist?” check while neither has committed yet, so both conclude “no” and both insert. Even using CreateOrUpdate can still lead to duplicates if both calls are effectively trying to create a “new” record at the same time.
Solution: I can suggest a few solutions that relates to how the DBMS works or APIs can be managed.
Solution 1: Enforce uniqueness at the database level.
Pick the business key that defines “this is the same record” (e.g., ExternalId, Email, ReferenceNumber, CompositeKey), and create a Unique Index on that column (or column set) in your OutSystems Entity.
This ensures the database becomes the final gatekeeper:
Solution 2: Use a locking read/update pattern for updates
If the record already exists, and both APIs are updating it concurrently, you may also need to prevent lost updates (one overwriting the other).
A common OutSystems approach is:
Solution 3: Make the API operation idempotent (prevents duplicates from retries / parallel callers)
Even when you fix concurrency at DB level, it’s good practice to make “create/update” endpoints idempotent so retries or parallel triggers don’t create additional side effects. This
This pattern is specifically designed to stop duplicate writes caused by retries/timeouts/double submits and “at-least-once” delivery behaviors.
Hope this helps.
Cheers,
Saugat
First, update the data using the CreateOrUpdate function.
Second, in CreateOrUpdate wrapper action, you need handle concurrency update (For this point: if source is update record, please use GetById() and check GetById() .Record.UpdateAt <= Source.UpdateAt, if true allow update else ignore )
Hope this helps for you!