implementing state_timeout in AshStateMachine

abeeshake456
2023-06-07

abeeshake456:

Just had a look at https://github.com/ash-project/ash_state_machine/blob/v0.1.4/test/ash_state_machine_test.exs

Is there an option similar to :state_timeout from https://www.erlang.org/doc/man/gen_statem.html#type-timeout_event_type

The use case is - if one state doesn’t get a new transition within :state_timeout seconds, it doesn’t keep waiting forever

jharton:

So depending on how long you want your timeout to be (and how restart robust) I see you have two main options.

  1. In your actions that move into a timeoutable state you add a change which calls :timer.apply_after/4 ( https://erlang.org/doc/man/timer.html#apply_after-4 ) to invoke another action which moves it into the timeout state if it hasn’t succeeded yet.
  2. In your actions that move into a timeoutable state you add a change which queues a job for later execution with a durable queue (eg Oban https://hexdocs.pm/oban/Oban.html#module-scheduling-jobs )

A variation on option 2 might be to just have an oban job that runs every n minutes which checks to see if there are any records that have been in the timeoutable state for longer than expected (eg expr(state == :waiting && updated_at < ago(5, :minute)) )

zachdaniel:

Keep in mind, ash_state_machine is not starting genservers.

zachdaniel:

Unless you’ve got your actions doing that.

zachdaniel:

It uses whatever data layer you’re using in the resource.

zachdaniel:

Id probably use ash_oban with a trigger on how long it’s been waiting, but that’s only good for things longer than a minute, generally

zachdaniel:

Which is basically #2 above but with ash_oban.

\ ឵឵឵:

If the exact duration of the timeout is relevant to the correctness of your application, you may also consider setting a timestamp attribute on your resource when entering the timed state and validating that it is within the allowed offset in actions relevant for that state.

\ ឵឵឵:

This is not mutually exclusive with the aforementioned methods for executing other actions/cleanups at some point after that duration has elapsed.

abeeshake456:

Some of the broader ideas make sense. I’ll try it out to understand better. 🙂