Skip to content

Commit

Permalink
improvement: support prefer_transaction? on DataLayer
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Oct 27, 2024
1 parent 29f7024 commit 931689a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 2 deletions.
19 changes: 19 additions & 0 deletions documentation/dsls/DSL-Ash.Notifier.PubSub.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ This is plug and play with `Phoenix.PubSub`, but could be used with any pubsub s
You configure a module that defines a `broadcast/3` function, and then add some "publications"
which configure under what conditions an event should be sent and what the topic should be.

## Example

```elixir
defmodule MyApp.User do
use Ash.Resource,
# ...
notifiers: [Ash.Notifier.PubSub]

# ...

pub_sub do
module MyAppWeb.Endpoint

prefix "user"
publish :update, ["updated", :_pkey]
end
end
```

## Debugging PubSub

It can be quite frustrating when setting up pub_sub when everything appears to be set up properly, but
Expand Down
6 changes: 5 additions & 1 deletion lib/ash/actions/update/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ defmodule Ash.Actions.Update.Bulk do
:bulk_destroy
end

if (has_after_batch_hooks? || !Enum.empty?(atomic_changeset.after_action)) &&
prefer_transaction? =
has_after_batch_hooks? || !Enum.empty?(atomic_changeset.after_action) ||
Ash.DataLayer.prefer_transaction?(atomic_changeset.resource)

if prefer_transaction? &&
Keyword.get(opts, :transaction, true) do
Ash.DataLayer.transaction(
List.wrap(atomic_changeset.resource) ++ atomic_changeset.action.touches_resources,
Expand Down
11 changes: 10 additions & 1 deletion lib/ash/changeset/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3356,7 +3356,16 @@ defmodule Ash.Changeset do
end

def with_hooks(changeset, func, opts) do
if opts[:transaction?] && Ash.DataLayer.data_layer_can?(changeset.resource, :transact) do
prefer_transaction? =
if Enum.empty?(changeset.before_action) && Enum.empty?(changeset.after_action) &&
Enum.empty?(changeset.around_action) do
Ash.DataLayer.prefer_transaction?(changeset.resource)
else
true
end

if prefer_transaction? && opts[:transaction?] &&
Ash.DataLayer.data_layer_can?(changeset.resource, :transact) do
transaction_hooks(changeset, fn changeset ->
resources =
changeset.resource
Expand Down
15 changes: 15 additions & 0 deletions lib/ash/data_layer/data_layer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ defmodule Ash.DataLayer do
@callback rollback(Ash.Resource.t(), term) :: no_return
@callback calculate(Ash.Resource.t(), list(Ash.Expr.t()), context :: map) ::
{:ok, term} | {:error, term}
@callback prefer_transaction?(Ash.Resource.t()) :: boolean
@callback can?(Ash.Resource.t() | Spark.Dsl.t(), feature()) :: boolean
@callback set_context(Ash.Resource.t(), data_layer_query(), map) ::
{:ok, data_layer_query()} | {:error, term}
Expand All @@ -297,6 +298,7 @@ defmodule Ash.DataLayer do
create: 2,
update: 2,
set_context: 3,
prefer_transaction?: 1,
calculate: 3,
destroy: 2,
filter: 3,
Expand Down Expand Up @@ -357,6 +359,19 @@ defmodule Ash.DataLayer do
data_layer(resource).calculate(resource, exprs, context)
end

@spec prefer_transaction?(Ash.Resource.t()) :: boolean
def prefer_transaction?(resource) do
data_layer = data_layer(resource)

if function_exported?(data_layer, :prefer_transaction?, 1) do
data_layer.prefer_transaction?(resource)
else
# default to false in 4.0
# also change in postgres data layer to default to false
true
end
end

@doc "Wraps the execution of the function in a transaction with the resource's data_layer"
@spec transaction(
Ash.Resource.t() | [Ash.Resource.t()],
Expand Down

0 comments on commit 931689a

Please sign in to comment.