mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
6.2 KiB
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 |
|
|
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
- Update
core/src/service/network/protocol/sync/messages.rs- New SyncMessage enum - Update
sync/handler.rs- Handle both state and log-based messages - Create
sync/state.rs- State-based sync logic - Create
sync/shared.rs- Log-based sync with HLC - Integrate with DeviceRegistry for peer lookup
- Add per-peer connection management
- 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:
StateChangeandStateBatch- Routes to PeerSync for state-based syncSharedChangeandSharedChangeBatch- Routes to PeerSync for log-based syncStateRequest/StateResponse- Stub for backfill (TODO)SharedChangeRequest/SharedChangeResponse- Stub for catch-up (TODO)AckSharedChanges- Routes to PeerSync for log pruningHeartbeat- 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