21 — Advanced

Async Execution

RuleSets and RuleFlows can be executed asynchronously using CompletableFuture — both implement the AsyncRunnable interface (new in 2.0.0). This enables non-blocking rule evaluation and parallel execution, leveraging the ExecutorService configured in the RuleContext.

Preferred: Async Inside a RuleFlow

The recommended way to run rules asynchronously is the RuleFlow async pipelineasyncRun(...) launches steps in parallel and await / awaitAll / awaitAny coordinate them, with binding isolation and exception handling built in:

RuleFlow<RuleContext> flow = RuleFlow.builder()
    .name("ParallelChecks")
    .asyncRun(pricingRuleSet, spec -> spec
        .as("pricingFuture")
        .withImmutableBindings())          // isolated snapshot — no races
    .asyncRun(inventoryRuleSet, spec -> spec
        .as("inventoryFuture")
        .withImmutableBindings())
    .awaitAll("pricingFuture", "inventoryFuture")
    .build();

flow.run(order -> myOrder);

Default Executor

By default, rulii uses a fixed thread pool sized to the number of available processors. This provides sensible concurrency without requiring explicit configuration.

Direct Async Execution (Low-Level)

For one-off cases, runAsync(...) is available directly on a RuleSet or RuleFlow:

RuleSet<?> ruleSet = RuleSet.builder()
    .with("AsyncRules")
    .rule(/* ... rules ... */)
    .build();

Bindings bindings = Bindings.builder().standard();
bindings.bind("name", "Alice");

// Run asynchronously
CompletableFuture<?> future = ruleSet.runAsync(bindings);

// Wait for result
future.join();

// Or use callbacks
future.thenAccept(result -> {
    System.out.println("Rules completed!");
});

Custom ExecutorService

You can provide your own ExecutorService to control the thread pool size and behavior:

ExecutorService executor = Executors.newFixedThreadPool(4);

RuleContext ctx = RuleContext.builder()
    .executorService(executor)
    .build(bindings);

CompletableFuture<?> future = ruleSet.runAsync(ctx);

Use Cases

Parallel Rule Sets

Run independent rule sets concurrently for faster evaluation

Non-Blocking Validation

Validate inputs without blocking the calling thread

Thread Safety Warning

Caution: Shared Mutable Bindings

Never reuse the same RuleContext for concurrent runAsync invocations — execution mutates the context's binding scope stack, and concurrent executions can corrupt or discard each other's scopes. Use a separate RuleContext per concurrent run, or better, orchestrate the parallel work inside a RuleFlow with asyncRun(...).withImmutableBindings(), which isolates each task automatically.