fix: declare unique_constraint on audit_log pkey to turn ConstraintError into soft error for *_and_log (OPS-4576)#36
Conversation
…ror into soft error for *_and_log (OPS-4576)
There was a problem hiding this comment.
Overall Assessment
The PR adds Changeset.unique_constraint(:id, name: "#{table}_pkey") inside changelog_changeset/1 so that pkey collisions on the audit_log table surface as {:error, changeset} instead of raising Ecto.ConstraintError. The existing callers (log_changes/5, log_changes_alone/6) already treat insert errors as best-effort logging and continue, so the outer transaction is preserved. The implementation is sound; no blocking bugs found.
Findings
No actionable findings.
Notes
- The
Changelogschema defaults its table viaApplication.compile_env(:ecto_trail, :table_name, "audit_log"); usingChangelog.__schema__(:source)inside the changeset builder correctly respects per-app overrides. - The new test exercises the exact code paths cited in the ticket (
update_and_log→log_changes→repo.insertviachangelog_changeset), but the PR description notes fullmix testcould not run locally due to missing Postgres—CI will be the first real execution. - The version bump (1.0.3 → 1.0.4) and CHANGELOG entry follow the project's release conventions.
|
Closing as a duplicate of #24 — all of these PRs fix the same bug: |
Description
Root cause of the production
Ecto.ConstraintError("audit_logs_pkey" unique_constraint) when callingupdate_and_log/log_changeson audit_logs (stacktrace points atecto_trail.ex:435insidelog_changes/5andecto_trail.ex:315insideupdate_and_log/4) was that the internalchangelog_changeset/1builder never declared aunique_constrainton the pkey.Per the exact guidance in the Ecto error message we observed, we now call
Changeset.unique_constraint(:id, name: "#{table}_pkey")on the changeset returned bychangelog_changeset. Violations are turned into{:error, changeset}(best-effort logging) instead of raisingEcto.ConstraintErrorand aborting the caller's transaction (e.g. theValiotApp.Repo.transactionin the reported GraphQL mutation).Summary of changes
lib/ecto_trail/ecto_trail.ex:578: addunique_constraintdeclaration (table name is dynamic viaChangelog.__schema__/1to respect:table_nameconfig).test/unit/ecto_trail_test.exs: TDD test under new describe "unique pkey constraint on audit_log (OPS-4576)" that forces a pkey collision (viaALTER SEQUENCE ... RESTART WITH 1) on the audit_log insert path and asserts the caller's tx still commits successfully.mix.exs: version 1.0.3 → 1.0.4 (semantic version bump before merge, per baseline rules).CHANGELOG.md: concise one-line entry for the fix.mix hex.outdated+ upgradedbenchee/credo(within allowed ranges) in the same PR per "Keep dependencies fresh" rule.Fixes the exact code path cited in the triage for OPS-4576.
Type of change
How Has This Been Tested?
unique_constraintdeclaration the new test reproduces theEcto.ConstraintErrorexactly as seen in prod.mix format(clean).mix deps.get+ compile (no new warnings from our diff).git diffreviewed before commit (no debug prints, no scope creep, no unrelated files).mix testcould not complete in this agent pod because Postgres is not available (test_helper.exs:82 does storage_up + migrator; connections refused on localhost:5432). This is pure infra for the test env; the unit test addition + the exercised paths (insert_and_log, update_and_log, log_changes) match the reported stack and the previous Multi integration tests. CI on the PR will run the suite against a real DB.Test Configuration:
Why
See Linear OPS-4576 (valiot/ecto_trail). Triage decision: NOTIFY+FIX (high, code_bug). "Root cause is missing unique_constraint handling inside Valiot-owned ecto_trail ... stacktrace explicitly shows the Ecto.ConstraintError path and the library's own code." Protocol requires the fix regardless of frequency.
https://github.com/valiot/ecto_trail/pulls (see also sibling tickets with similar prefixes for related audit pkey work).
Checklist:
mix format)Closes OPS-4576