spacedrive/.tasks/LSYNC-013-sync-protocol-handler.md

6.2 KiB

id, title, status, assignee, parent, priority, tags, depends_on, design_doc, completed
id title status assignee parent priority tags depends_on design_doc completed
LSYNC-013 Hybrid Sync Protocol Handler (State + Log Based) Done james LSYNC-000 High
sync
networking
protocol
peer-to-peer
leaderless
LSYNC-014
LSYNC-015
LSYNC-016
core/src/infra/sync/NEW_SYNC.md 2025-10-09

Description

Create sync protocol handler supporting the new hybrid model:

  • State-based messages for device-owned data (locations, entries)
  • Log-based messages with HLC for shared resources (tags, albums)

No leader/follower distinction - all devices are peers.

Architecture Change

Old: Leader/follower with sequence-based sync New: Peer-to-peer with hybrid strategy

Benefits:

  • No bottleneck (no leader)
  • Works offline (all peers equal)
  • Simpler (no election/heartbeats)

SyncMessage Enum (Revised)

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SyncMessage {
    // ===== State-Based (Device-Owned Data) =====

    /// Broadcast current state of device-owned resource
    StateChange {
        model_type: String,      // "location", "entry", "volume"
        record_uuid: Uuid,
        device_id: Uuid,         // Owner
        data: serde_json::Value,
        timestamp: DateTime<Utc>,
    },

    /// Batch state changes (efficiency)
    StateBatch {
        model_type: String,
        device_id: Uuid,
        records: Vec<StateRecord>,
    },

    /// Request full state from peer
    StateRequest {
        model_types: Vec<String>,
        device_id: Option<Uuid>,  // Specific device or all
        since: Option<DateTime>,  // Incremental
    },

    /// Response with state
    StateResponse {
        model_type: String,
        device_id: Uuid,
        records: Vec<StateRecord>,
        has_more: bool,
    },

    // ===== Log-Based (Shared Resources) =====

    /// Broadcast shared resource change (with HLC)
    SharedChange {
        hlc: HLC,
        model_type: String,      // "tag", "album", "user_metadata"
        record_uuid: Uuid,
        change_type: ChangeType, // Insert/Update/Delete
        data: serde_json::Value,
    },

    /// Batch shared changes
    SharedChangeBatch {
        entries: Vec<SharedChangeEntry>,
    },

    /// Request shared changes since HLC
    SharedChangeRequest {
        since_hlc: Option<HLC>,
        limit: usize,
    },

    /// Response with shared changes
    SharedChangeResponse {
        entries: Vec<SharedChangeEntry>,
        has_more: bool,
    },

    /// Acknowledge received shared changes (for pruning)
    AckSharedChanges {
        from_device: Uuid,
        up_to_hlc: HLC,
    },
}

Implementation Steps

  1. Update core/src/service/network/protocol/sync/messages.rs - New SyncMessage enum
  2. Update sync/handler.rs - Handle both state and log-based messages
  3. Create sync/state.rs - State-based sync logic
  4. Create sync/shared.rs - Log-based sync with HLC
  5. Integrate with DeviceRegistry for peer lookup
  6. Add per-peer connection management
  7. Register protocol with ALPN: /spacedrive/sync/2.0.0 (version bump!)

Protocol Handler Structure

// core/src/service/network/protocol/sync/handler.rs
pub struct SyncProtocolHandler {
    library_id: Uuid,
    shared_changes_db: Arc<SharedChangesDb>,  // My log of shared changes
    device_registry: Arc<RwLock<DeviceRegistry>>,
    event_bus: Arc<EventBus>,
    hlc_generator: Arc<Mutex<HLCGenerator>>,
    // No role field!
}

impl ProtocolHandler for SyncProtocolHandler {
    const ALPN: &'static [u8] = b"/spacedrive/sync/2.0.0";

    async fn handle_stream(
        &self,
        stream: BiStream,
        peer_device_id: Uuid,
    ) -> Result<(), NetworkingError>;
}

impl SyncProtocolHandler {
    /// Broadcast state change to all peers
    pub async fn broadcast_state_change(
        &self,
        change: StateChange,
    ) -> Result<(), SyncError>;

    /// Broadcast shared change to all peers
    pub async fn broadcast_shared_change(
        &self,
        entry: SharedChangeEntry,
    ) -> Result<(), SyncError>;

    /// Request state from peer
    pub async fn request_state(
        &self,
        peer_id: Uuid,
        request: StateRequest,
    ) -> Result<StateResponse, SyncError>;

    /// Request shared changes from peer
    pub async fn request_shared_changes(
        &self,
        peer_id: Uuid,
        since_hlc: Option<HLC>,
    ) -> Result<Vec<SharedChangeEntry>, SyncError>;

    /// Handle incoming message
    async fn handle_message(
        &self,
        msg: SyncMessage,
        stream: &mut BiStream,
        from_device: Uuid,
    ) -> Result<(), SyncError>;
}

Connection Management

  • Protocol uses Iroh BiStreams
  • Each device maintains connections to all peer devices
  • Auto-reconnect on connection loss
  • No heartbeats needed (connection itself is the liveness indicator)
  • Offline changes queue locally, sync on reconnect

Acceptance Criteria

  • SyncProtocolHandler supports both state and log-based messages
  • SyncMessage enum updated with new message types
  • Can broadcast state changes to all peers
  • Can broadcast shared changes with HLC
  • Can request state from specific peer
  • Can request shared changes since HLC
  • ACK mechanism for log pruning
  • BiStream communication working
  • Protocol registered with ALPN
  • Integration tests validate peer-to-peer flow (pending)

Implementation Notes (Oct 9, 2025)

Successfully implemented in core/src/service/network/protocol/sync/handler.rs:

Message Handling:

  • StateChange and StateBatch - Routes to PeerSync for state-based sync
  • SharedChange and SharedChangeBatch - Routes to PeerSync for log-based sync
  • StateRequest / StateResponse - Stub for backfill (TODO)
  • SharedChangeRequest / SharedChangeResponse - Stub for catch-up (TODO)
  • AckSharedChanges - Routes to PeerSync for log pruning
  • Heartbeat - Basic echo response

Key Features:

  • All message types properly deserialized and routed
  • Error handling with proper propagation
  • Logging for debugging
  • No leader/follower logic
  • Ready for end-to-end testing

References

  • New architecture: core/src/infra/sync/NEW_SYNC.md
  • HLC implementation: LSYNC-014
  • State-based sync: LSYNC-015
  • Log-based sync: LSYNC-016