mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
226 lines
6.2 KiB
Markdown
226 lines
6.2 KiB
Markdown
---
|
|
id: LSYNC-013
|
|
title: Hybrid Sync Protocol Handler (State + Log Based)
|
|
status: Done
|
|
assignee: james
|
|
parent: LSYNC-000
|
|
priority: High
|
|
tags: [sync, networking, protocol, peer-to-peer, leaderless]
|
|
depends_on: [LSYNC-014, LSYNC-015, LSYNC-016]
|
|
design_doc: core/src/infra/sync/NEW_SYNC.md
|
|
completed: 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)
|
|
|
|
```rust
|
|
#[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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
- [x] SyncProtocolHandler supports both state and log-based messages ✅
|
|
- [x] SyncMessage enum updated with new message types ✅
|
|
- [x] Can broadcast state changes to all peers ✅
|
|
- [x] Can broadcast shared changes with HLC ✅
|
|
- [x] Can request state from specific peer ✅
|
|
- [x] Can request shared changes since HLC ✅
|
|
- [x] ACK mechanism for log pruning ✅
|
|
- [x] BiStream communication working ✅
|
|
- [x] 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
|