Database Runtime
DatabaseRuntime
provides a simpler, yet less powerful alternative to full-fledged event-sourcing solutions like Akka/Pekko.
It stores events in a database table without maintaining any state in memory. Each interaction with the runtime reads events directly from the journal. This design results in higher latency but reduces memory usage.
Usage
Follow these steps to use the DatabaseRuntime
. Detailed explanations are provided in the sections below:
"org.business4s" %% "workflows4s-doobie" % "0.1.2"
val workflow: WIO.Initial = ???
val initialState: State = ???
val transactor: Transactor[IO] = ???
val storage: WorkflowStorage[WorkflowId, Event] = ???
val knockerUpper: KnockerUpper.Agent[WorkflowId] = ???
val runtime: DatabaseRuntime[Ctx, WorkflowId] = DatabaseRuntime.default(workflow, initialState, transactor, knockerUpper, storage)
val wfInstance: IO[WorkflowInstance[IO, WCState[Ctx]]] = runtime.createInstance(WorkflowId(1L))
Workflow Storage
Database runtime is database-agnostic but requires a database-specific component called WorkflowStorage
.
This component is responsible for storing and reading events as well as locking the workflow.
Users are expected to implement custom storages that fit their needs.
Postgres Storage
PostgresWorkflowStorage
is a minimal, but fully functional storage relying on postgres advisory locks and assuming byte-based store.
given eventCodec: ByteCodec[Event] = ???
val storage: WorkflowStorage[WorkflowId, Event] = new PostgresWorkflowStorage[Event]()
It requires the migration to be applied to the database by the user.
Locking the Workflow
To understand why locking the workflow is important, consider the following scenario:
- A workflow awaits two signals in parallel.
- Both signals arrive simultaneously.
- Only one path of the workflow can be followed.
To address this, workflows are locked, ensuring that only a single signal is processed at a time.