Back to Iii

Architecture Overview

docs/advanced/architecture.mdx

0.13.08.2 KB
Original Source

Understanding the iii architecture helps you build more efficient and scalable applications.

System Components

The iii architecture consists of three main layers:

  1. Engine Core - Manages worker connections, routing, and module lifecycle
  2. Core Modules - Provide interfaces to external systems (HTTP, Redis, WebSocket)
  3. Workers - External processes that execute business logic
mermaid
graph TD
    subgraph "External World"
        Client[HTTP Client]
        Redis[(Redis)]
        User[WebSocket
User]
    end

    subgraph "iii Engine Process"
        Core[Engine Core]
        Reg[Worker
Registry]

        subgraph "Core Modules"
            API[RestApiModule]
            Stream[StreamModule]
            Log[OtelModule]
            Queue[QueueModule]
            Cron[CronModule]
        end
    end

    subgraph "Worker Processes"
        W1[Node.js
Worker]
        W2[Python
Worker]
    end

    Client -->|HTTP
Request| API
    User -->|WS
Message| Stream

    API --> Core
    Stream --> Core

    Core -->|Lookup| Reg
    Core -->|Invoke
Function| W1
    Core -->|Invoke
Function| W2

    W1 -->|Register| Core
    W2 -->|Register| Core

    Queue -.->|Persist/Sub| Redis
    Stream -.->|State| Redis
    Cron -.->|Locks| Redis
    Log -.->|Store| Redis

Worker Registry

The Worker Registry tracks connected workers and their registered functions:

ComponentDescription
WorkerRegistryThread-safe map storing active workers by UUID
WorkerRepresents a connected client with WebSocket channel
function_idsSet of function IDs the worker can execute
invocationsActive request IDs being processed

Communication Flow

HTTP Request to Worker

mermaid
sequenceDiagram
    participant C as Client
    participant API as RestAPI
    participant E as Engine
    participant W as Worker

    Note over W,E: Initialization Phase
    W->>E: Connect WebSocket
    W->>E: Register Function (api.echo)
    W->>E: Register Trigger (POST /echo)
    E->>API: Setup Route /echo

    Note over C,W: Runtime Phase
    C->>+API: POST /echo {data}
    API->>+E: Route Request
    E->>E: Lookup Worker for 'api.echo'
    E->>+W: Invoke 'api.echo' (Payload)
    W-->>-E: InvocationResult (JSON)
    E-->>-API: Response Body
    API-->>-C: HTTP 200 OK

Event Publishing

mermaid
sequenceDiagram
    participant W1 as Worker1
    participant E as Engine
    participant R as Redis
    participant W2 as Worker2

    W1->>E: Invoke enqueue
    E->>R: Enqueue to Queue
    R-->>E: Enqueued

    R->>E: Notify Subscribers
    E->>W2: Dequeue from Queue
    W2-->>E: Processing Complete

Module Architecture

Core Modules implement the CoreModule trait and bridge external protocols to internal function calls.

HTTP Module

Maps HTTP routes to internal function paths using a hot router:

  • Dynamic Registration: Routes can be added at runtime
  • Path Router: Links HTTP method + path to function
  • Request Mapping: Converts HTTP requests to function invocations

Streams Module

Manages real-time state and WebSocket connections:

  • State Management: Get/set/delete operations on hierarchical data
  • Real-time Sync: WebSocket-based state synchronization
  • Authentication: Optional auth function for connection validation

Queue Module

Implements publish-subscribe pattern:

  • Adapters: Pluggable backends (Redis, RabbitMQ, Built-in)
  • Topics: Subscribe to specific event topics
  • Async Processing: Non-blocking event distribution

Cron Module

Handles distributed scheduling:

  • Cron Parser: Supports standard cron expressions
  • Distributed Locks: Prevents duplicate execution across instances
  • Job Management: Tokio-based task scheduling

Data Flow Patterns

Synchronous Pattern (API Requests)

mermaid
graph LR
    Client[Client] -->|HTTP| Engine[Engine]
    Engine -->|Invoke| Worker[Worker]
    Worker -->|Result| Engine
    Engine -->|Response| Client

Asynchronous Pattern (Events)

mermaid
graph TD
    Publisher[Publisher] -->|Emit| Engine[Engine]
    Engine -->|Store| Redis[(Redis)]
    Redis -.->|Notify| Engine
    Engine -->|Invoke| Sub1[Subscriber 1]
    Engine -->|Invoke| Sub2[Subscriber 2]

