View Source Runbox.Runtime.Stage.Emulator (runbox v13.0.3)

StageBased scenario runtime single-process emulator.

Using this module you can emulate multi-process StageBased scenario inside a single process. The emulation uses the original templates, i.e. modules implementing Runbox.Scenario.Template.StageBased behavior.

The emulation is designed to emulate a StageBased scenario in another scenario, usually in another scenario type (like Simple). You can also use this to test StageBased scenarios, however, Runbox.Runtime.Stage.Sandbox might be a better fit for testing.

If you plan to use this in a Simple scenario checkout a macro Runbox.Runtime.Simple.StageBasedEmulator. It provides deeper integration with Simple scenario runtime and enables you to quickly and easily convert StageBased scenario to Simple scenario emulating the original behavior.

Example use

The emulation consists of the following steps and activities. The emulated scenario is defined by a list of modules - templates. The emulation has its own internal state, so you don't need to remember the modules yourself.

First it's usually needed to know which logical topics are required by the emulated scenario. You can use function input_topics/1 for this purpose. It returns list of all input topics defined in templates' subscriptions as well as the type of the topic (input or load).

Then you can actually start executing the emulated scenario. At first you need to initialize it via initialize/2. This initializes the internals of the emulator, creates all static units (those defined in Runbox.Scenario.Template.StageBased.instances/0) in every template and also executes the Runbox.Scenario.Template.StageBased.init/2 callback for each of these units.

Then you can start handling messages via the function handle_message/3. Apart from regular scenario outputs, like output actions, this also outputs timeout registrations that need to be handled externally. This is done because handling timeouts internally might lead to problems when using the emulator in a scenario.

If you are persisting the internal state, such as in a savepoint, you need to use reinitialize_state/1 to refresh information that might not survive a persistence. Note the state is only designed to be persisted via ETF, it doesn't support JSON or other more restrictive formats.

Summary

Functions

Handles a message in the emulation session.

Initializes an emulation session.

Returns input topics the templates are subscribed to.

Re-initializes a state after it has been persisted and loaded.

Types

Functions

Link to this function

handle_message(message, from, state)

View Source
@spec handle_message(
  message :: Runbox.Message.t(),
  origin_logical_topic :: String.t() | any(),
  state()
) :: {:ok, [output()], state()}

Handles a message in the emulation session.

Takes a message, the logical topic it came from and the state of the emulation session and handles the message by the emulated scenario. This can produce outputs and also can change the state of the emulated scenario.

One of the outputs is a Runbox.Runtime.RuntimeInstruction. The caller is responsible for evaluating these instructions. E.g. if a timeout is registered the caller must ensure the timeout message is handled at the appropriate time.

Note that when handling such timeouts the argument from is ignored. Timeout messages are automatically routed to the unit they were created in and are not subjected to the regular message routing.

Link to this function

initialize(modules, start_from_ts)

View Source
@spec initialize(templates :: [module()], start_from_timestamp :: non_neg_integer()) ::
  {:ok, [output()], state()}

Initializes an emulation session.

The emulation is started by an initialization. This takes a list of templates and parameters needed for their initialization. The templates are used to create an emulated version of the computation network, much like a true StageBased scenario. This is encapsulated in an internal state. The state holds all the information needed for the emulation, including the current scenario state, and is then passed to handle_message/3 when consuming messages.

Statically defined units (defined via Runbox.Scenario.Template.StageBased.instances/0) are instantiated and initialized. This can produce output actions which are returned from this function.

One of the outputs is a Runbox.Runtime.RuntimeInstruction. The caller is responsible for evaluating these instructions. E.g. if a timeout is registered the caller must ensure the timeout message is handled at the appropriate time.

@spec input_topics(templates :: [module()]) :: [
  {:input_topic | :load_topic, topic :: String.t()}
]

Returns input topics the templates are subscribed to.

Goes through every specified template and finds all input topics in the subscriptions. Returns a list of all topics the templates are subscribed to including the type of the topic.

Link to this function

reinitialize_state(state)

View Source
@spec reinitialize_state(state()) :: state()

Re-initializes a state after it has been persisted and loaded.

The emulation state can be persisted into ETF. However, when such state is loaded some internal references might not work correctly and it is required to re-initialize the state. Call this function after you load the state and before you start using it.