Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Components

We could breakup this architecture into a bunch of different components but we decide to start with two: a cache and a container manager.

Container Manager

Let’s focus on the container manager first. What are the potential states the container manager could be in?

Well first of all it could be idle, waiting for a request to come in.

But once a request comes in the first thing we expect it to do is pull the proper container from the container registry. Then it stands the container up. Then at some later point when the user is done with the repl, it shuts down the container.

So in Fault we define a component and give it those states

component containerMng = states{
    idle: func{
       ...
    },
    pullContainer: func{
        ...
    },
    standUpContainer: func{
        ...
    },
    shutdownContainer: func{
        ...
    },
};

The transitions between states are nice and orderly. We don’t have any real forks in the road to contend with.

stateDiagram
	containerMng_idle --> containerMng_pullContainer
	containerMng_pullContainer --> containerMng_standUpContainer
	containerMng_standUpContainer --> containerMng_shutdownContainer
	containerMng_shutdownContainer --> 
containerMng_idle

Cache

The cache sits in front of the container manager and routes requests to active containers it knows about or back to the container manager to create a new environment for the repl.

The cache too can be idle when no requests are coming in. When a request enters the system, the cache first checks to see if it knows an active container the request belongs to. If it does it returns the record and reroutes the request to the correct place. If it doesn’t it passes the request back to the container manager and (eventually) creates a new record. Periodically it goes through all its records and cleans up the old ones.

component replCache = states{
    idle: func{
       ...
    },
    lookupRecord: func{
        ...
    },
    returnRecord: func{
        ...
    },
    createRecord:func{
        ...
    },
    expired: func{
        ...
    },
};

State Machine

While most single components can be drawn as state machines that loop back on themselves, our REPL’s two components actually interact with each other. When the cache can’t find a record of an active container it triggers a state change on the container manager. The container manager’s response triggers a state change on the cache (storing the record).

If we want to visualize that exchange it might look something like this:

stateDiagram
state replCache {
	replCache_idle --> replCache_expired
	replCache_idle --> replCache_lookupRecord
	replCache_expired --> replCache_idle
	replCache_createRecord --> containerMng_pullContainer
	replCache_returnRecord --> replCache_idle
	replCache_lookupRecord --> replCache_returnRecord
	replCache_lookupRecord --> replCache_createRecord
}
state containerMng {
	containerMng_pullContainer --> containerMng_standUpContainer
}

Initial States

Finally we need to tell Fault which of these states the model starts from. We do this via a start block.

start {
    replCache:lookupRecord,
    containerMng:idle,
};

For this model we’re going to cut to the chase and start the cache immediately in the lookupRecord state while the container manager is idle.

Start Block -vs- Run Block

.fsystem files must have a start block but they can also have a run block too. Fault will favor the run block, executing it first, then trigger all the active states after a single loop of the run block has completed.

When you import .fspec files Fault ignores whatever run blocks they might have (but honors any defined invariants like assertions or assumptions). This allows you to treat specifications as subsystems that can run as stand alone models as well as pieces of a larger more complex system.

If a .fsystem file does not have a run block, Fault will execute only the starting states of each component. If you want Fault to go farther you can define an empty run block

for 5 run {};