Scheduled Pattern (Cron)

mermaid
graph TD
    Scheduler[Cron Scheduler] -->|Tick| Lock{Acquire Lock?}
    Lock -->|Success| Execute[Execute Function]
    Lock -->|Fail| Skip[Skip Execution]
    Execute -->|Complete| Release[Release Lock]

Scalability Considerations

Horizontal Scaling

Run multiple engine instances with shared Redis:

  • Stateless Workers: Connect to any engine instance
  • Shared State: Redis provides distributed state
  • Load Distribution: HTTP load balancer for API requests
  • Lock-based Coordination: Cron jobs use distributed locks

Vertical Scaling

Optimize single instance performance:

  • Rust Performance: Core modules built for speed and memory efficiency
  • Async Runtime: Tokio for concurrent request handling
  • Connection Pooling: Efficient WebSocket management
  • Adapter Optimization: Redis pipelining, connection pooling

SDK Connection Flow

The SDK connection lifecycle manages initialization, function registration, and automatic reconnection with the engine.

mermaid
sequenceDiagram
    participant SDK as SDK Client
    participant WS as WebSocket
    participant E as Engine
    participant R as Function Registry

    Note over SDK,R: Initialization Phase
    SDK->>WS: registerWorker(url)
    WS->>E: WebSocket CONNECT
    E->>E: Generate worker_id
    E-->>WS: Connected (worker_id)
    WS-->>SDK: Connection Established

    Note over SDK,R: Function Registration
    SDK->>E: Register Function (fn_a)
    E->>R: Store fn_a -> worker_id
    SDK->>E: Register Function (fn_b)
    E->>R: Store fn_b -> worker_id
    E-->>SDK: Registration Confirmed

    Note over SDK,E: Heartbeat Loop
    loop Every 30s
        SDK->>E: Ping
        E-->>SDK: Pong
    end

    Note over SDK,R: Disconnection and Reconnection
    WS--xE: Connection Lost
    E->>R: Remove worker_id entries

    loop Exponential Backoff
        SDK->>WS: Reconnect (1s, 2s, 4s, 8s...)
        WS--xE: Connection Failed
    end

    SDK->>WS: Reconnect
    WS->>E: WebSocket CONNECT
    E->>E: Generate new worker_id
    E-->>SDK: Connected (new worker_id)

    Note over SDK,R: Re-registration
    SDK->>E: Register Function (fn_a)
    E->>R: Store fn_a -> new worker_id
    SDK->>E: Register Function (fn_b)
    E->>R: Store fn_b -> new worker_id
    E-->>SDK: Registration Confirmed

Package Architecture

All three SDK packages connect to the same iii Engine using a shared JSON-over-WebSocket wire protocol.

mermaid
graph TD
    subgraph NodeJS["Node.js SDK (iii-sdk)"]
        NodeSDK["iii-sdk"]
        ws["ws"]
        otelNode["opentelemetry/sdk-node"]
        otelApi["opentelemetry/api"]
        NodeSDK --> ws
        NodeSDK --> otelNode
        NodeSDK --> otelApi
    end

    subgraph Python["Python SDK (iii)"]
        PySDK["iii"]
        websockets["websockets"]
        otelPy["opentelemetry-sdk"]
        otelPyApi["opentelemetry-api"]
        PySDK --> websockets
        PySDK --> otelPy
        PySDK --> otelPyApi
    end

    subgraph Rust["Rust SDK (iii-sdk)"]
        RustSDK["iii-sdk"]
        tokioWS["tokio-tungstenite"]
        otelRust["opentelemetry"]
        RustSDK --> tokioWS
        RustSDK --> otelRust
    end

    subgraph Protocol["Wire Protocol"]
        Proto["JSON over WebSocket"]
    end

    subgraph Core["iii Engine"]
        Engine["Engine Core"]
    end

    NodeSDK -->|WebSocket| Proto
    PySDK -->|WebSocket| Proto
    RustSDK -->|WebSocket| Proto
    Proto --> Engine

Extension Points

Custom Modules

Build custom core modules by implementing the CoreModule trait:

  1. Define module configuration
  2. Implement initialization logic
  3. Register trigger types
  4. Expose functions to workers

Custom Adapters

Replace default adapters with custom implementations:

  • Event Adapters: Alternative message brokers (RabbitMQ, Kafka)
  • Stream Adapters: Different storage backends
  • Cron Adapters: Alternative scheduling systems
  • Logging Adapters: Custom log destinations