Breaking action chain in a before_action?
Problem Statement:
My webhook receives at-least-once messaging from an external producer, and I want to persist these Events -- ids generated by the producer-- as an Ash Resource, such that only one instance is saved in the database (postgres), and an Oban job is only enqueued once.
I've created a custom
:add
action with the upsert?
feature, and that works great for the Event persistence -- idempotent no-ops on subsequent calls.
Issue:
However, it doesn't look like there is any information in the Ash.Changeset.after_action
record to determine if it was a fresh record or an existing record, and thus, I don't see a way to conditionally enqueue an Oban job.
Possible solutions?
1. Add a nonce
to Event, have a before action populate the changeset with a random number; followed by an after action that compares the returned record vs the changeset nonce... and enqueue Oban job if they match... This would be a probabilistic approach
2. Or, probably better, is if there a way for a before_action
to load a possibly already existing Event with that given id-producer pair; if exists then shortcut stop the action, returning that Event as the result of the action? Or is this an around_action
? -- knowing that these steps all live in the same db transaction...2 Replies
You have a few options. Here are some details that might help:
There is
Ash.Changeset.set_result
which will set the result of the action and avoid the initial insert.
At some point in the future, I'm going to add metadata on the object that is returned that tells you wether or not it was created or updated as a result of the operation, which is something that people usually need.
Until then, the general trick is to add timestamps and compare them
i.e if inserted_at == updated_at
then you just created the record
otherwise you just updated itI've been messing around and just came up with the following:
and
The above seems to work as I expected...
But now that you mentioned
Ash.Changeset.set_result
, I'm going to try using that in a before_action
... since that around_action piece looks a bit hairy to me
scratch that... i think you're right... created_at and updated_at comparisons in the after_action looks to be more clear.