Xstate: Persisting Parallel State Machines Fails

Created on 13 Dec 2019  Â·  13Comments  Â·  Source: davidkpiano/xstate

Description
I'm trying to repeatedly externally step a parallel state machine with persistence. I have set up a simple linear machine that goes a -> b -> c -> d and notes entry and exit.

The base machine is

    states: {
      a: {
        entry: ["doEntry"],
        exit: ["doExit"],
        on: { TRIGGER: { target: "b" } }
      },
      b: {
        entry: ["doEntry"],
        exit: ["doExit"],
        on: { TRIGGER: { target: "c" } }
      },
      c: {
        entry: ["doEntry"],
        exit: ["doExit"],
        on: { TRIGGER: { target: "d" } }
      },
      d: { type: "final" }
    }

In the code sandbox I set up 4 tests.
I create a parallel machine which uses two instance of the base machine.

1) Simple Linear works as expected.
2) Simple Parallel also works as expected. You see two entrys to a, two exits of a.. etc
3) Step Linear Works, but complains about Warning: No implementation found for action type
I would like to know why that is the case, but the output looks correct
4) Step Parallel does not work.

For quick reference, the serialize/deserialize code is snipped below:

function step(machine, jsonState) {
  const service = interpret(machine);

  if (jsonState === null) {
    service.start();
  } else {
    var restoredState = State.create(JSON.parse(jsonState));
    service.start(restoredState);
    service.send("TRIGGER");
  }

  var retVal = JSON.stringify(service.state);
  service.stop();
  return retVal;
}

function parallelStepTest (machine) {
  console.log('start')
  jsonState.b = step(machine, null)
  console.log('step 1')
  jsonState.c = step(machine, jsonState.b)
  console.log('step 2')
  jsonState.d = step(machine, jsonState.c)
}

Expected Result

Step Parallel
start
entry a
entry a
step 1
exit a
exit a
entry b
entry b
step 2
exit b
exit b
entry c
entry c
step 3
exit c
exit c

Actual Result

Step Parallel
start
entry a
entry a
step 1
entry a
entry a
exit a
entry b
exit a
entry b
step 2
step 3

Reproduction

Additional context

bug

Most helpful comment

@davidkpiano I/We appreciate it. XState is impressive work. I would think wanting to persist complex states (and store them in a database) is a probably pretty common use case. Maybe if the documentation added an example of the "right way"â„¢ to take a complex machine and persist/rehydrate, it would go a long way.

As an example, the one at the bottom of https://xstate.js.org/docs/guides/parallel.html has hierarchy and parallel machines

Either way, thanks for creating XState. It's impressive work.

All 13 comments

Just a quick question so I get a better understanding of the reported issue - you want to rehydrate your child machines' states, right?

For this to work I think you'd have to serialize also your children states and later when rehydrating you'd have to rehydrate them first recursively and after that you'd be able to call a start on your root and step into it.

Hello @Andarist, thanks for following up.

Yes, I would expect to be able to serialize the complex machine at any state and then resume it at a later point.

Thanks for the tip there. I didn't realize that capturing the state of a machine wouldn't include the child states. I would expect it would do so. Is there a case where you might want to save a parent state without capturing the child states?

Either way, I will investigate recursively descending into child properties to save/restore state. If you or anyone else has an example of this, I would appreciate it very much.

Thanks again!

Has anyone successfully achieved this? I'm having a lot of trouble restoring child states correctly.

I created a new version of the code sandbox which captures the issue I'm having here:
https://codesandbox.io/s/xstate-parallel-machine-persist-ktjtw

It appears to correctly restore the state for step 1.. however on step 2 and further it hits
console.log('no service for ' + name) because the call to service.start(resolvedState) does not create the service.children map.

If anyone has a simple example of recursive machine re-hydration, or a suggestion as to why service.start isn't creating the children I'd greatly appreciate it.

Thanks.

@ASKobayashi any luck on solving this? I am running into a similar need.

So far, it seems like the current code may actually make it impossible, as the childMachine in the parent state is overwritten by the exec function https://github.com/davidkpiano/xstate/blob/master/packages/core/src/interpreter.ts#L871.

For this to work, I think the resolvedState would need to be called as the argument here
https://github.com/davidkpiano/xstate/blob/master/packages/core/src/interpreter.ts#L979

@wilson51492 Unfortunately, I don't have a solution yet. I did do some digging after I saw #941. It looks like there's some upcoming work that may help address some of these issues.. I also noticed #164 demonstrates a somewhat similar task. I haven't been able to take the time to revisit this yet and see if a fresh take will produce better results. I'd love to hear if you're successful at this though!

@ASKobayashi thanks for the quick response. I will let you know if I come up with anything.

I'll take a look at this soon.

@davidkpiano I/We appreciate it. XState is impressive work. I would think wanting to persist complex states (and store them in a database) is a probably pretty common use case. Maybe if the documentation added an example of the "right way"â„¢ to take a complex machine and persist/rehydrate, it would go a long way.

As an example, the one at the bottom of https://xstate.js.org/docs/guides/parallel.html has hierarchy and parallel machines

Either way, thanks for creating XState. It's impressive work.

dito to all of that ^

I appreciate it, thank you! Persisting complex states should "just work" ideally.

Any updates on how to persist/restore whole state of complex machine, including states of children sub machines?

Would love to hear the recommended approach as well.

I have to write official docs on this, but you would want to use an event sourcing pattern: https://github.com/davidkpiano/xstate/discussions/1217#discussioncomment-23219

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pke picture pke  Â·  3Comments

bradwoods picture bradwoods  Â·  3Comments

3plusalpha picture 3plusalpha  Â·  3Comments

laurentpierson picture laurentpierson  Â·  3Comments

bradwoods picture bradwoods  Â·  3Comments