A read-before-insert check cannot guarantee uniqueness under concurrent requests; a compound unique index can.
Spring Data MongoDB tenant uniqueness: enforce it in an index
The race is between the two reads
Two requests for tenant-west both check that invoiceCode INV-47 is unused. Both reads return empty. If the application relies on that check, both inserts can succeed. A unique index over tenantId and invoiceCode makes the database reject the second insert. A global unique index on invoiceCode would reject a legitimate invoice with the same code in another tenant.
Treat index creation as a deployment step
Spring Data MongoDB does not enable automatic index creation by default. An @CompoundIndex annotation documents the mapping, but an existing production collection still needs a reviewed migration that checks duplicate data before creating the index. Build the index ahead of the application change, record the rollout, and test the duplicate-key exception path. Expand-before-use is the same release principle in a relational store.
Assert both dimensions
Insert INV-47 twice in tenant-west and require one rejection. Insert INV-47 in tenant-east and require success. Inspect the actual index specification in the database; a repository mock cannot prove uniqueness. Return a stable conflict response rather than leaking the raw database error. The annotation is a mapping sketch, not evidence the index exists in production.
Implementation sketch
@Document("invoice_reservations")
@CompoundIndex(name = "tenant_invoice_unique",
def = "{'tenantId': 1, 'invoiceCode': 1}", unique = true)
class InvoiceReservation {
@Id String id;
String tenantId;
String invoiceCode;
}Cost and verification
Each insert or update maintains the compound index. The extra write cost buys a database-enforced invariant across every application replica.
Common Mistakes
- Do not rely on existsBy... followed by insert for a uniqueness guarantee.
- Do not assume the annotation created an index on an existing deployment.
- Do not omit tenantId from a tenant-scoped unique key.
Read next
Spring Data MongoDB optimistic locking: save the version you read, Spring JWT tenant claims: reject missing or malformed ownership before conversion, Spring database rollout: add a nullable column before changing every writer, Spring Boot Testcontainers and Flyway: let one owner prepare the test schema.
