Best Practices - ruvnet/ruv-FANN GitHub Wiki
This document outlines best practices for developing, maintaining, and deploying the ruv-FANN (Rust Universal Vector - Fast Approximate Nearest Neighbors) project. These guidelines ensure code quality, performance, security, and maintainability across all components.
ruv-FANN/
├── Semantic_Cartan_Matrix/ # Core implementation
│ ├── micro_cartan_attn/ # Attention mechanisms
│ ├── micro_core/ # Core functionality
│ ├── micro_metrics/ # Performance metrics
│ ├── micro_routing/ # Routing algorithms
│ └── micro_swarm/ # Swarm coordination
├── docs/ # Documentation
├── examples/ # Usage examples
├── tests/ # Integration tests
└── wiki/ # Project wiki
- Single Responsibility: Each crate should have a focused purpose
- Clear Dependencies: Minimize cross-crate dependencies
- API Surface: Keep public APIs minimal and well-documented
// Good: Clear module structure
pub mod attention {
pub mod cartan_attention;
pub mod semantic_matrix;
mod internal_utils; // Private implementation details
}
pub mod core {
pub mod vector_operations;
pub mod distance_metrics;
}
- Maximum 500 lines per file: Split large files into focused modules
- Logical grouping: Group related functionality together
- Clear naming: Use descriptive file and module names
// lib.rs - Main entry point
pub mod attention;
pub mod core;
pub mod metrics;
pub mod routing;
pub mod swarm;
// Re-export common types
pub use attention::CartanAttention;
pub use core::{Vector, DistanceMetric};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum RuvFannError {
#[error("Vector dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch { expected: usize, actual: usize },
#[error("Invalid distance metric: {metric}")]
InvalidDistanceMetric { metric: String },
#[error("Swarm coordination failed: {reason}")]
SwarmError { reason: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
pub type Result<T> = std::result::Result<T, RuvFannError>;
// Use ? operator for error propagation
pub fn process_vectors(vectors: Vec<Vector>) -> Result<ProcessedVectors> {
let normalized = normalize_vectors(vectors)?;
let indexed = build_index(normalized)?;
Ok(ProcessedVectors { indexed })
}
// Provide context with map_err
fn load_config(path: &Path) -> Result<Config> {
std::fs::read_to_string(path)
.map_err(|e| RuvFannError::Io(e))?
.parse()
.map_err(|_| RuvFannError::InvalidConfig)
}
// Prefer in-place operations when possible
impl Vector {
pub fn normalize_mut(&mut self) {
let norm = self.norm();
if norm > f32::EPSILON {
for component in &mut self.data {
*component /= norm;
}
}
}
// Provide immutable version when needed
pub fn normalize(&self) -> Self {
let mut result = self.clone();
result.normalize_mut();
result
}
}
use object_pool::Pool;
pub struct VectorPool {
pool: Pool<Vec<f32>>,
}
impl VectorPool {
pub fn get_vector(&self, capacity: usize) -> Vec<f32> {
let mut vec = self.pool.try_pull().unwrap_or_default();
vec.clear();
vec.reserve(capacity);
vec
}
pub fn return_vector(&self, vec: Vec<f32>) {
self.pool.attach(vec);
}
}
use std::arch::x86_64::*;
#[cfg(target_arch = "x86_64")]
unsafe fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(a.len(), b.len());
assert!(a.len() % 8 == 0);
let mut sum = _mm256_setzero_ps();
for i in (0..a.len()).step_by(8) {
let va = _mm256_loadu_ps(a.as_ptr().add(i));
let vb = _mm256_loadu_ps(b.as_ptr().add(i));
sum = _mm256_fmadd_ps(va, vb, sum);
}
// Horizontal sum
let mut result = [0f32; 8];
_mm256_storeu_ps(result.as_mut_ptr(), sum);
result.iter().sum()
}
use rayon::prelude::*;
impl SemanticCartanMatrix {
pub fn batch_transform(&self, vectors: &[Vector]) -> Vec<Vector> {
vectors
.par_iter()
.map(|v| self.transform(v))
.collect()
}
pub fn parallel_search(&self, query: &Vector, k: usize) -> Vec<SearchResult> {
self.index
.par_chunks(1000)
.flat_map(|chunk| {
chunk.iter()
.map(|item| SearchResult {
distance: self.distance(query, &item.vector),
item: item.clone(),
})
})
.collect::<Vec<_>>()
.into_iter()
.k_closest(k)
}
}
use tokio::sync::{RwLock, Semaphore};
use std::sync::Arc;
pub struct AsyncVectorIndex {
data: Arc<RwLock<VectorData>>,
semaphore: Arc<Semaphore>,
}
impl AsyncVectorIndex {
pub async fn search(&self, query: Vector, k: usize) -> Result<Vec<SearchResult>> {
let _permit = self.semaphore.acquire().await?;
let data = self.data.read().await;
tokio::task::spawn_blocking(move || {
data.search_blocking(query, k)
}).await?
}
pub async fn insert(&self, vector: Vector) -> Result<()> {
let mut data = self.data.write().await;
data.insert(vector).await
}
}
pub fn validate_vector_dimensions(vectors: &[Vector], expected_dim: usize) -> Result<()> {
for (i, vector) in vectors.iter().enumerate() {
if vector.len() != expected_dim {
return Err(RuvFannError::DimensionMismatch {
expected: expected_dim,
actual: vector.len(),
}.with_context(format!("Vector at index {}", i)));
}
}
Ok(())
}
pub fn safe_normalize(vector: &mut [f32]) -> Result<()> {
let norm_sq: f32 = vector.iter().map(|x| x * x).sum();
if norm_sq.is_nan() || norm_sq.is_infinite() {
return Err(RuvFannError::InvalidVector("NaN or infinite values detected"));
}
if norm_sq < f32::EPSILON {
return Err(RuvFannError::InvalidVector("Zero vector cannot be normalized"));
}
let norm = norm_sq.sqrt();
for component in vector {
*component /= norm;
}
Ok(())
}
pub struct SafeVectorAccess<'a> {
data: &'a [f32],
}
impl<'a> SafeVectorAccess<'a> {
pub fn new(data: &'a [f32]) -> Self {
Self { data }
}
pub fn get(&self, index: usize) -> Option<f32> {
self.data.get(index).copied()
}
pub fn get_range(&self, range: Range<usize>) -> Option<&[f32]> {
self.data.get(range)
}
}
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct SecurityConfig {
pub max_vector_dimension: usize,
pub max_batch_size: usize,
pub rate_limit_per_second: u32,
pub enable_metrics: bool,
}
impl SecurityConfig {
pub fn from_env() -> Result<Self> {
let config = Self {
max_vector_dimension: env::var("RUVAN_MAX_VECTOR_DIM")
.unwrap_or_else(|_| "1024".to_string())
.parse()?,
max_batch_size: env::var("RUVAN_MAX_BATCH_SIZE")
.unwrap_or_else(|_| "1000".to_string())
.parse()?,
rate_limit_per_second: env::var("RUVAN_RATE_LIMIT")
.unwrap_or_else(|_| "100".to_string())
.parse()?,
enable_metrics: env::var("RUVAN_ENABLE_METRICS")
.unwrap_or_else(|_| "true".to_string())
.parse()?,
};
config.validate()?;
Ok(config)
}
fn validate(&self) -> Result<()> {
if self.max_vector_dimension == 0 || self.max_vector_dimension > 10_000 {
return Err(RuvFannError::InvalidConfig("Invalid max_vector_dimension"));
}
if self.max_batch_size == 0 || self.max_batch_size > 100_000 {
return Err(RuvFannError::InvalidConfig("Invalid max_batch_size"));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn test_vector_normalization() {
let mut vector = vec![3.0, 4.0];
safe_normalize(&mut vector).unwrap();
let norm: f32 = vector.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!((norm - 1.0).abs() < f32::EPSILON);
}
#[test]
fn test_dimension_mismatch_error() {
let vectors = vec![
Vector::new(vec![1.0, 2.0]),
Vector::new(vec![1.0, 2.0, 3.0]), // Wrong dimension
];
let result = validate_vector_dimensions(&vectors, 2);
assert!(matches!(result, Err(RuvFannError::DimensionMismatch { .. })));
}
}
proptest! {
#[test]
fn test_distance_metric_properties(
v1 in prop::collection::vec(any::<f32>(), 1..100),
v2 in prop::collection::vec(any::<f32>(), 1..100)
) {
if v1.len() == v2.len() {
let dist = euclidean_distance(&v1, &v2);
// Distance is non-negative
prop_assert!(dist >= 0.0);
// Distance is symmetric
let dist_rev = euclidean_distance(&v2, &v1);
prop_assert!((dist - dist_rev).abs() < f32::EPSILON);
// Distance to self is zero
let self_dist = euclidean_distance(&v1, &v1);
prop_assert!(self_dist < f32::EPSILON);
}
}
}
#[tokio::test]
async fn test_complete_search_workflow() {
let config = TestConfig::default();
let mut index = SemanticCartanMatrix::new(config).await.unwrap();
// Insert test vectors
let test_vectors = generate_test_vectors(1000, 128);
for vector in test_vectors.iter() {
index.insert(vector.clone()).await.unwrap();
}
// Build index
index.build().await.unwrap();
// Perform search
let query = test_vectors[0].clone();
let results = index.search(&query, 10).await.unwrap();
assert_eq!(results.len(), 10);
assert_eq!(results[0].item.id, query.id); // Self should be closest
// Verify distances are sorted
for i in 1..results.len() {
assert!(results[i-1].distance <= results[i].distance);
}
}
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn benchmark_vector_operations(c: &mut Criterion) {
let vectors = generate_random_vectors(1000, 128);
c.bench_function("dot_product_naive", |b| {
b.iter(|| {
for i in 0..vectors.len()-1 {
black_box(dot_product_naive(&vectors[i], &vectors[i+1]));
}
})
});
c.bench_function("dot_product_simd", |b| {
b.iter(|| {
for i in 0..vectors.len()-1 {
black_box(unsafe { dot_product_simd(&vectors[i], &vectors[i+1]) });
}
})
});
}
criterion_group!(benches, benchmark_vector_operations);
criterion_main!(benches);
pub struct TestDataBuilder {
dimension: usize,
count: usize,
distribution: Distribution,
}
impl TestDataBuilder {
pub fn new() -> Self {
Self {
dimension: 128,
count: 1000,
distribution: Distribution::Normal,
}
}
pub fn with_dimension(mut self, dim: usize) -> Self {
self.dimension = dim;
self
}
pub fn with_count(mut self, count: usize) -> Self {
self.count = count;
self
}
pub fn build(self) -> Vec<Vector> {
match self.distribution {
Distribution::Normal => generate_normal_vectors(self.count, self.dimension),
Distribution::Uniform => generate_uniform_vectors(self.count, self.dimension),
}
}
}
/// Semantic Cartan Matrix for high-dimensional vector operations.
///
/// This structure implements a Cartan-based attention mechanism for
/// efficient similarity search in high-dimensional spaces.
///
/// # Examples
///
/// ```rust
/// use ruv_fann::{SemanticCartanMatrix, Vector};
///
/// let config = CartanConfig::default();
/// let mut matrix = SemanticCartanMatrix::new(config)?;
///
/// // Insert vectors
/// let vector = Vector::new(vec![1.0, 2.0, 3.0]);
/// matrix.insert(vector).await?;
///
/// // Build index
/// matrix.build().await?;
///
/// // Search
/// let query = Vector::new(vec![1.1, 2.1, 3.1]);
/// let results = matrix.search(&query, 10).await?;
/// ```
///
/// # Performance
///
/// The search operation has O(log n) complexity for indexed data,
/// with SIMD optimizations providing 2-4x speedup on supported hardware.
///
/// # Thread Safety
///
/// This structure is thread-safe for concurrent reads, but writes
/// require exclusive access. Use `Arc<RwLock<>>` for shared access.
#[derive(Debug)]
pub struct SemanticCartanMatrix {
// Implementation details...
}
/// Internal helper for computing Cartan metric distances.
///
/// This function uses the Cartan connection to compute distances
/// in the semantic manifold. The implementation follows the
/// mathematical formulation from [Paper Reference].
///
/// # Arguments
///
/// * `point_a` - First point in the manifold
/// * `point_b` - Second point in the manifold
/// * `connection` - Cartan connection coefficients
///
/// # Returns
///
/// The geodesic distance between the two points.
///
/// # Panics
///
/// Panics if the points have different dimensions or if the
/// connection matrix is not properly initialized.
fn compute_cartan_distance(
point_a: &[f32],
point_b: &[f32],
connection: &CartanConnection,
) -> f32 {
// Implementation...
}
# ruv-FANN: Rust Universal Vector - Fast Approximate Nearest Neighbors
## Quick Start
```rust
use ruv_fann::prelude::*;
let vectors = load_vectors("data.bin")?;
let index = build_index(vectors)?;
let results = index.search(&query, 10)?;
- 🚀 High Performance: SIMD-optimized vector operations
- 🧠 Semantic Understanding: Cartan-based attention mechanisms
- 🔧 Flexible: Multiple distance metrics and index types
- 🦀 Safe: Memory-safe Rust implementation
- ⚡ Async: Non-blocking operations with Tokio
Add to your Cargo.toml
:
[dependencies]
ruv-fann = "0.2.0"
[Include architectural diagrams and explanations]
[Include benchmark results and comparisons]
See CONTRIBUTING.md for development setup and guidelines.
### 3. API Documentation
#### OpenAPI Specification (for HTTP APIs)
```yaml
openapi: 3.0.0
info:
title: ruv-FANN API
description: Fast Approximate Nearest Neighbors service
version: 0.2.0
paths:
/search:
post:
summary: Search for similar vectors
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
query:
type: array
items:
type: number
example: [1.0, 2.0, 3.0]
k:
type: integer
minimum: 1
maximum: 1000
example: 10
responses:
'200':
description: Search results
content:
application/json:
schema:
type: object
properties:
results:
type: array
items:
type: object
properties:
id:
type: string
distance:
type: number
vector:
type: array
items:
type: number
# Multi-stage build for smaller production images
FROM rust:1.70 as builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
# Build with optimizations
RUN cargo build --release
FROM debian:bullseye-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN useradd -r -s /bin/false appuser
COPY --from=builder /app/target/release/ruv-fann /usr/local/bin/
COPY --chown=appuser:appuser config/ /app/config/
USER appuser
WORKDIR /app
EXPOSE 8080
CMD ["ruv-fann", "--config", "/app/config/production.toml"]
version: '3.8'
services:
ruv-fann:
build: .
ports:
- "8080:8080"
environment:
- RUST_LOG=info
- RUVAN_CONFIG_PATH=/app/config/development.toml
volumes:
- ./config:/app/config:ro
- ./data:/app/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
redis_data:
# production.toml
[server]
host = "0.0.0.0"
port = 8080
workers = 4 # Number of CPU cores
[vector_index]
dimensions = 512
index_type = "hnsw"
ef_construction = 200
max_connections = 16
use_simd = true
[memory]
vector_pool_size = 10000
batch_size = 1000
max_memory_mb = 4096
[performance]
enable_metrics = true
metrics_interval_seconds = 60
log_slow_queries = true
slow_query_threshold_ms = 100
[security]
max_request_size = "10MB"
rate_limit_per_second = 1000
enable_cors = false
use tracing_subscriber::{fmt, EnvFilter, prelude::*};
use tracing_appender::rolling::{RollingFileAppender, Rotation};
pub fn init_logging() -> Result<()> {
let file_appender = RollingFileAppender::new(
Rotation::daily(),
"/var/log/ruv-fann",
"app.log"
);
let (file_writer, _guard) = tracing_appender::non_blocking(file_appender);
tracing_subscriber::registry()
.with(
fmt::layer()
.with_target(false)
.with_thread_ids(true)
.with_file(true)
.with_line_number(true)
)
.with(
fmt::layer()
.with_writer(file_writer)
.json()
)
.with(
EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info"))
)
.init();
Ok(())
}
use prometheus::{Counter, Histogram, Registry, Encoder, TextEncoder};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Metrics {
pub search_requests: Counter,
pub search_duration: Histogram,
pub index_size: prometheus::IntGauge,
pub memory_usage: prometheus::IntGauge,
}
impl Metrics {
pub fn new() -> Result<Self> {
let search_requests = Counter::new(
"search_requests_total",
"Total number of search requests"
)?;
let search_duration = Histogram::with_opts(
prometheus::HistogramOpts::new(
"search_duration_seconds",
"Search request duration in seconds"
).buckets(vec![0.001, 0.01, 0.1, 1.0, 10.0])
)?;
let index_size = prometheus::IntGauge::new(
"index_vectors_total",
"Total number of vectors in index"
)?;
let memory_usage = prometheus::IntGauge::new(
"memory_usage_bytes",
"Current memory usage in bytes"
)?;
Ok(Self {
search_requests,
search_duration,
index_size,
memory_usage,
})
}
}
use axum::{Json, response::Json as ResponseJson};
use serde_json::{json, Value};
pub async fn health_check() -> ResponseJson<Value> {
let health_status = json!({
"status": "healthy",
"timestamp": chrono::Utc::now().to_rfc3339(),
"version": env!("CARGO_PKG_VERSION"),
"checks": {
"database": check_database_connection().await,
"memory": check_memory_usage().await,
"index": check_index_health().await,
}
});
ResponseJson(health_status)
}
async fn check_database_connection() -> Value {
// Implementation
json!({"status": "ok", "latency_ms": 5})
}
use consul::Client as ConsulClient;
use std::collections::HashMap;
pub struct ServiceDiscovery {
consul: ConsulClient,
service_name: String,
}
impl ServiceDiscovery {
pub async fn register_instance(&self, instance_id: String, address: String, port: u16) -> Result<()> {
let service = consul::catalog::Service {
id: Some(instance_id),
service: self.service_name.clone(),
address: Some(address),
port: Some(port),
tags: vec!["ruv-fann".to_string(), "vector-search".to_string()],
meta: HashMap::from([
("version".to_string(), env!("CARGO_PKG_VERSION").to_string()),
("capabilities".to_string(), "search,insert".to_string()),
]),
..Default::default()
};
self.consul.agent().service_register(&service).await?;
Ok(())
}
pub async fn discover_peers(&self) -> Result<Vec<ServiceInstance>> {
let services = self.consul.health()
.service(&self.service_name, None, true)
.await?;
Ok(services.into_iter()
.map(|s| ServiceInstance {
id: s.service.id.unwrap_or_default(),
address: s.service.address.unwrap_or_default(),
port: s.service.port.unwrap_or(8080),
})
.collect())
}
}
Following these best practices ensures:
- Code Quality: Maintainable, readable, and robust code
- Performance: Optimal resource utilization and response times
- Security: Safe operations and proper input validation
- Reliability: Comprehensive testing and error handling
- Scalability: Patterns that support growth and distribution
- Observability: Proper monitoring and debugging capabilities
Regular review and updates of these practices help maintain project health as requirements evolve.