docs/advanced/architecture.mdx
Understanding the iii architecture helps you build more efficient and scalable applications.
The iii architecture consists of three main layers:
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
The Worker Registry tracks connected workers and their registered functions:
| Component | Description |
|---|---|
WorkerRegistry | Thread-safe map storing active workers by UUID |
Worker | Represents a connected client with WebSocket channel |
function_ids | Set of function IDs the worker can execute |
invocations | Active request IDs being processed |
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
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
Core Modules implement the CoreModule trait and bridge external protocols to internal function calls.
Maps HTTP routes to internal function paths using a hot router:
Manages real-time state and WebSocket connections:
Implements publish-subscribe pattern:
Handles distributed scheduling:
graph LR
Client[Client] -->|HTTP| Engine[Engine]
Engine -->|Invoke| Worker[Worker]
Worker -->|Result| Engine
Engine -->|Response| Client
graph TD
Publisher[Publisher] -->|Emit| Engine[Engine]
Engine -->|Store| Redis[(Redis)]
Redis -.->|Notify| Engine
Engine -->|Invoke| Sub1[Subscriber 1]
Engine -->|Invoke| Sub2[Subscriber 2]
graph TD
Scheduler[Cron Scheduler] -->|Tick| Lock{Acquire Lock?}
Lock -->|Success| Execute[Execute Function]
Lock -->|Fail| Skip[Skip Execution]
Execute -->|Complete| Release[Release Lock]
Run multiple engine instances with shared Redis:
Optimize single instance performance:
The SDK connection lifecycle manages initialization, function registration, and automatic reconnection with the engine.
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
All three SDK packages connect to the same iii Engine using a shared JSON-over-WebSocket wire protocol.
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
Build custom core modules by implementing the CoreModule trait:
Replace default adapters with custom implementations: