ruvector/crates/ruvector-replication
rUv eafba64fa5
fix(security): RUSTSEC advisories + clippy hardening in RuVector (#504)
* fix(security): RUSTSEC advisories + clippy hardening in RuVector

- Replace all bare `partial_cmp().unwrap()` calls on f32/f64 with
  `.unwrap_or(Ordering::Equal)` to prevent panics on NaN values in
  sorting/max-by operations across ruvllm, ruvector-dag, prime-radiant,
  and rvagent-wasm (12 sites in production code).
- Add input validation guards to the HTTP search endpoint: reject k=0,
  k > 10_000, empty vectors, and vectors exceeding 65_536 dimensions,
  preventing memory exhaustion via unbounded allocations.
- Harden LocalFsBackend::execute in rvagent-cli with env_clear() +
  safe-env allowlist (SEC-005), deadline-based timeout enforcement, and
  1 MB output truncation, matching the security posture of LocalShellBackend.
- Remove 129 occurrences of the deprecated `unused_unit = "allow"` lint
  and 3 occurrences of the removed `clippy::match_on_vec_items` lint from
  Cargo.toml files workspace-wide; both are no-ops in current Rust/Clippy.
- All 653+ tests across ruvector-core, ruvector-server, ruvector-dag,
  rvagent-cli, and prime-radiant pass with zero failures.

Note: `bytes` is already at 1.11.1 (>= 1.10.0); `paste` 1.0.15 is a
transitive dependency with no semver fix available upstream; `cargo audit`
returns clean.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): cargo fmt + restore workspace unused_unit lint allow

- Run cargo fmt --all across all 9 files that drifted from rustfmt style
  (prime-radiant/energy.rs, ruvector-dag/bottleneck.rs+reasoning_bank.rs,
   ruvector-server/points.rs, ruvllm/pretrain_pipeline.rs+report.rs+registry.rs,
   rvagent-cli/app.rs, rvagent-wasm/gallery.rs)
- Add [workspace.lints.clippy] unused_unit = "allow" to root Cargo.toml;
  the per-crate entries removed in the security commit were still needed —
  moving to workspace-level is cleaner and restores -D warnings CI pass

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): remove unneeded unit return type in ruvix bench

Removes `-> ()` from the Fn bound in run_benchmark_with_kernel
(crates/ruvix/benches/src/ruvix.rs:50) — triggers clippy::unused_unit
under -D warnings. Clippy prefers `Fn(&mut Kernel)` without explicit
unit return.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): resolve rustfmt and clippy unused_unit failures

- Run cargo fmt --all to fix long closure formatting in 9 files
  (energy.rs, bottleneck.rs, reasoning_bank.rs, points.rs,
  pretrain_pipeline.rs, report.rs, registry.rs, app.rs, gallery.rs)
- Add unused_unit = "allow" to [lints.clippy] in ruvix-bench and
  ruvector-mincut Cargo.toml files to suppress the unused_unit lint
  that was previously suppressed globally and now fires on two
  Fn(&mut T) -> () and FnMut() -> () function bounds

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 05:40:24 -04:00
..
src chore(workspace): cargo fmt — mechanical whitespace fix across 427 files 2026-04-24 10:44:02 -04:00
Cargo.toml fix(security): RUSTSEC advisories + clippy hardening in RuVector (#504) 2026-05-23 05:40:24 -04:00
README.md docs: add install one-liners and footer links to ruvllm and replication READMEs 2026-02-27 03:40:08 +00:00

ruvector-replication

Crates.io docs.rs License: MIT Rust

Multi-master vector replication with quorum writes, vector clocks, and automatic conflict resolution.

ruvector-replication = "0.1.1"

When your vector database runs on more than one node, you need a way to keep data in sync without losing writes or slowing down queries. ruvector-replication handles that: it replicates vectors across nodes, resolves conflicts automatically, and lets you trade off consistency versus speed per-write. It plugs into the RuVector ecosystem alongside Raft consensus and auto-sharding.

Single-node vector DB ruvector-replication
Availability One node goes down, everything stops Replicas serve reads and accept writes
Write scaling One writer Multi-master -- write to any node
Conflict handling N/A Vector clocks, last-write-wins, or CRDTs
Consistency control N/A Per-write: One, Quorum, or All
Sync efficiency N/A Incremental deltas with compression
Recovery Manual restore from backup Automatic replica recovery

Quick Start

use ruvector_replication::{Replicator, ReplicationConfig, ConsistencyLevel};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ReplicationConfig {
        replication_factor: 3,
        consistency_level: ConsistencyLevel::Quorum,
        sync_interval: Duration::from_millis(100),
        batch_size: 1000,
        compression: true,
        ..Default::default()
    };

    let replicator = Replicator::new(config).await?;
    replicator.start().await?;

    Ok(())
}

