0

What we need to do: We need to feature flag a few things in our current state machine

My ideal solution: Always load, no matter what state is, all feature flags and assign them to the state machine context

Attempts: Tried using async actions and invoke services, however, I cannot find a way to always run either of them

This basically my state machine and how I envisioned loading feature flag. However, the invoke.src function just gets called for the first time when I'm first loading the state machine.

Every time that I hydrate the state machine and the state machine is in one of the state, for example create, the invoke.src function does not get called therefore no FF is loaded into the context

const stateMachine = createStateMachine({
    id: 'state-machine',
    invoke: {
        src: async () => {
           return await featureFlagService.load();
        },
       onDone: {
         actions: assign(_context, event) => ({ featureFlagEvaluations: event.data }),
       }
    },
    states: {
        'create': { ... },
        'retrieve': { ... },
    }
});

Does anyone have any idea of how to implement such use case?

  • Do you want to feature flag the machine itself *(i.e. allow/restrict specific transitions or states)* or do you want to make sure feature flags are loaded and assigned to the machine context regardless of the initial state of the machine? – customcommander Jan 06 '23 at 10:50
  • Also you state machine doesn't look right to me. The `invoke` property must be nested in a state node e.g. `load-flags` so should look something like this: `{initial:'load-flags',states:{'load-flags': {invoke:{…}}, 'create': …}}` – customcommander Jan 06 '23 at 10:58
  • @customcommander thank you for your comments. I'd like to load all feature flags and assigned them to the state machine context regardless of the initial state of the machine or the target state is so that I can avoid letting the state machine transit to certain states. Regarding the state machine structure, we can have invoke in the first level of the state machine configuration. It works as it is, however, it just works when the initial state is loaded, if I target another state the invoke function does not get called – Guilherme Gambeti Jan 09 '23 at 00:56
  • The use case is quite straightforward: I need to always load the FF before any interaction with the state machine (e.g. state transition) – Guilherme Gambeti Jan 09 '23 at 01:04

1 Answers1

0

You should use the actor model approach. Each time when you need to refresh/fetch the FF you should spawn FF-machine and on done call parentSend() message which will update the context to your main SM(state-machine)

const stateMachine = createStateMachine({
    id: 'state-machine',
    invoke: {
        src: async () => {
           return await featureFlagService.load();
        },
       onDone: [{
         actions:  assign({
         ffActorRef: () => spawn(featureFlagMachine, 'ffActor'),
      }),
    }],
    states: {
        'create': { ... },
        'retrieve': { ... },
    },
    on:{
        REFRESH_FEATURE_FLAG : [{
         actions: assign(_context, event) => ({ featureFlagEvaluations: event.data }),
        }]
    }
});

const featureFlagMachine = createStateMachine({
    id: 'ff-machine',
    initial: 'retrieve',
    invoke: {
        src: async () => {
           return await featureFlagService.load();
        },
       onDone: [{
         actions: ['notifyRefresh']
    }],
    states: {
        'create': { ... },
        'retrieve': { ... },
    },
},
{
       actions: {
        notifyRefresh: sendParent((ctx, event) => {
        return {
          type: 'REFRESH_FEATURE_FLAG',
          data: { featureFlagEvaluations: event.data },
        };
      }),
    },
  }

}

);

Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
Ajay Kumar
  • 91
  • 5