Machine Execution Engine

The Machine Execution Engine stands as a foundational component within the Arvo orchestration system, serving as the powerhouse behind state machine execution and lifecycle management. Working in seamless coordination with the ArvoOrchestrator, it brings machine definitions to life through the MachineRegistry while maintaining state persistence via MachineMemory.

At its core, the execution engine operates as an integral part of the Arvo orchestration system's architecture. The system follows a clean, hierarchical structure where the ArvoOrchestrator coordinates between three main components: the MachineRegistry for resolving machine definitions, MachineMemory for state management, and the MachineExecutionEngine for handling execution logic.

ArvoOrchestrator
├── MachineRegistry // Resolves machine definitions
├── MachineMemory // Manages persistent state
└── MachineExecutionEngine // Handles execution logic

The orchestrator follows a carefully designed event processing flow. When an event arrives, the system first resolves the appropriate machine through the registry. It then retrieves any existing state from memory, processes the event through the execution engine, and finally persists the updated state back to memory. This orchestrated flow ensures consistent and reliable state machine execution across the entire system.

The engine exposes a clean, deterministic interface for machine execution that prioritizes simplicity and predictability:

interface IMachineExecutionEngine {
execute({
machine, // Resolved machine definition
state, // Current state (null for new executions)
event, // Triggering event
}: ExecuteMachineInput): ExecuteMachineOutput;
}

The default implementation, MachineExecutionEngine, leverages the power of XState to provide robust state machine execution. This implementation handles two distinct execution scenarios that form the backbone of state machine orchestration.

For new workflow executions, the engine creates a fresh start by instantiating a new XState actor with the machine's logic. It carefully validates that the triggering event matches the machine's expected source type, sets up the initial machine context using the event data, and begins execution from the machine's defined initial state.

When dealing with existing workflows, the engine takes a different approach. It reconstructs the XState actor using the stored snapshot, restores the machine's previous context and state, and processes the new event within the existing workflow context. This allows for seamless continuation of execution from the last known state point.

This dual-mode architecture ensures that the engine can handle both new workflow initiations and continued processing of existing workflows while maintaining execution continuity and state persistence between events.

The system's flexible architecture allows for custom execution engine implementations to support different state machine libraries or patterns. Here's an example of how a custom engine might be implemented:

class CustomExecutionEngine implements IMachineExecutionEngine {
execute({ machine, state, event }: ExecuteMachineInput) {
// Initialize or resume execution based on state presence
const executor = state
? this.resumeExecution(state)
: this.startExecution(machine);

// Process the incoming event
const result = executor.process(event);

// Return execution output with updated state and events
return {
state: result.snapshot,
events: result.emittedEvents,
finalOutput: result.output,
};
}
}

When implementing a custom engine, several key requirements must be met. The engine should support both new and resumed executions, implement robust event processing capabilities, and generate appropriate event outputs. It must handle the complete execution lifecycle while integrating with system telemetry and implementing comprehensive error handling strategies.

The execution engine integrates seamlessly with other system components through well-defined interfaces:

class ArvoOrchestrator {
constructor({
executionEngine, // IMachineExecutionEngine
memory, // IMachineMemory
registry, // IMachineRegistry
});
}

The system's data flow follows a structured pattern where events flow through the orchestrator to the registry for machine resolution, then to memory for state retrieval, through the execution engine for processing, back to memory for state persistence, and finally return to the orchestrator for event emission.

State management in the execution engine requires careful attention to detail. Transitions between states must be handled atomically to ensure consistency, while proper resource cleanup prevents memory leaks and other performance issues. State integrity should be validated at system boundaries to catch potential issues early in the execution cycle.

Event processing demands equal attention to detail. The system must preserve strict event ordering to maintain execution consistency, while implementing graceful error recovery ensures system reliability. Support for event replay capabilities helps with debugging and system recovery scenarios, and maintaining event idempotency prevents duplicate processing issues.

Integration with the broader system requires adherence to interface contracts and implementation of comprehensive telemetry. Custom implementations should handle all possible execution scenarios while maintaining backward compatibility with existing systems. Clear documentation of integration requirements helps prevent implementation issues and reduces system integration time.

For more detailed information about the Machine Execution Engine, you can refer to our detailed execution flow diagrams. The XState documentation provides additional context for the default implementation.

We welcome contributions to the Machine Execution Engine. Please review our contribution guidelines in the CONTRIBUTING.md file for detailed information about our development process, coding standards, and pull request procedures.