Block Execution

Understand how blocks execute within blueprint flows: dataflow between blocks, the order in which flow paths run, error handling patterns, and debugging techniques.

Dataflow Between Blocks

In RUAL, data moves between blocks through pin connections. Each block can have input pins (in-pins) and output pins (out-pins). When you connect an out-pin of one block to an in-pin of another, you establish a data pathway. The system uses two distinct types of connections:

  • Flow pins: These control when a block executes. A block with a flow in-pin will only execute when it receives the flow signal from a preceding block.
  • Data pins: These carry typed data (value, number, condition, object, array, date, file, query, mutations, etc.) between blocks. Data pins define what information a block receives or produces.

Blocks that do not have a flow in-pin: such as value_default, condition_is_true, or query_bool_filter: are evaluated when the block they connect to needs their data. This means data-only blocks are resolved on demand as part of the execution chain.

Flow paths run one after another, depth first

Execution follows the flow pin connections, one block at a time. When a flow starts, for example from a trigger_custom_function block, RUAL runs the block its flow out-pin is wired to, then follows that block's own flow out-pins, and so on.

When a block has several flow out-pins, or one flow out-pin is wired to several blocks, RUAL follows them one after another. It runs the first target and everything downstream of it to the end before it starts the second target: depth first, never in parallel. Routing blocks such as branch, elseif and switch choose which of their outputs run; every other block runs all of its wired flow outputs. A block that cancels the flow stops what follows it.

  • Different flow outputs of one block run in the order of their pin ids.
  • Several wires on the same output run in the order they are saved in. In Blueprint Language that is the order you read: an on block runs before the statements that follow it.

Order matters

A flow that splits does not run its paths at the same time: the first path finishes before the next one starts, so a later path sees what an earlier one wrote. To run work concurrently inside one run, use a block made for it: parallel_functions, function_run_parallel, or the concurrency pin of function_foreach and array_map. Separate requests and events do run at the same time, each in its own run, so read-modify-write sections that must not overlap still need lock & wait blocks.

Precompiled Data

Each node prepares a blueprint once and keeps that prepared copy in memory: this is the precompiled blueprint. It holds the blueprint's structure, not values: every run computes its own values, and within one run a data block is computed at most once however many blocks read it. A save refreshes the precompiled blueprint on every node, and there is no idle expiry.

When calling functions, only pass the data you actually need (for example, just a guid instead of an entire object).

Error Handling

When a block fails, the flow usually does not stop: most blocks that can fail report it on their own output pins and let the flow continue. There is one exception: when a flow block itself cannot execute at all, its error pin is set to RUAL_SYSTEM_ERROR and the run ends there, so nothing after that block runs. The pins blocks use to report a failure are:

  • success. A condition pin that returns true if the operation succeeded and false if it failed.
  • error. A value pin that contains the error message when the operation fails.

It is the developer's responsibility to check these pins and decide how to handle errors. Common patterns include:

  • Checking the success pin and showing an error message in the UI.
  • Logging errors to storage or sending them to an external webhook (for example, an email notification).
  • Using a condition block to branch the flow based on whether the operation succeeded.
Example Error Output
{
  "success": false,
  "error": "Document not found for the given GUID"
}
No try/catch equivalent Unlike traditional programming, RUAL does not have a try/catch mechanism. Each block handles its own errors and exposes the result through its output pins. Always check the success and error pins on blocks that perform storage operations, HTTP requests, or other operations that can fail.

Debugging Blueprint Execution

RUAL provides several tools for debugging your blueprint flows:

Simulation Mode

Certain blocks, particularly those at the beginning of a flow, feature a play icon. Clicking this icon opens simulation options where you can set custom input values and execute the flow in simulation mode. The simulation traces through the connected blocks, showing you the output at each step. This is particularly useful for end-stage debugging.

Simulation Guide Learn how to configure simulation values for different pin types.

Production Run

When you select Production Run within the play mode options, it executes the flow using the non-deployed (development) blueprint but against production data. This allows you to debug with real data without affecting the live production environment. Note that the Production Run permission can be disabled for specific users in User Access Management.

Console

The blueprint console can be activated through the Options menu in the top right of your blueprint. The console shows execution details and can help identify issues in your flows.

Audit Log

Every blueprint modification is automatically logged. You can view the audit log through the Options menu or by clicking on a specific block's options () and selecting Revisions to see changes affecting only that block.

Asynchronous Patterns

While flows execute their blocks based on flow pin connections, there are several ways to offload work to run asynchronously:

Pattern Use Case
Queue Offload computationally intensive tasks (like PDF generation) to background processing. The queue executes a function at a specified time, potentially on another node. Use the function_custom_execute_from_queue block.
Storage Events React to document changes (create, update, remove) in a specific storage. These events execute asynchronously after the document operation completes, on a FIFO basis. Use the storage_event block.
Repeating Events Schedule recurring tasks that execute at a specified interval. Similar to setInterval() or crontab. Use the schedule_repeating_event block.
Important The function_custom_execute block executes a function and waits for it to complete before continuing the flow. If you want to run something in the background without waiting, use the queue instead.

Locking and Concurrency

When multiple flows could modify the same data simultaneously, you can use locking blocks to prevent race conditions:

Note that while the storage system itself is transaction-based (updates are processed sequentially per document), locking is useful when you need to ensure a broader set of operations runs atomically.

Best Practices

  • Keep flows focused: Each flow should have a single responsibility. Use functions to break complex logic into smaller, reusable pieces.
  • Use namespaces: When managing many functions across blueprints, use the namespace feature to organize them clearly.
  • Check error pins: Always handle the success and error pins on blocks that interact with storage, external APIs, or other operations that can fail.
  • Offload heavy work: Use the queue for tasks like PDF generation, email sending, or batch processing that don't need to complete immediately.
  • Use caching: Leverage Redis cache to avoid repeated storage queries. See Common Pitfalls for guidance on proper cache usage.

Frequently asked

In what order do blocks execute in a RUAL blueprint?

Execution follows the flow pin connections one block at a time. When a block has several flow outputs, or one flow output is wired to several blocks, they run one after another, and each path runs to its end before the next one starts, never in parallel. Different flow outputs run in the order of their pin ids, and several wires on one output in the order they are saved in. To run work concurrently, use parallel_functions, function_run_parallel or the concurrency pin of function_foreach or array_map.

When are data-only blocks evaluated in RUAL?

Blocks without a flow in-pin, such as value_default, condition_is_true or query_bool_filter, are evaluated on demand when the block they connect to needs their data, resolved as part of the execution chain.

Does RUAL have a try/catch mechanism?

No. Each block handles its own errors and exposes the result through a success condition pin and an error value pin, and the flow usually does not stop on failure. Only a flow block that cannot execute at all ends the run, with RUAL_SYSTEM_ERROR on its error pin. It is the developer's responsibility to check those pins on every block that can fail.

What is precompiled data in RUAL?

Each node prepares a blueprint's structure once and keeps it in memory. Block values are not kept: every run computes its own, and within one run a data block is computed at most once. A save refreshes the prepared blueprint on every node, so the first request on each node after a save is the expensive one. There is no idle expiry.