Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: client-initiated PUT requests not cached locally when forwarded to target peer #2010

Closed
Labels
A-networkingArea: Networking, ring protocol, peer discovery E-mediumExperience needed to fix/implement: Medium / intermediate P-highHigh priority T-bugType: Something is broken
@sanity

Description

Client-initiated PUT requests not cached locally when forwarded to target peer

Problem

When a client publishes a contract update via fdev publish, the local node fails to cache the new state if it determines another peer should be the primary holder. This causes the publishing node to continue serving stale cached state even after successfully initiating a PUT operation.

Steps to Reproduce

  1. Run Freenet node in network mode with at least one peer connection
  2. Publish a contract update via fdev publish for a contract that routes to a remote peer (contract location != local peer location)
  3. Observe PUT operation is forwarded to remote peer
  4. Access the contract via HTTP gateway immediately after publishing

Expected: Local node serves the newly published state
Actual: Local node serves old cached state from before the PUT

Observed Behavior

From logs during cargo make publish-river (contract BcfxyjCH4snaknrBoCiqhYc9UFvmiJvhsp5d4L5DuvRa):

[00:01:18] PUT request initiated by client
[00:01:18] Determined PUT routing target: v6MWKgqHiBMNcGtG (@ 0.923911415153386)
[00:01:18] Forwarding PUT to target peer
[00:01:18] Sending outbound message to peer
[00:02:23] Transaction timed out: 01K8M1WZYG2NCWGQR8JDN0MH01

After timeout, HTTP gateway still serves old state:

[00:02:23] fetched contract state, contract: BcfxyjCH4snaknrBoCiqhYc9UFvmiJvhsp5d4L5DuvRa,
 state_size: 2134337 // OLD STATE

New state size should be: 2218761 bytes

Root Cause

In crates/core/src/operations/put.rs, the request_put() function at lines ~955-1130:

// Find the optimal target for this contract
let target = op_manager
 .ring
 .closest_potentially_caching(&key, [&own_location.peer].as_slice());
if target.is_none() {
 // NO OTHER PEERS: Store locally then broadcast
 let updated_value = put_contract(op_manager, key, value, ...).await?;
 // ... broadcast to subscribers
} else {
 // PEER FOUND: Forward WITHOUT storing locally
 let target_peer = target.unwrap();
 put_op.state = Some(PutState::AwaitingResponse { ... });
 let msg = PutMsg::RequestPut {
 id,
 sender: own_location,
 contract,
 related_contracts,
 value,
 htl,
 target: target_peer,
 };
 // DIRECTLY FORWARDS - NO LOCAL CACHE UPDATE
 op_manager.notify_op_change(NetMessage::from(msg), OpEnum::Put(put_op)).await?;
}

The else branch forwards the PUT without calling put_contract(), so the local node never caches the new state.

Relationship to PR #1996

PR #1996 fixed a similar issue for incoming PUT requests (when receiving from network), ensuring upsert_contract_state() persists merged state after validation.

However, that fix only applies to the process_put_request() code path. The request_put() function (client-initiated PUTs) has a separate code path that was not addressed.

Suggested Fix

The request_put() function should always call put_contract() to update the local cache before forwarding to the target peer:

if target.is_none() {
 // No other peers - handle locally
 let updated_value = put_contract(op_manager, key, value, ...).await?;
 // ... broadcast logic
} else {
 // Peer found - cache locally THEN forward
 let target_peer = target.unwrap();
 // NEW: Cache locally first
 let updated_value = put_contract(op_manager, key, value.clone(),
 related_contracts.clone(), &contract).await?;
 // THEN forward to target peer
 put_op.state = Some(PutState::AwaitingResponse {
 key,
 upstream: None,
 contract: contract.clone(),
 state: updated_value.clone(), // Use cached value
 subscribe,
 });
 let msg = PutMsg::RequestPut { ... };
 op_manager.notify_op_change(...).await?;
}

This ensures:

Impact

Affected scenarios:

  • Contract publishing via fdev publish when running in network mode
  • Any client-initiated PUT that routes to a non-local peer
  • Particularly impacts webapp contracts where publishers expect immediate availability

Not affected:

Environment

Additional Context

This was discovered while publishing River webapp updates. The build timestamp embedded in the webapp confirmed the local node was serving stale state even though the PUT operation had been initiated successfully.

Related: #1995, PR #1996

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-networkingArea: Networking, ring protocol, peer discovery E-mediumExperience needed to fix/implement: Medium / intermediate P-highHigh priority T-bugType: Something is broken

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /