Skip to content

test: regression tests for bulk manage_relationship rollback bug#702

Open
barnabasJ wants to merge 3 commits intomainfrom
test/bulk-manage-relationship-rollback
Open

test: regression tests for bulk manage_relationship rollback bug#702
barnabasJ wants to merge 3 commits intomainfrom
test/bulk-manage-relationship-rollback

Conversation

@barnabasJ
Copy link
Contributor

Summary

  • Adds regression tests for a bug where run_after_action_hooks in bulk actions does not call Ash.DataLayer.rollback when manage_relationships fails
  • Tests cover bulk_create (including upserts), bulk_update, and bulk_destroy
  • All bulk tests use batch_size: 2 with 5 inputs to exercise batch boundary behavior
  • Tests cover all strategies ([:stream], [:atomic, :stream], [:atomic_batches, :stream]) and both transaction modes (:batch and :all)

Related

Note: The 7 transaction: :batch tests will fail until the fix in ash-project/ash#2592 is merged and the ash dependency is updated. The remaining 12 tests (:all + controls) pass on the current codebase.

Destroy path inconsistency

Worth noting: the non-bulk Ash.destroy!/2 control test behaves differently from Ash.create!/2 and Ash.update!/2. Single create and update both exercise manage_relationship and correctly roll back when the child validation fails. Single destroy does not exercise manage_relationship for has_many with type: :create at all — the parent is simply deleted without attempting child creation. The bulk destroy path does exercise it (via run_after_action_hooks), so the bug manifests there.

This means there's an inconsistency between the single and bulk destroy paths regarding when manage_relationship is evaluated. It may be intentional (managing relationships on destroy is unusual), but it's worth investigating whether the single destroy path should also honor manage_relationship changes, or whether the bulk destroy path should skip them to match.

Test plan

  • Without fix (stock ash): mix test test/bulk_manage_relationship_rollback_test.exs → 19 tests, 7 failures (all :batch)
  • With fix (ASH_VERSION=local): 19 tests, 0 failures

When manage_relationships fails in run_after_action_hooks, the error
path returns {:error, error, changeset} without calling
Ash.DataLayer.rollback. This causes the parent record change to be
committed despite the child validation failure when using
transaction: :batch.

All bulk tests use batch_size: 2 with 5 inputs to exercise batch
boundary behavior. Tests cover bulk_create (including upserts),
bulk_update, and bulk_destroy across all strategies (stream, atomic,
atomic_batches) and both transaction modes (batch and all).
…rollback

When the rollback fix is applied, the failing batch triggers
stop_on_error so subsequent batches never run. Updated assertions
to check that the failing batch's records are NOT present rather
than asserting exact record lists.
@zachdaniel
Copy link
Contributor

It may be intentional (managing relationships on destroy is unusual), but it's worth investigating whether the single destroy path should also honor manage_relationship changes, or whether the bulk destroy path should skip them to match.

It is strange to manage_relationship on destroy but regardless bulk + non-bulk semantics should be identical. Would you mind opening a separate issue about that? Great catch 🙇

@barnabasJ
Copy link
Contributor Author

It may be intentional (managing relationships on destroy is unusual), but it's worth investigating whether the single destroy path should also honor manage_relationship changes, or whether the bulk destroy path should skip them to match.

It is strange to manage_relationship on destroy but regardless bulk + non-bulk semantics should be identical. Would you mind opening a separate issue about that? Great catch 🙇

Sure, is the expectation that manage_relationship is just not supported by destroy actions?

I can't really see a good usecase for it, I guess with the right options you might be able to delete something using it, but for that we have the cascade_destroy change, or datalayer level things.

Just wanted to make sure I know the way you wanna go with it before opening the issue

@zachdaniel
Copy link
Contributor

Sure, is the expectation that manage_relationship is just not supported by destroy actions?

It's meant to work roughly the same, especially considering that destroy actions could actually be soft? true mapping to an update action. But in general we don't require that the source record still exist, except certain configurations would naturally raise an error (like relate).

… behavior

Now that ash's single hard destroy path calls manage_relationships,
the control test should verify that invalid child data properly
triggers an error and transaction rollback, matching bulk destroy
behavior. Also adds a test confirming FK constraint rollback when
creating children for a hard-deleted parent.
barnabasJ added a commit to ash-project/ash that referenced this pull request Feb 27, 2026
The single hard destroy path in `destroy.ex` never called
`manage_relationships`, causing any `manage_relationship` changes
declared on hard destroy actions to be silently ignored. The bulk
destroy, soft destroy, create, and update paths all properly called
`manage_relationships`.

Adds `manage_relationships/4` to `Ash.Actions.Destroy` following the
same pattern used in `Ash.Actions.Update`, and pipes the data layer
result through it before the notification/select pipeline.

Also adds comprehensive tests for manage_relationship on destroy
actions: single hard destroy, single soft destroy, bulk hard destroy,
and bulk soft destroy.

Closes ash-project/ash_postgres#702
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.

2 participants