Architecture Principles

OOCANA is OOMOL's core workflow engine, providing the foundational runtime support for the entire OOMOL Studio.

OOCANA Overview

OOCANA is an orchestration tool designed for scheduling and executing sequences of task units. The architecture consists of:

  • Scheduling logic stored in flow.oo.yaml files (referred to as flow)
  • Task execution content stored in block.oo.yaml files (referred to as block)
  • block.oo.yaml contains references to execution code, which we call block functions

Block functions can be written in either Nodejs or Python, with a mandatory function name main. This function accepts two parameters:

  1. Input data
  2. Auxiliary functions provided by the block runtime

The function should return a dictionary object (or in Nodejs, a Promise that resolves to a dictionary), which becomes the block's output data.

The flow file defines the triggering sequence and data transfer relationships between blocks.

Block Orchestration and Execution

The execution sequence of blocks is determined by connections defined in the flow, following a data-flow driven model — when required data is ready, the corresponding block is automatically triggered for execution. This differs from event-driven step triggering in GitHub Actions (where the next step is triggered after the previous one completes), which is primarily used in CI/CD scenarios like compilation and packaging.

Runtime Environments and Executors

Currently, blocks support Nodejs and Python runtime environments. Each runtime environment has a corresponding Executor that loads and executes the block code.

Key execution characteristics:

  • Same-language blocks run in the same runtime environment by default
  • Same-process optimization: When consecutive blocks use the same language and belong to the same package, OOCANA places them in the same process to reduce overhead
  • Independent processes: Blocks can be configured to run in independent processes for isolation or debugging purposes

For detailed information about executors and process management, see Executor Concepts.

Data Transfer Between Blocks

Data transfer between different language environments is transferred via JSON, while blocks in the same language environment can transfer data through JSON or directly via variables.

JSON Serialization (Cross-Language or Cross-Process):

  • Universal compatibility across all supported languages
  • Used when blocks run in different languages or independent processes
  • Slight performance overhead for serialization/deserialization
  • All Handle types except Variable can be cached

Direct Variable Passing (Same Language, Same Process):

  • Maximum performance with zero serialization overhead
  • Used via Variable type Handles
  • Can pass complex objects that cannot be serialized
  • Variables follow pass-by-reference semantics - modifying a variable value in one block affects the same variable in other blocks
  • Cannot be cached (upstream blocks will always re-execute)
info

When using Variable type Handles for maximum performance, be aware that the blocks become tightly coupled and can only work with blocks in the same language environment. This reduces block portability but provides significant performance benefits for complex object passing.

Block Data Transfer Specifications

Data formats are defined separately in block and flow. Currently, OOCANA runtime does not validate these data formats (some GUI components handle this validation). During execution, the actual data format returned by a block is not yet validated. Therefore, developers must ensure that returned data formats conform to the specifications defined by both flow and block.

Block Debugging and Acceleration Strategies

When a flow contains numerous blocks with time-consuming execution, debugging can be accelerated by specifying block execution strategies. Two acceleration methods are supported:

  1. Termination Point Strategy: Execution ends after reaching a specified block
  2. Cache Strategy: Utilizing previously executed data cache (JSON data only) to skip blocks that don't need re-execution, quickly reaching the specified block