Sequence number and primary term can reject a stale full-document reindex; source-version checks protect the business projection order.
Spring Data Elasticsearch sequence conflict: do not overwrite a newer projection
Two versions exist
Elasticsearch assigns a sequence number and primary term to a stored document. Spring Data can carry those values on a loaded entity and use them when indexing it again, rejecting a stale write with an optimistic-locking failure. Separately, an event projector may receive business version 84 after version 91 because of replay. Elasticsearch's concurrency metadata protects against concurrent index operations, but a fresh write of old business data can still be wrong unless the projector compares the source version.
Set a source ordering rule
Store sourceVersion inside the search document and reject any update whose source version is not newer than the indexed one. Keep the same document ID across retries. A projector that gives every replay a new random ID makes deduplication impossible. MongoDB resume tokens and idempotent consumers both need this target-side rule.
Test out-of-order delivery
Apply source version 91, then deliver 84 and 91 again. The indexed version must remain 91 and no duplicate document may appear. Also load one Elasticsearch entity twice, index one copy, then assert the second stale copy conflicts. The entity field sketch exposes the index-level mechanism; the source-version rule requires explicit projector code.
Implementation sketch
@Document(indexName = "orders-read-v4")
class OrderSearchDocument {
@Id String orderId;
long sourceVersion;
SeqNoPrimaryTerm indexVersion;
}Cost and verification
Version checks add comparisons and conflict retries. They protect data quality; a full rebuild may still be cheaper than repairing many silently stale search documents.
Common Mistakes
- Do not confuse Elasticsearch's sequence number with the domain source version.
- Do not assign a new document ID to every replay of one order.
- Do not retry a stale full-document write without reloading current state.
Read next
Spring Data Elasticsearch write versus search: refresh is the visibility boundary, Spring Data MongoDB change stream: resume token and idempotent projection, Spring Kafka duplicate delivery: let a unique event ID decide the second debit, Spring Data Elasticsearch mapping rollout: build a new index, then move the read alias.