Key Features

Feature What It Does Why It Matters
Multi-master replication Write to any node in the cluster No single point of failure for writes
Configurable consistency Choose One, Quorum, or All per write Trade latency for safety on a per-operation basis
Vector clock conflict resolution Track causal ordering across nodes Detect and resolve concurrent writes correctly
CRDT support Conflict-free replicated data types Guaranteed convergence without coordination
Change streams Real-time replication event stream Monitor sync status and react to changes
Incremental sync with compression Only send deltas, compressed on the wire Minimize bandwidth between nodes
Automatic recovery Replicas catch up after failures No manual intervention on node restart
Bandwidth throttling Cap replication throughput Protect production traffic from replication storms

Write with Replication

use ruvector_replication::{Replicator, WriteOptions};

// Write with quorum consistency
let options = WriteOptions {
    consistency: ConsistencyLevel::Quorum,
    timeout: Duration::from_secs(5),
};

replicator.write(vector_entry, options).await?;

// Write with eventual consistency (faster)
let options = WriteOptions {
    consistency: ConsistencyLevel::One,
    ..Default::default()
};

replicator.write(vector_entry, options).await?;

Monitor Replication

// Get replication lag
let lag = replicator.lag().await?;
println!("Replication lag: {:?}", lag);

// Get replica status
for replica in replicator.replicas().await? {
    println!("{}: {} (lag: {}ms)",
        replica.id,
        replica.status,
        replica.lag_ms
    );
}

// Subscribe to replication events
let mut stream = replicator.events().await?;
while let Some(event) = stream.next().await {
    match event {
        ReplicationEvent::Synced { node_id, entries } => {
            println!("Synced {} entries to {}", entries, node_id);
        }
        ReplicationEvent::Conflict { key, resolution } => {
            println!("Conflict on {}: {:?}", key, resolution);
        }
        _ => {}
    }
}

API Overview

Core Types

// Replication configuration
pub struct ReplicationConfig {
    pub replication_factor: usize,
    pub consistency_level: ConsistencyLevel,
    pub sync_interval: Duration,
    pub batch_size: usize,
    pub compression: bool,
    pub conflict_resolution: ConflictResolution,
}

// Consistency levels
pub enum ConsistencyLevel {
    One,      // Write to one replica
    Quorum,   // Write to majority
    All,      // Write to all replicas
}

// Conflict resolution strategies
pub enum ConflictResolution {
    LastWriteWins,
    VectorClock,
    Custom(Box<dyn ConflictResolver>),
}

// Replica information
pub struct ReplicaInfo {
    pub id: NodeId,
    pub status: ReplicaStatus,
    pub lag_ms: u64,
    pub last_sync: DateTime<Utc>,
}

Replicator Operations

impl Replicator {
    pub async fn new(config: ReplicationConfig) -> Result<Self>;
    pub async fn start(&self) -> Result<()>;
    pub async fn stop(&self) -> Result<()>;

    // Write operations
    pub async fn write(&self, entry: VectorEntry, options: WriteOptions) -> Result<()>;
    pub async fn write_batch(&self, entries: Vec<VectorEntry>, options: WriteOptions) -> Result<()>;

    // Monitoring
    pub async fn lag(&self) -> Result<Duration>;
    pub async fn replicas(&self) -> Result<Vec<ReplicaInfo>>;
    pub async fn events(&self) -> Result<impl Stream<Item = ReplicationEvent>>;

    // Management
    pub async fn add_replica(&self, node_id: NodeId) -> Result<()>;
    pub async fn remove_replica(&self, node_id: NodeId) -> Result<()>;
    pub async fn force_sync(&self, node_id: NodeId) -> Result<()>;
}

Architecture

┌─────────────────────────────────────────────────────────┐
│                   Replication Flow                       │
│                                                         │
│  Client                                                 │
│    │                                                    │
│    ▼                                                    │
│  ┌──────────┐     Quorum Write    ┌──────────┐         │
│  │ Primary  │────────────────────▶│ Replica 1│         │
│  │          │                     │          │         │
│  │ Vectors  │────────────────────▶│ Vectors  │         │
│  └──────────┘                     └──────────┘         │
│       │                                                 │
│       │        Async Replication                        │
│       └──────────────────────────▶┌──────────┐         │
│                                   │ Replica 2│         │
│                                   │          │         │
│                                   │ Vectors  │         │
│                                   └──────────┘         │
└─────────────────────────────────────────────────────────┘

Documentation

License

MIT License - see LICENSE for details.


Part of Ruvector - Built by rUv

Star on GitHub

Documentation | Crates.io | GitHub