xstate icon indicating copy to clipboard operation
xstate copied to clipboard

Bug: reenter/always transitions do not get executed when state is restored

Open florian-lefebvre opened this issue 1 year ago • 11 comments

XState version

XState version 5

Description

If a machine has a state with reenter + always, after restarting the machine with the persisted state, the transition will not be re-executed.

Expected result

I expect the transition to keep looping with some restored state.

Actual result

The transition is never taken again.

Reproduction

https://stately.ai/registry/editor/d7e28966-d655-4b64-9312-a2b5aea2d0c1?machineId=06480795-85c4-42fc-967b-443fa349f204

Additional context

No response

florian-lefebvre avatar Dec 22 '23 15:12 florian-lefebvre

This is expected - a state machine can only change its state as a result of the transition. Restoring a state is not a transition. Think of it as "pause"+"resume". We resume with whatever you have restored and we wait for new transitions to happen. Otherwise, you'd risk re-executing the same actions (some could have already been executed before you paused). On top of that, actions might depend on event and when you resume no event is received so we don't have any event value to call your actions with.

Andarist avatar Dec 22 '23 15:12 Andarist

So what would be the plan here then? Event-sourcing? https://stately.ai/docs/persistence#event-sourcing

florian-lefebvre avatar Dec 22 '23 16:12 florian-lefebvre

Could you tell me what problem are you facing?

Andarist avatar Dec 23 '23 08:12 Andarist

So what would be the plan here then? Event-sourcing? https://stately.ai/docs/persistence#event-sourcing

Yes, the solution would be replaying the events to get to the state if it is necessary to re-execute the actions.

davidkpiano avatar Jan 07 '24 16:01 davidkpiano

Sorry @davidkpiano, I was on vacation so I couldn't answer! I don't agree with the resolution of the issue (probably an understanding issue on my end but still). So let me reexplain what issue I'm facing and why I think it should not happen.

How the machine is designed

My machine has some logic (it does not matter what) and in a specific state (let's call it playing), it has an always reenter self-transition with a delay of 1s. It basically acts as a timer in this specific state.

So when the machine is in the playing state, the timer context variable is incremented by 1 every second.

What happens with persistence

On subscribe, I persist the machine using getPersistedSnapshot. When I restore the state using snapshot, the machine is in the right state (playing) but the always reenter self-transition is not taken.

What I expected

I'd expect this transition to be taken straight away because I don't think it should rely on another event to be triggered. Being in the playing state should be enough.

Alternatives

There's event-sourcing as you said and I may take this route anyway, but I think something can be fixed here.

florian-lefebvre avatar Jan 08 '24 09:01 florian-lefebvre

Ah okay @florian-lefebvre - this would be related to persisting and restoring timer, which we are still working on solving.

davidkpiano avatar Jan 08 '24 09:01 davidkpiano

Just to be clear, the context is persisted well, just not updated through the transition. Thanks for re-opening so quickly!

florian-lefebvre avatar Jan 08 '24 09:01 florian-lefebvre

I faced the same issue for a timer state machine I wanted to persist and restore. I tried event sourcing but the context depended on Date.now(). I don't know if there is a way around it and I didn't want to go in that direction.

I settled on a workaround with an event with a guard to the state where the reentering self-transition is set up to "jumpstart" the machine whenever it is restored. This is a very hacky way to fix it but maybe ¯\_(ツ)_/¯ this can help you @florian-lefebvre.

State machine link: https://stately.ai/registry/editor/350b30e8-7251-4b58-bd6c-670bfee8af31?mode=Design&machineId=04d2084b-bb49-4e65-b68e-3db3ac7c40ae

image

abizek avatar Mar 25 '24 08:03 abizek