# Core Concepts Understanding the fundamental concepts of Huly Virtual Network is essential for building robust distributed systems. ## Table of Contents - [Architecture Overview](#architecture-overview) - [Network](#network) - [Agents](#agents) - [Containers](#containers) - [Clients](#clients) - [Communication Patterns](#communication-patterns) - [Lifecycle Management](#lifecycle-management) - [Endpoint References](#endpoint-references) - [Container Kinds and Labels](#container-kinds-and-labels) ## Architecture Overview Huly Network uses a hub-and-spoke architecture with three main components: ```mermaid graph TB subgraph "Network (Hub)" N[Network Server] R[Router] REG[Container Registry] end subgraph "Agent 1" A1[Agent] C1A[Container A] C1B[Container B] end subgraph "Agent 2" A2[Agent] C2A[Container C] C2B[Container D] end subgraph "Clients" CLIENT1[Client 1] CLIENT2[Client 2] end CLIENT1 -->|Request Container| N CLIENT2 -->|Request Container| N N -->|Route to Agent| A1 N -->|Route to Agent| A2 A1 --> C1A A1 --> C1B A2 --> C2A A2 --> C2B ``` ### Key Principles 1. **Centralized Coordination**: The network server coordinates all agents and containers 2. **Distributed Execution**: Containers run on agents, which can be distributed across multiple machines 3. **Dynamic Discovery**: Clients discover and connect to containers through the network 4. **Automatic Lifecycle**: The network manages container lifecycles based on client references 5. **Fault Tolerance**: Automatic cleanup of failed agents and orphaned containers ## Network The **Network** is the central coordinator that: - Maintains a registry of all agents and their capabilities - Tracks all active containers and their locations - Routes client requests to appropriate containers - Manages container lifecycles (creation, reference counting, cleanup) - Provides service discovery and load balancing - Broadcasts events to clients about system changes ### Network Responsibilities ```typescript interface Network { // Agent management register(record: AgentRecord, agent: NetworkAgentApi): Promise unregister(agentId: AgentUuid): Promise ping(agentId: AgentUuid): Promise // Container management get(client: ClientUuid, kind: ContainerKind, options: GetOptions): Promise<[ContainerUuid, ContainerEndpointRef]> release(client: ClientUuid, uuid: ContainerUuid): Promise list(kind?: ContainerKind): Promise // Communication request(target: ContainerUuid, operation: string, data?: any): Promise // Discovery agents(): AgentRecord[] kinds(): ContainerKind[] } ``` ### Network Server The network server listens on a TCP port (default: 3737) and: - Accepts connections from clients and agents - Uses ZeroMQ for high-performance messaging - Handles concurrent requests efficiently - Maintains connection health through ping/pong **Example: Starting a Network Server** ```typescript import { NetworkImpl, TickManagerImpl } from '@hcengineering/network-core' import { NetworkServer } from '@hcengineering/network-server' const tickManager = new TickManagerImpl(1000) // 1000 ticks/sec tickManager.start() const network = new NetworkImpl(tickManager) const server = new NetworkServer( network, tickManager, '*', // Bind to all interfaces 3737 // Port ) console.log('Network server running on port 3737') ``` ## Agents **Agents** are worker processes that: - Host and manage containers - Register with the network to advertise capabilities - Create containers on-demand or pre-provision them (stateless) - Handle container lifecycle (start, stop, health checks) - Route requests between clients and containers ### Agent Capabilities Each agent declares which **container kinds** it can create: ```typescript // Note: For production code, use serveAgent() on the client // This example uses AgentImpl directly for educational purposes const agent = new AgentImpl('my-agent' as AgentUuid, { session: async (options) => { /* create session container */ }, workspace: async (options) => { /* create workspace container */ }, query: async (options) => { /* create query container */ } }) ``` ### Agent Registration Agents register with the network to announce availability: ```typescript import { NetworkAgentServer } from '@hcengineering/network-client' const agentServer = new NetworkAgentServer( tickManager, 'localhost', // Network host '*', // Bind address 3738 // Agent port for container connections ) await agentServer.start(agent) // Register with network await client.register(agent) ``` ### Agent Health Agents must: - Send regular pings to the network (default: every 1 second) - Respond to health checks - Handle graceful shutdown - Clean up containers on termination If an agent fails to ping within the `aliveTimeout` (default: 3 seconds), the network: 1. Marks the agent as dead 2. Removes all its containers 3. Broadcasts removal events 4. Allows standby agents to take over (for stateless containers) ## Containers **Containers** are the workhorses of the system. They: - Implement specific business logic - Process requests from clients - Maintain internal state - Broadcast events to connected clients - Are automatically created and destroyed based on demand ### Container Interface Every container must implement: ```typescript interface Container { // Handle incoming requests request(operation: string, data?: any, clientId?: ClientUuid): Promise // Health check ping(): Promise // Cleanup and shutdown terminate(): Promise // Event broadcasting support connect(clientId: ClientUuid, broadcast: (data: any) => Promise): void disconnect(clientId: ClientUuid): void // Optional termination callback onTerminated?(): void } ``` ### Container Lifecycle ```mermaid stateDiagram-v2 [*] --> Creating: Client requests container Creating --> Active: Factory creates instance Active --> Referenced: Client holds reference Referenced --> Active: Client releases reference Active --> Terminating: Timeout expires (no references) Referenced --> Terminating: All clients released Terminating --> [*]: Cleanup complete ``` **Key lifecycle events:** 1. **Creation**: Factory method called when container is requested 2. **Active**: Container is running and processing requests 3. **Referenced**: At least one client holds a reference 4. **Idle Timeout**: Container kept alive for `containerTimeout` after last reference released 5. **Termination**: `terminate()` called, then removed from network ### Container Types #### Stateful Containers (Dynamic) Created on-demand when requested: ```typescript // Note: For production code, use serveAgent() on the client // This example uses AgentImpl directly for educational purposes const agent = new AgentImpl('agent-1' as any, { 'user-session': async (options: GetOptions) => { const uuid = options.uuid ?? generateUuid() const container = new UserSessionContainer(uuid, options) return { uuid, container, endpoint: `session://agent1/${uuid}` as any } } }) ``` #### Stateless Containers (Pre-provisioned) Pre-created for high availability: ```typescript const leaderContainer = new LeaderContainer('leader-001' as ContainerUuid) agent.addStatelessContainer( 'leader-001' as ContainerUuid, 'leader' as ContainerKind, 'leader://agent1/leader-001' as ContainerEndpointRef, leaderContainer ) ``` Multiple agents can register the same stateless container UUID. The network accepts the first and rejects others, enabling automatic failover. ### Container Example ```typescript import type { Container, ContainerUuid, ClientUuid } from '@hcengineering/network-core' class DataStoreContainer implements Container { private data = new Map() private connections = new Map Promise>() constructor(readonly uuid: ContainerUuid) {} async request(operation: string, data?: any): Promise { switch (operation) { case 'set': this.data.set(data.key, data.value) await this.broadcast({ type: 'dataChanged', key: data.key }) return { success: true } case 'get': return { value: this.data.get(data.key) } default: return { error: 'Unknown operation' } } } async ping(): Promise {} async terminate(): Promise { this.data.clear() this.connections.clear() } connect(clientId: ClientUuid, broadcast: (data: any) => Promise): void { this.connections.set(clientId, broadcast) } disconnect(clientId: ClientUuid): void { this.connections.delete(clientId) } private async broadcast(event: any): Promise { const promises = Array.from(this.connections.values()).map((fn) => fn(event)) await Promise.all(promises) } } ``` ## Clients **Clients** are applications or services that: - Connect to the network server - Request containers by kind and optional criteria - Send requests to containers - Receive events from containers - Manage container references (acquire/release) ### Client Connection ```typescript import { createNetworkClient } from '@hcengineering/network-client' const client = createNetworkClient( 'localhost:3737', // Network address 3600 // Alive timeout in seconds (optional) ) await client.waitConnection(5000) // Wait up to 5 seconds ``` ### Requesting Containers Clients request containers by **kind** and optional **criteria**: ```typescript // Get any container of this kind const ref = await client.get('user-session' as ContainerKind, {}) // Get specific container by UUID const ref = await client.get('user-session' as ContainerKind, { uuid: 'session-123' as ContainerUuid }) // Get container with labels const ref = await client.get('workspace' as ContainerKind, { labels: ['premium', 'us-west'] }) // Get container with extra data const ref = await client.get('query-engine' as ContainerKind, { extra: { database: 'analytics', userId: 'user-456' } }) ``` ### Client API ```typescript interface NetworkClient { // Container management get(kind: ContainerKind, request: GetOptions): Promise list(kind?: ContainerKind): Promise // Agent management register(agent: NetworkAgent): Promise unregister(agentId: AgentUuid): Promise // Discovery agents(): AgentRecord[] kinds(): ContainerKind[] // Events onUpdate(listener: NetworkUpdateListener): () => void // Connection close(): Promise } ``` ## Communication Patterns Huly Network supports multiple communication patterns: ### 1. Request/Response (Synchronous) Direct request to container with response: ```typescript const result = await containerRef.request('processData', { value: 42 }) console.log(result) // { processed: true, result: 84 } ``` ### 2. Fire-and-Forget (Asynchronous) Send data without waiting for response: ```typescript await containerRef.request('logEvent', { event: 'user_login', timestamp: Date.now() }) ``` ### 3. Event Broadcasting (Publish/Subscribe) Container broadcasts events to all connected clients: ```typescript // Client side const connection = await containerRef.connect() connection.on = async (event) => { console.log('Received:', event) } // Container side connect(clientId: ClientUuid, broadcast: (data: any) => Promise): void { this.clients.set(clientId, broadcast) } // Broadcast to all clients for (const broadcast of this.clients.values()) { await broadcast({ type: 'update', data: changes }) } ``` ### 4. Bidirectional Streaming Establish persistent connection for streaming: ```typescript const connection = await containerRef.connect() // Receive stream connection.on = async (chunk) => { console.log('Chunk:', chunk) } // Send requests await connection.request('subscribe', { topic: 'updates' }) await connection.request('getData', { range: [0, 100] }) ``` ## Lifecycle Management ### Reference Counting The network uses reference counting to manage container lifecycles: - Each `client.get()` increments the reference count - Each `containerRef.close()` decrements it - Containers with zero references are kept alive for `containerTimeout` - After timeout, containers are automatically terminated ```typescript // Acquire reference (ref count = 1) const ref1 = await client.get('service' as any, { uuid: 'svc-1' }) // Acquire another reference to same container (ref count = 2) const ref2 = await client.get('service' as any, { uuid: 'svc-1' }) // Release first reference (ref count = 1) await ref1.close() // Release second reference (ref count = 0) await ref2.close() // Container kept alive for containerTimeout, then terminated ``` ### Health Monitoring The network continuously monitors health: **Agent Health:** - Agents must ping every `pingInterval` (default: 1 second) - Network marks agent as dead after `aliveTimeout` (default: 3 seconds) - Dead agents' containers are removed **Container Health:** - Containers respond to `ping()` calls - Failed pings may trigger termination - Unhealthy containers are removed from registry ### Graceful Shutdown Proper cleanup on shutdown: ```typescript // Container cleanup async terminate(): Promise { // 1. Notify connected clients await this.broadcastShutdown() // 2. Close external connections await this.database.close() // 3. Clear internal state this.data.clear() // 4. Release resources this.connections.clear() } // Agent cleanup await agentServer.close() tickManager.stop() // Client cleanup await containerRef.close() await client.close() // Server cleanup await server.close() tickManager.stop() ``` ## Endpoint References Containers are addressed by **endpoint references**: ```typescript type ContainerEndpointRef = string & { _containerEndpointRef: true } ``` ### Endpoint Types 1. **Direct Endpoint**: Direct connection to container ``` tcp://host:port/uuid ``` 2. **Routed Endpoint**: Connection through agent ``` agent://host:port:agentId/uuid ``` 3. **No-Connect Endpoint**: Request-only, no persistent connection ``` noconnect://host:port/uuid ``` ### Parsing Endpoints ```typescript import { parseEndpointRef, EndpointKind } from '@hcengineering/network-core' const parsed = parseEndpointRef(endpoint) console.log(parsed.kind) // EndpointKind.direct | routed | noconnect console.log(parsed.host) // Host address console.log(parsed.port) // Port number console.log(parsed.uuid) // Container UUID console.log(parsed.agentId) // Agent ID (for routed) ``` ## Container Kinds and Labels ### Container Kinds Containers are categorized by **kind** (string type): ```typescript type ContainerKind = string & { _containerKind: true } ``` Examples: - `'user-session'` - User session management - `'workspace'` - Workspace containers - `'query-engine'` - Query processing - `'transactor'` - Transaction handling Agents declare which kinds they support: ```typescript // Note: For production code, use serveAgent() on the client // This example uses AgentImpl directly for educational purposes const agent = new AgentImpl('agent-1' as any, { 'user-session': sessionFactory, workspace: workspaceFactory, 'query-engine': queryFactory }) ``` ### Labels Containers can have **labels** for fine-grained selection: ```typescript // Create container with labels await client.get('workspace' as any, { labels: ['premium', 'us-west', 'production'] }) // Labels enable: // - Multi-tenancy (tenant ID as label) // - Geographic routing (region labels) // - Tier-based selection (free, premium, enterprise) // - Environment separation (dev, staging, production) ``` ### Get Options ```typescript interface GetOptions { uuid?: ContainerUuid // Specific container UUID extra?: Record // Additional parameters for factory labels?: string[] // Labels for selection } ``` ## Summary Key concepts to remember: 1. **Network** = Central coordinator 2. **Agent** = Worker node hosting containers 3. **Container** = Service instance with business logic 4. **Client** = Application requesting containers 5. **Reference Counting** = Automatic lifecycle management 6. **Endpoint** = Address for reaching a container 7. **Kind** = Container type/category 8. **Labels** = Fine-grained selection criteria These concepts form the foundation for building scalable, fault-tolerant distributed systems with Huly Network. ## Next Steps - [Container Development Guide](CONTAINER_DEVELOPMENT.md) - Build your first container - [Agent Development Guide](AGENT_DEVELOPMENT.md) - Create custom agents - [Client Usage Guide](CLIENT_USAGE.md) - Connect and use containers - [Production Deployment](PRODUCTION_DEPLOYMENT.md) - Deploy to production --- Need more help? Check the [Troubleshooting Guide](TROUBLESHOOTING.md) or [Examples](../examples/).