Persistence (Serialization)

Table of contents

  • Persist StateMachine - means transform it into serializable representation, such as Serializable object or JSON text, and possibly saving it into some persistent storage like a file or sending by a network.
  • Restoration - is a process of restoring the StateMachine from the serializable representation.

There are several kinds or levels of StateMachine persistence (serialization). Let’s look at sample use cases:

1) Structure + configuration - Create StateMachine on some process/host and send its structure and active configuration by network to another process/host. The receiver can dynamically create the same StateMachine instance in the same state as original one. 2) Configuration only - Both original and restored StateMachine instances are crated by identical static code (in a single or multiple different processes/hosts). Only active configuration can be saved and restored.

Case 1 currently lacks built-in support by the library (you can open an issue if you need something like that). Case 2 in turn may be reached in two different ways:

1) Persisting state - serializing all internal data, active states, variables etc. from original StateMachine and applying them to restored one. 2) Event recording - serializing all incoming events, and applying them later on new StateMachine instance, which should lead it into the same state as original. This also allows to execute library callbacks (listeners) if necessary, which is not possible with state persistence approach. Currently only this approach has built-in support.

Event recording

The library supports event recording out of the box. To enable it you should use EventRecordingArguments in CreationArguments when creating a machine instance by createStateMachine() functions family. The recording process can be configured with EventRecordingArguments properties.

val machine = createStateMachine(
    creationArguments = buildCreationArguments { eventRecordingArguments = buildEventRecordingArguments {} }
) {
    // ...
}

When the machine had processed your business logic events, and you want to save its state configuration, first you have to get the recorded events:

val recordedEvents = machine.eventRecorder.getRecordedEvents()

RecordedEvents object now is ready to be serialized. The library provides an implementation of serialization process using kotlinx.serialization library starting from KStateMachine version v0.32.0.

Initialize serialization format (JSON for instance) using KStateMachineSerializersModule. This module contains serialization routines for library classes.

    val jsonFormat = Json {
    // use special, library provided SerializersModule for RecordedEvents and its internals
    // from kstatemachine-serialization artifact
    serializersModule = KStateMachineSerializersModule + SerializersModule { /* ... */ }
}

And encode (serialize) RecordedEvents object:

val recordedEventsJson = jsonFormat.encodeToString(recordedEvents)

Custom serialization library

Alternatively you can use some other serialization library to serialize RecordedEvents class by your own (you will also need to serialize SerializableGeneratedEvent class for internal events generated by the library itself).

Restoring StateMachine

When a user wants to restore the StateMachine, he deserializes RecordedEvents object and creates StateMachine instance having exactly the same structure as original one. Typically, both instances are created by the same code.

val restoredRecordedEvents = jsonFormat.decodeFromString<RecordedEvents>(recordedEventsJson)

Calling restoreByRecordedEvents() or its blocking analog restoreByRecordedEventsBlocking() will process recorded events over just created StateMachine instance.

machine2.restoreByRecordedEvents(restoredRecordedEvents)

restoreByRecordedEvents() method will start the machine if necessary. You can configure restoration process by restoreByRecordedEvents() arguments. The machine should not process any events before its restoration (in such case exception will be thrown) as it can possibly lead to incorrect restoration result.

See Event recording sample