# velo-common Common types for the Velo distributed systems stack. ## Overview This crate provides the foundational types used across Velo for identity and addressing. The design prioritizes: - **Compact representations** for embedding in fixed-size handles - **Transport-agnostic addressing** without enumerating all possible transports - **KV-store friendly** serialization using opaque `Bytes` ## Identity Types ### InstanceId `InstanceId` is a UUID-based identifier that serves as the **source of truth** for identifying a running Velo instance. It is used for: - Transport-level routing - Discovery registration - Peer management ```rust use velo_common::InstanceId; let instance_id = InstanceId::new_v4(); let uuid = instance_id.as_uuid(); ``` ### WorkerId `WorkerId` is a deterministic 64-bit identifier derived from `InstanceId` via xxh3 hash. The compact representation enables embedding worker identity into fixed-size handles. **Design rationale**: A `u128` handle can encode: - 64 bits for `WorkerId` - 64 bits for additional data (sequence numbers, flags, etc.) This value-semantics approach simplifies passing identity through systems that work with fixed-size integers. ```rust use velo_common::InstanceId; let instance_id = InstanceId::new_v4(); let worker_id = instance_id.worker_id(); // Deterministic derivation // Embed in a u128 handle let other_data: u128 = 42; let handle: u128 = (worker_id.as_u64() as u128) << 64 | other_data; ``` The derivation is always consistent—calling `worker_id()` multiple times returns the same value. ## Address Types ### WorkerAddress `WorkerAddress` is an opaque byte container holding transport endpoint information. Internally, it's a MessagePack-encoded map of `TransportKey -> Bytes`, but this structure is intentionally hidden from consumers. **Key design decisions**: 1. **Opaque values**: Transport endpoints are stored as raw bytes. They could be simple strings (`"tcp://127.0.0.1:5555"`) or complex serialized objects. The interpretation is left to the transport implementation. 2. **No transport enum**: Rather than defining an enum of all possible transports with their configurations, we use string keys (`"tcp"`, `"rdma"`, `"grpc"`, etc.). This allows transports to be added without modifying the common types. 3. **KV-store friendly**: The entire address serializes to a `Bytes` blob, suitable for storage in etcd, Redis, or any key-value store without schema changes. ```rust use velo_common::WorkerAddress; use std::collections::HashMap; // Addresses are typically constructed by velo-transports transport builders, // but here we demonstrate the consumer API: let mut map = HashMap::new(); map.insert("tcp".to_string(), b"tcp://127.0.0.1:5555".to_vec()); map.insert("rdma".to_string(), b"rdma://10.0.0.1:6666".to_vec()); let encoded = rmp_serde::to_vec(&map).unwrap(); let address = WorkerAddress::from_encoded(encoded); // Reading an address (consumer perspective) let transports = address.available_transports().unwrap(); // ["tcp", "rdma"] let tcp_endpoint = address.get_entry("tcp").unwrap(); // Some(Bytes) ``` ### TransportKey A type-safe wrapper around transport identifiers. Provides zero-cost abstraction over `Arc` with efficient cloning and HashMap compatibility. ```rust use velo_common::TransportKey; use std::collections::HashMap; let key = TransportKey::from("tcp"); let key2: TransportKey = "rdma".into(); // Works with HashMap lookups via Borrow let mut map = HashMap::new(); map.insert(TransportKey::from("tcp"), "127.0.0.1:5555"); assert!(map.get("tcp").is_some()); // &str lookup works ``` ### PeerInfo Combines `InstanceId` and `WorkerAddress` into a single structure representing a discoverable peer. This is the primary type exchanged during peer discovery and registration. ```rust use velo_common::{InstanceId, PeerInfo, WorkerAddress}; use std::collections::HashMap; let instance_id = InstanceId::new_v4(); let map: HashMap> = HashMap::new(); let encoded = rmp_serde::to_vec(&map).unwrap(); let worker_address = WorkerAddress::from_encoded(encoded); let peer_info = PeerInfo::new(instance_id, worker_address); assert_eq!(peer_info.instance_id(), instance_id); ``` ## Address Construction `WorkerAddress` instances are constructed by transport builders in `velo-transports`. Each transport (TCP, gRPC, NATS, UCX, etc.) contributes its endpoint data: ```text ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ TCP Transport │ │ gRPC Transport │ │ UCX Transport │ │ Builder │ │ Builder │ │ Builder │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ "tcp" -> endpoint │ "grpc" -> endpoint │ "ucx" -> blob │ │ │ └───────────────────────┼───────────────────────┘ │ ▼ ┌────────────────────────┐ │ WorkerAddress │ │ (MessagePack map) │ │ │ │ tcp -> bytes │ │ grpc -> bytes │ │ ucx -> bytes │ └────────────────────────┘ ``` When a Velo client receives a `PeerInfo`, it can: 1. Check `available_transports()` to see what's supported 2. Extract the relevant endpoint via `get_entry(key)` 3. Register the peer with its own transports This design decouples the common types from specific transport implementations. ## Wire Format - **InstanceId**: Serializes as a UUID string (JSON) or 16 bytes (binary) - **WorkerId**: Serializes as a u64 - **WorkerAddress**: Serializes as a byte array (the MessagePack-encoded map) - **PeerInfo**: Serializes as a struct with `instance_id` and `worker_address` fields All types implement `serde::Serialize` and `serde::Deserialize`.