rhailib/_archive/dispatcher/README.md
2025-08-06 15:09:52 +02:00

4.8 KiB

Rhai Client

The rhai-client crate provides a fluent builder-based interface for submitting Rhai scripts to a distributed task execution system over Redis. It enables applications to offload Rhai script execution to one or more worker services and await the results.

Features

  • Fluent Builder API: A RhaiDispatcherBuilder for easy client configuration and a PlayRequestBuilder for constructing and submitting script execution requests.
  • Asynchronous Operations: Built with tokio for non-blocking I/O.
  • Request-Reply Pattern: Submits tasks and awaits results on a dedicated reply queue, eliminating the need for polling.
  • Configurable Timeouts: Set timeouts for how long the client should wait for a task to complete.
  • Direct-to-Worker-Queue Submission: Tasks are sent to a queue named after the worker_id, allowing for direct and clear task routing.
  • Manual Status Check: Provides an option to manually check the status of a task by its ID.

Core Components

  • RhaiDispatcherBuilder: A builder to construct a RhaiDispatcher. Requires a caller_id and Redis URL.
  • RhaiDispatcher: The main client for interacting with the task system. It's used to create PlayRequestBuilder instances.
  • PlayRequestBuilder: A fluent builder for creating and dispatching a script execution request. You can set:
    • worker_id: The ID of the worker queue to send the task to.
    • script or script_path: The Rhai script to execute.
    • request_id: An optional unique ID for the request.
    • timeout: How long to wait for a result.
  • Submission Methods:
    • submit(): Submits the request and returns immediately (fire-and-forget).
    • await_response(): Submits the request and waits for the result or a timeout.
  • RhaiTaskDetails: A struct representing the details of a task, including its script, status (pending, processing, completed, error), output, and error messages.
  • RhaiDispatcherError: An enum for various errors, such as Redis errors, serialization issues, or task timeouts.

How It Works

  1. A RhaiDispatcher is created using the RhaiDispatcherBuilder, configured with a caller_id and Redis URL.
  2. A PlayRequestBuilder is created from the client.
  3. The script, worker_id, and an optional timeout are configured on the builder.
  4. When await_response() is called: a. A unique task_id (UUID v4) is generated. b. Task details are stored in a Redis hash with a key like rhailib:<task_id>. c. The task_id is pushed to the worker's queue, named rhailib:<worker_id>. d. The client performs a blocking pop (BLPOP) on a dedicated reply queue (rhailib:reply:<task_id>), waiting for the worker to send the result.
  5. A rhai-worker process, listening on the rhailib:<worker_id> queue, picks up the task, executes it, and pushes the final RhaiTaskDetails to the reply queue.
  6. The client receives the result from the reply queue and returns it to the caller.

Prerequisites

  • A running Redis instance accessible by the client and the worker services.

Usage Example

The following example demonstrates how to build a client, submit a script, and wait for the result.

use rhai_dispatcher::{RhaiDispatcherBuilder, RhaiDispatcherError};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();

    // 1. Build the client
    let client = RhaiDispatcherBuilder::new()
        .caller_id("my-app-instance-1")
        .redis_url("redis://127.0.0.1/")
        .build()?;

    // 2. Define the script and target worker
    let script = r#" "Hello, " + worker_id + "!" "#;
    let worker_id = "worker-1";

    // 3. Use the PlayRequestBuilder to configure and submit the request
    let result = client
        .new_play_request()
        .worker_id(worker_id)
        .script(script)
        .timeout(Duration::from_secs(5))
        .await_response()
        .await;

    match result {
        Ok(details) => {
            log::info!("Task completed successfully!");
            log::info!("Status: {}", details.status);
            if let Some(output) = details.output {
                log::info!("Output: {}", output);
            }
        }
        Err(RhaiDispatcherError::Timeout(task_id)) => {
            log::error!("Task {} timed out.", task_id);
        }
        Err(e) => {
            log::error!("An unexpected error occurred: {}", e);
        }
    }

    Ok(())
}

Refer to the examples/ directory for more specific use cases, such as timeout_example.rs which tests the timeout mechanism.

Building and Running Examples

To run an example (e.g., timeout_example):

cd src/client # (or wherever this client's Cargo.toml is)
cargo run --example timeout_example

Ensure a Redis server is running and accessible at redis://127.0.0.1/.