Skip to content

Add ErrRecordExists sentinel and IsAlreadyExists predicate - #2

Merged
trakhimenok merged 1 commit into
mainfrom
record-exists-sentinel
Aug 23, 2026
Merged

trakhimenok merged 1 commit into
mainfrom
record-exists-sentinel

Conversation

@trakhimenok

Copy link
Copy Markdown
Contributor

Summary

  • Adds ErrRecordExists and IsAlreadyExists, the mirror image of the existing ErrRecordNotFound/IsNotFound pair, so an adapter that can reliably tell "insert failed because the key already exists" from any other insert failure has an exported sentinel/predicate to use.
  • Follows the exact existing idiom: a wrapped errors.New sentinel plus an errors.Is-based predicate, documented with the caveat that false is not proof of "not a conflict" — only adapters that adopt the sentinel report true.

Why

Insert is specified to fail when a key already exists, and adapters already implement that, but there is no way for a caller to distinguish that failure from any other insert error. This has already caused a real defect: sneat-co/slugs claims a unique slug via tx.Insert and, lacking any way to tell "already exists" apart, treats every insert failure as ErrSlugTaken — so a transient backend blip is misreported to a user as their chosen name being taken. sneat-co/bookius has the identical issue.

This is step 1 of a 3-repo rollout: dal-go/record (this PR) → dal-go/dalgo (adapters + conformance suite) → dal-go/dalgo2firestore (the adapter that actually fixes the live defect, since Firestore is what Sneat runs on).

Test plan

  • go build ./...
  • go vet ./...
  • go test -race ./... -cover
  • gofmt -l . (clean except a pre-existing, untouched update/update.go finding)
  • golangci-lint run ./... (0 issues)
  • New errors_test.go covers both predicates: true on the sentinel (bare and wrapped), false on nil, the other sentinel, and an unrelated error

🤖 Generated with Claude Code

Insert is specified to fail when a key already exists, but there was no
exported way to tell that failure apart from any other insert error — the
mirror-image gap of ErrRecordNotFound/IsNotFound. Downstream consumers
(sneat-co/slugs, bookius) currently must treat every insert failure as
"slug taken", so a transient backend blip is misreported to users as a
name collision.

ErrRecordExists/IsAlreadyExists follow the exact ErrRecordNotFound/IsNotFound
idiom: a wrapped sentinel plus an errors.Is-based predicate. IsAlreadyExists
positively identifies a confirmed conflict; false is not proof of the
opposite, because an adapter that hasn't adopted the sentinel yet will never
return it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@trakhimenok
trakhimenok merged commit 1a4a63d into main Aug 23, 2026
3 checks passed
@trakhimenok
trakhimenok deleted the record-exists-sentinel branch August 23, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant