I have the following (simplified) table examples in a PostgreSQL 11 database:
| Column | Type
| id | uuid
| core_id | character varying(255)
| name | character varying(255)
Indexes:
"examples_core_id_pkey" PRIMARY KEY, btree (core_id)
"examples_core_id_key" UNIQUE CONSTRAINT, btree (core_id)
"examples_id_unique" UNIQUE CONSTRAINT, btree (id)
Now, let's consider the following SQL statement:
INSERT INTO examples
( id, core_id, name)
VALUES
( $id, $coreId, $name)
ON CONFLICT (core_id)
DO UPDATE
SET
name = $name
When I try two inserts
- Insert 1 ('abc', 'abc', 'somename')
- Insert 2 ('abc' 'abc', 'somename')
I sometimes get a SequelizeUniqueConstraint error:
duplicate key value violates unique constraint
examples_id_unique
I don't understand why this is happening because I am specifying core_id in the ON CONFLICT clause. Thus, I am interpreting it so that if there's a lack of uniqueness on core_id, just perform the UPDATE. I was thinking postgres would just check the arbitrer index (core_id) in those cases and run the update.
Based on postgres docs v11
The optional ON CONFLICT clause specifies an alternative action to raising a unique violation or exclusion constraint violation error. For each individual row proposed for insertion, either the insertion proceeds, or, if an arbiter constraint or index specified by conflict_target is violated, the alternative conflict_action is taken.
I wonder if this is a concurrency problem because in a production environment, I get the following flow:
- Receive message 1 and message 2 with the same content at the same time.
- Message 1 passes, Message 2 fails with the sequelize validation error.
- Message 2 gets reprocessed, then passes.
This seems very similar to this question INSERT ON CONFLICT DO UPDATE SET (an UPSERT) statement with a unique constraint is generating constraint violations when run concurrently but haven't found a solution to this problem yet.
The "arbiter index" chosen by the UPSERT is determined by the "conflict target" declared in the
ON CONFLICTclause. The manual:In your case,
ON CONFLICT (id, core_id)determines (only!) the multicolumn unique constraintid_core_id_unique. But not the other three unique constraints / indexes. The unique violation is only suppressed for the chosen arbiter index(es). Determining an alternative action would be ambiguous for multiple differing constraints.You actually have 4 unique indexes:
The constraint (and index)
examples_core_id_keyis 100% redundant, because the PK index is already implemented with an identical unique index. Drop that constraint at your earliest convenience (and implicitly also the index).That leaves two more unique indexes that will not tolerate duplicate input. The only way to catch unique violations from multiple different
UNIQUE(andEXCLUSION) constraints is the generic clauseON CONFLICT DO NOTHING. See:Or you get rid of the other overlapping constraints. Do you really need all (remaining) three? A
UNIQUEconstraint on(core_id, id)makes little sense for a table with aPRIMARY KEYon(core_id). MultipleUNIQUEconstraints are a rare exception for a halfway normalized design to begin with ...You changed the question after my answer. I am not writing another answer, but some variation of this will be the perfect solution to deal with multiple separate
UNIQUEconstraints: