Skip to content

Fix Update Propagation Architecture: Implement Proximity-Based Forwarding and Repair Subscription Topology #1848

@sanity

Description

@sanity

Fix Update Propagation Architecture: Implement Proximity-Based Forwarding and Repair Subscription Topology

Problem Summary

The Freenet network has a fundamental architectural problem with how updates propagate between peers. Multiple interconnected issues (#1846, #1847, freenet/river#33, freenet/river#34) all stem from the same root cause: subscription networks form disconnected islands, preventing updates from reaching all peers that need them.

This breaks the core promise of Freenet's delta-sync model where updates should spread "like a virus" through the network.

Current Broken State

What's Happening

When multiple peers cache the same contract and make updates:

  1. Updates only propagate to direct subscribers (via get_broadcast_targets_update)
  2. Subscription trees don't form naturally as designed
  3. Nodes become isolated with no subscription connections
  4. Each peer maintains divergent contract state

Concrete Example (River Chat)

Alice creates room → Caches locally (optimal location) → Cannot subscribe
Bob joins room → Subscribes to provider → No path to Alice
Result: Messages don't propagate between users

Root Causes

1. Optimal Location Paradox (#1847)

Nodes at the optimal network position for a contract cannot establish subscriptions because the subscription logic only looks for remote peers:

// In request_subscribe and start_subscription_request:
if closest.is_none() {
    if op_manager.ring.should_seed(&key) {
        // Node is isolated - no subscription possible
        return;
    }
}

2. No Neighbor Awareness

Nodes don't know if adjacent peers are caching the same contracts. Updates only flow through explicit subscription connections, missing natural propagation paths between neighbors.

3. Incomplete Update Implementation

The TODO in update.rs confirms this is unfinished:

// TODO: complete update logic in the network

Proposed Solution: Phased Approach

Phase 1: Fix Subscription Formation (Immediate Priority)

  • Allow next-best peer subscriptions - Nodes at optimal location should subscribe to next-best available peers
  • Fix bidirectional subscription establishment - Both peers should add each other as subscribers (this should already work per design)
  • Verify subscription trees form naturally - As peers cache contracts, subscription relationships should emerge

Phase 2: Application Layer Fixes (High Priority)

  • Fix WebSocket response routing - Ensure SubscribeResponse messages reach clients
  • Handle concurrent subscriptions - Multiple nodes should be able to subscribe simultaneously

Phase 3: Proximity Propagation (If Needed)

Note: This phase may be unnecessary if Phase 1 fixes work properly and subscription trees form naturally as designed.

  • Add bloom filter exchange between neighbors - Peers periodically share which contracts they're caching
  • Extend update propagation logic:
pub(crate) fn get_broadcast_targets_update(
    &self,
    key: &ContractKey,
    sender: &PeerId,
) -> Vec<PeerKeyLocation> {
    let mut targets = Vec::new();

    // Existing: Add explicit subscribers
    targets.extend(self.ring.subscribers_of(key)...);

    // NEW: Add neighbors caching the same contract
    targets.extend(self.ring.proximity_neighbors_for(key)...);

    targets
}
  • Create redundant update paths - Updates flow through both subscriptions AND proximity

Expected Outcome

With these changes:

  1. Natural tree formation - The network self-organizes as originally designed when subscription fixes are applied
  2. River chat works - Users see each other's messages through properly formed subscription trees
  3. Updates reach all caching nodes - Fixed subscription topology ensures update propagation
  4. Fallback redundancy if needed - Proximity propagation provides additional paths if subscription trees alone prove insufficient

Success Criteria

  • Integration tests pass with multiple nodes subscribing and updating
  • River chat users can see each other's messages
  • Updates propagate even when direct subscription paths don't exist
  • Nodes at optimal location receive updates from the network

Related Issues

This issue consolidates and replaces:

Implementation Notes

Start with Phase 1 fixes as they should restore core subscription functionality. Phase 2 (application layer) should be addressed next to ensure protocols work correctly with the network layer. Phase 3 (proximity propagation) should only be implemented if Phases 1 and 2 don't fully resolve the issues - it may be unnecessary if subscription trees form naturally as designed.

cc @iduartgomez for review of this architectural approach

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-contractsArea: Contract runtime, SDK, and executionA-networkingArea: Networking, ring protocol, peer discoveryE-mediumExperience needed to fix/implement: Medium / intermediateP-highHigh priorityT-bugType: Something is broken

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions