11 KiB
Architecture: Circle Management System
1. Introduction & Overview
This document outlines the architecture for a system that manages multiple "Circles." Each Circle is an independent entity comprising its own database, a Rhai scripting engine, a dedicated Rhai worker process, and a WebSocket (WS) server for external interaction. A central command-line orchestrator will be responsible for initializing, running, and monitoring these Circles based on a configuration file.
The primary goal is to allow users to define multiple isolated Rhai environments, each accessible via a unique WebSocket endpoint, and for scripts executed within a circle to interact with that circle's dedicated persistent storage.
2. Goals
- Create a command-line application (
circles_orchestrator
) to manage the lifecycle of multiple Circles. - Each Circle will have:
- An independent
OurDB
instance for data persistence. - A dedicated
Rhai Engine
configured with itsOurDB
. - A dedicated
Rhai Worker
processing scripts for that circle. - A dedicated
WebSocket Server
exposing an endpoint for that circle.
- An independent
- Circle configurations (name, ID, port) will be loaded from a
circles.json
file. - The orchestrator will display a status table of all running circles, including their worker queue and WS server URL.
- Utilize existing crates (
ourdb
,rhai_engine
,rhai_worker
,rhai_client
,server_ws
) with necessary refactoring to support library usage.
3. System Components
- Orchestrator (
circles_orchestrator
):- Location:
/Users/timurgordon/code/git.ourworld.tf/herocode/circles/cmd/src/main.rs
- Role: Parses
circles.json
, initializes, spawns, and monitors all components for each defined circle. Displays system status.
- Location:
- Circle Configuration (
circles.json
):- Location: e.g.,
/Users/timurgordon/code/git.ourworld.tf/herocode/circles/cmd/circles.json
- Role: Defines the set of circles to be managed, including their ID, name, and port.
- Location: e.g.,
- OurDB (Per Circle):
- Library:
/Users/timurgordon/code/git.ourworld.tf/herocode/db/ourdb/src/lib.rs
- Role: Provides persistent key-value storage for each circle, configured in incremental mode. Instance data stored at
~/.hero/circles/{id}/
.
- Library:
- Rhai Engine (Per Circle):
- Library:
/Users/timurgordon/code/git.ourworld.tf/herocode/rhailib/src/engine/src/lib.rs
- Role: Provides the Rhai scripting environment for a circle, configured with the circle's specific
OurDB
instance.
- Library:
- Rhai Worker (Per Circle):
- Library:
/Users/timurgordon/code/git.ourworld.tf/herocode/rhailib/src/worker/src/lib.rs
- Role: Executes Rhai scripts for a specific circle. Listens on a dedicated Redis queue for tasks and uses the circle's
Rhai Engine
.
- Library:
- Rhai Client (Per Circle WS Server):
- Library:
/Users/timurgordon/code/git.ourworld.tf/herocode/rhailib/src/client/src/lib.rs
- Role: Used by a
Circle WebSocket Server
to send script execution tasks to its correspondingRhai Worker
via Redis.
- Library:
- Circle WebSocket Server (Per Circle):
- Library:
/Users/timurgordon/code/git.ourworld.tf/herocode/circles/server_ws/src/lib.rs
- Role: Exposes a WebSocket endpoint for a specific circle, allowing external clients to submit Rhai scripts for execution within that circle.
- Library:
- Redis:
- URL:
redis://127.0.0.1:6379
- Role: Acts as the message broker between
Rhai Clients
(within WS Servers) andRhai Workers
.
- URL:
4. High-Level Design Aspects
4.1. Orchestrator Logic (Conceptual)
The circles_orchestrator
reads the circles.json
configuration. For each defined circle, it:
- Determines the
OurDB
path based on the circle's ID. - Initializes the
OurDB
instance. - Creates a
Rhai Engine
configured with thisOurDB
. - Spawns a
Rhai Worker
task, providing it with the engine and the circle's identity (for Redis queue naming). - Spawns a
Circle WebSocket Server
task, providing it with the circle's identity and port. It then monitors these components and displays their status.
4.2. Database Path Convention
- Base: System user's home directory (e.g.,
/Users/username
or/home/username
). - Structure:
~/.hero/circles/{CIRCLE_ID}/
- Example: For a circle with ID
1
, the database path would be~/.hero/circles/1/
.
4.3. Configuration File Format (circles.json
)
A JSON array of objects. Each object represents a circle:
id
:u32
- Unique identifier.name
:String
- Human-readable name.port
:u16
- Port for the WebSocket server.
[
{ "id": 1, "name": "Alpha Circle", "port": 8081 },
{ "id": 2, "name": "Beta Circle", "port": 8082 }
]
4.4. Conceptual Output Table Format (Orchestrator)
Circle Name | ID | Worker Status | Worker Queues | WS Server URL |
---|---|---|---|---|
Alpha Circle | 1 | Running | rhai_tasks:alpha_circle |
ws://127.0.0.1:8081/ws |
4.5. Interaction Flow (Single Circle)
- An external client connects to a specific Circle's WebSocket Server.
- The client sends a Rhai script via a JSON-RPC message.
- The WS Server uses its embedded
Rhai Client
to publish the script and task details to a Redis queue specific to that circle (e.g.,rhai_tasks:alpha_circle
). - The
Rhai Worker
for that circle picks up the task from its Redis queue. - The Worker uses its
Rhai Engine
(which is configured with the circle'sOurDB
) to execute the script. - Any database interactions within the script go through the circle's
OurDB
. - The Worker updates the task status and result/error in Redis.
- The
Rhai Client
(in the WS Server), which has been polling Redis for the result, receives the update. - The WS Server sends the script's result or error back to the external client via WebSocket.
5. Diagrams
5.1. Component Diagram
graph TD
UserInterface[User/Admin] -- Manages --> OrchestratorCli{circles_orchestrator}
OrchestratorCli -- Reads --> CirclesJson[circles.json]
subgraph Circle 1
direction LR
OrchestratorCli -- Spawns/Manages --> C1_OurDB[(OurDB @ ~/.hero/circles/1)]
OrchestratorCli -- Spawns/Manages --> C1_RhaiEngine[Rhai Engine 1]
OrchestratorCli -- Spawns/Manages --> C1_RhaiWorker[Rhai Worker 1]
OrchestratorCli -- Spawns/Manages --> C1_WSServer[WS Server 1 @ Port 8081]
C1_RhaiEngine -- Uses --> C1_OurDB
C1_RhaiWorker -- Uses --> C1_RhaiEngine
C1_WSServer -- Contains --> C1_RhaiClient[Rhai Client 1]
end
subgraph Circle 2
direction LR
OrchestratorCli -- Spawns/Manages --> C2_OurDB[(OurDB @ ~/.hero/circles/2)]
OrchestratorCli -- Spawns/Manages --> C2_RhaiEngine[Rhai Engine 2]
OrchestratorCli -- Spawns/Manages --> C2_RhaiWorker[Rhai Worker 2]
OrchestratorCli -- Spawns/Manages --> C2_WSServer[WS Server 2 @ Port 8082]
C2_RhaiEngine -- Uses --> C2_OurDB
C2_RhaiWorker -- Uses --> C2_RhaiEngine
C2_WSServer -- Contains --> C2_RhaiClient[Rhai Client 2]
end
C1_RhaiWorker -- Listens/Publishes --> Redis[(Redis @ 127.0.0.1:6379)]
C1_RhaiClient -- Publishes/Subscribes --> Redis
C2_RhaiWorker -- Listens/Publishes --> Redis
C2_RhaiClient -- Publishes/Subscribes --> Redis
ExternalWSClient1[External WS Client] -- Connects --> C1_WSServer
ExternalWSClient2[External WS Client] -- Connects --> C2_WSServer
5.2. Sequence Diagram (Request Flow for one Circle)
sequenceDiagram
participant ExtWSClient as External WS Client
participant CircleWSServer as Circle WS Server (e.g., Port 8081)
participant RhaiClientLib as Rhai Client Library (in WS Server)
participant RedisBroker as Redis
participant RhaiWorker as Rhai Worker (for the circle)
participant RhaiEngineLib as Rhai Engine Library (in Worker)
participant CircleOurDB as OurDB (for the circle)
ExtWSClient ->>+ CircleWSServer: Send Rhai Script (JSON-RPC "play" over WS)
CircleWSServer ->>+ RhaiClientLib: submit_script_and_await_result(circle_name, script, ...)
RhaiClientLib ->>+ RedisBroker: LPUSH rhai_tasks:circle_name (task_id)
RhaiClientLib ->>+ RedisBroker: HSET rhai_task_details:task_id (script details)
RhaiClientLib -->>- CircleWSServer: Returns task_id (internally starts polling)
RhaiWorker ->>+ RedisBroker: BLPOP rhai_tasks:circle_name (blocks)
RedisBroker -->>- RhaiWorker: Returns task_id
RhaiWorker ->>+ RedisBroker: HGETALL rhai_task_details:task_id
RedisBroker -->>- RhaiWorker: Returns script details
RhaiWorker ->>+ RhaiEngineLib: eval_with_scope(script)
RhaiEngineLib ->>+ CircleOurDB: DB Operations (if script interacts with DB)
CircleOurDB -->>- RhaiEngineLib: DB Results
RhaiEngineLib -->>- RhaiWorker: Script Result/Error
RhaiWorker ->>+ RedisBroker: HSET rhai_task_details:task_id (status="completed/error", output/error)
RhaiClientLib ->>+ RedisBroker: HGETALL rhai_task_details:task_id (polling)
RedisBroker -->>- RhaiClientLib: Task details (status, output/error)
alt Task Completed
RhaiClientLib -->>- CircleWSServer: Result (output)
CircleWSServer -->>- ExtWSClient: WS Response (JSON-RPC with result)
else Task Errored
RhaiClientLib -->>- CircleWSServer: Error (error message)
CircleWSServer -->>- ExtWSClient: WS Response (JSON-RPC with error)
else Timeout
RhaiClientLib -->>- CircleWSServer: Timeout Error
CircleWSServer -->>- ExtWSClient: WS Response (JSON-RPC with timeout error)
end
5.3. Conceptual Directory Structure
/Users/timurgordon/code/git.ourworld.tf/herocode/
├── circles/
│ ├── cmd/ <-- NEW Orchestrator Crate
│ │ ├── Cargo.toml
│ │ ├── circles.json (example config)
│ │ └── src/
│ │ └── main.rs (Orchestrator logic)
│ ├── server_ws/ <-- EXISTING, to be refactored to lib
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs (WebSocket server library logic)
│ ├── ARCHITECTURE.md (This document)
│ └── README.md
├── db/
│ └── ourdb/
│ └── src/lib.rs (Existing OurDB library)
└── rhailib/ <-- Current Workspace (contains other existing libs)
├── src/
│ ├── client/
│ │ └── src/lib.rs (Existing Rhai Client library)
│ ├── engine/
│ │ └── src/lib.rs (Existing Rhai Engine library)
│ └── worker/
│ └── src/lib.rs (Existing Rhai Worker library, to be refactored)
├── Cargo.toml
└── ...