Skip to content

feat: Request needed pieces from new peers on connection#188

Merged
artrixdotdev merged 9 commits intomainfrom
feat/request-piece-on-connect
Dec 4, 2025
Merged

feat: Request needed pieces from new peers on connection#188
artrixdotdev merged 9 commits intomainfrom
feat/request-piece-on-connect

Conversation

@artrixdotdev
Copy link
Owner

@artrixdotdev artrixdotdev commented Nov 26, 2025

#179

Summary by CodeRabbit

  • New Features

    • Peer-ready signaling so peers notify the torrent controller when ready, triggering next-block requests.
    • High-level incoming-piece flow and a block-scheduling helper to orchestrate receiving, deduplicating, storing, validating, and completing pieces.
  • Refactor

    • Centralized piece-transfer pipeline: incoming piece handling delegated to the new flow for clearer sequencing and reduced inline complexity.

✏️ Tip: You can customize this high-level summary in your review settings.

@artrixdotdev artrixdotdev linked an issue Nov 26, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Walkthrough

PeerActor now notifies the Torrent supervisor with TorrentMessage::PeerReady(peer_id) after sending its initial Bitfield. TorrentActor gains incoming_piece (async) and next_block_coordinates helpers; TorrentMessage adds PeerReady and delegates IncomingPiece handling to TorrentActor::incoming_piece.

Changes

Cohort / File(s) Summary
Peer actor startup notification
crates/libtortillas/src/peer/actor.rs
After sending initial Bitfield, PeerActor asynchronously sends TorrentMessage::PeerReady(peer_id) to the Torrent supervisor; supervisor failures map to PeerActorError::SupervisorCommunicationFailed and are returned from on_start.
Torrent actor piece reception & scheduling
crates/libtortillas/src/torrent/actor.rs
Adds pub async fn incoming_piece(index: usize, offset: usize, block: Bytes) to orchestrate deduplication, disk write (tokio::fs), validation, completion handling, and triggering piece completion. Adds pub fn next_block_coordinates(&self, piece_index: usize) -> (usize,usize,usize) and supporting internal helpers for block init/marking and re-request logic.
Torrent messages & handler changes
crates/libtortillas/src/torrent/messages.rs
Adds TorrentMessage::PeerReady(PeerId). Replaces inlined IncomingPiece processing with a call to self.incoming_piece(...).await. Adds PeerReady handling that requests the next block via next_block_coordinates when peer and torrent state permit; logs warnings otherwise.

Sequence Diagram(s)

sequenceDiagram
    actor PeerActor
    participant TorrentActor as Torrent
    participant Storage
    participant PieceManager

    PeerActor->>Torrent: send initial Bitfield
    PeerActor->>Torrent: TorrentMessage::PeerReady(peer_id)

    Torrent->>Torrent: handle PeerReady
    alt peer known & torrent Downloading & ready
        Torrent->>PeerActor: request next block (next_block_coordinates)
    else
        Torrent->>Torrent: log warning (peer not-ready/unknown)
    end

    PeerActor->>Torrent: IncomingPiece(index, offset, block)

    Torrent->>Torrent: incoming_piece(index, offset, block)
    Torrent->>Torrent: check duplicate / validate offset
    alt not duplicate & valid
        Torrent->>Storage: write block to disk
        Torrent->>Torrent: mark block received
        alt piece complete
            Torrent->>PieceManager: validate_and_send_piece
            alt piece valid
                Torrent->>Torrent: notify peers & trackers / advance next_piece
            else
                Torrent->>Torrent: cleanup invalid piece & re-request
            end
        else
            Torrent->>PeerActor: request next block (next_block_coordinates)
        end
    else
        Torrent->>Torrent: ignore duplicate or invalid block
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on:
    • crates/libtortillas/src/torrent/actor.rs — new async flow, disk I/O, deduplication, completion transitions.
    • crates/libtortillas/src/torrent/messages.rsPeerReady handling and IncomingPiece delegation consistency.
    • crates/libtortillas/src/peer/actor.rs — supervisor communication error mapping and propagation.

Possibly related PRs

Suggested reviewers

  • kurealnum

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: requesting pieces from newly connected peers, which aligns with the core functionality added across peer/actor.rs, torrent/actor.rs, and torrent/messages.rs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/request-piece-on-connect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added enhancement New feature or request med prio Medium Priority refactor Neither fixes a bug nor adds a feature labels Nov 26, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
crates/libtortillas/src/torrent/actor.rs (1)

310-327: Consider graceful error handling for I/O operations.

The expect() calls at lines 318 and 321 will panic on I/O failures (disk full, permissions, etc.). For a long-running torrent client, consider propagating errors gracefully or at least logging and skipping the block to allow re-request.

 async fn write_block_to_storage(&self, index: usize, offset: usize, block: Bytes) {
     match &self.piece_storage {
        PieceStorageStrategy::Disk(_) => {
-          let path = self
-             .get_piece_path(index)
-             .expect("Failed to get piece path");
-          util::write_block_to_file(path, offset, block)
-             .await
-             .expect("Failed to write block to file")
+          let path = match self.get_piece_path(index) {
+             Ok(p) => p,
+             Err(e) => {
+                warn!(index, error = %e, "Failed to get piece path");
+                return;
+             }
+          };
+          if let Err(e) = util::write_block_to_file(path, offset, block).await {
+             warn!(index, offset, error = %e, "Failed to write block to file");
+          }
        }
        PieceStorageStrategy::InFile => {
           unimplemented!()
        }
     }
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9cd0c26 and a027446.

📒 Files selected for processing (3)
  • crates/libtortillas/src/peer/actor.rs (1 hunks)
  • crates/libtortillas/src/torrent/actor.rs (2 hunks)
  • crates/libtortillas/src/torrent/messages.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 125
File: crates/libtortillas/src/torrent/actor.rs:151-171
Timestamp: 2025-08-28T06:33:16.003Z
Learning: In the torrent actor's append_peer function in crates/libtortillas/src/torrent/actor.rs, when a PeerStream is provided, the peer has already been pre-handshaked by the engine. The engine handles the initial handshake validation, info hash verification, and peer ID extraction before passing the stream to the torrent actor. Therefore, the peer.id should already be populated when a stream is provided.
Learnt from: kurealnum
Repo: artrixdotdev/tortillas PR: 99
File: crates/libtortillas/src/engine/mod.rs:153-155
Timestamp: 2025-08-18T18:25:24.831Z
Learning: The peer stream handling logic in the Engine's EngineMessage::IncomingPeer handler in crates/libtortillas/src/engine/mod.rs is temporary and planned to be migrated to separate code in the future, so extensive refactoring may not be worthwhile in this location.
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 160
File: crates/libtortillas/src/peer/actor.rs:76-76
Timestamp: 2025-10-14T04:03:55.852Z
Learning: In BitTorrent P2P networking (libtortillas crate), peer network errors (I/O errors, connection resets, unexpected EOF) occur at very high frequency as normal protocol behavior. These should be logged at trace level to avoid excessive log volume, not at debug/warn/error levels.
📚 Learning: 2025-08-28T06:33:16.003Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 125
File: crates/libtortillas/src/torrent/actor.rs:151-171
Timestamp: 2025-08-28T06:33:16.003Z
Learning: In the torrent actor's append_peer function in crates/libtortillas/src/torrent/actor.rs, when a PeerStream is provided, the peer has already been pre-handshaked by the engine. The engine handles the initial handshake validation, info hash verification, and peer ID extraction before passing the stream to the torrent actor. Therefore, the peer.id should already be populated when a stream is provided.

Applied to files:

  • crates/libtortillas/src/peer/actor.rs
  • crates/libtortillas/src/torrent/actor.rs
  • crates/libtortillas/src/torrent/messages.rs
📚 Learning: 2025-08-18T18:25:24.831Z
Learnt from: kurealnum
Repo: artrixdotdev/tortillas PR: 99
File: crates/libtortillas/src/engine/mod.rs:153-155
Timestamp: 2025-08-18T18:25:24.831Z
Learning: The peer stream handling logic in the Engine's EngineMessage::IncomingPeer handler in crates/libtortillas/src/engine/mod.rs is temporary and planned to be migrated to separate code in the future, so extensive refactoring may not be worthwhile in this location.

Applied to files:

  • crates/libtortillas/src/peer/actor.rs
  • crates/libtortillas/src/torrent/actor.rs
  • crates/libtortillas/src/torrent/messages.rs
📚 Learning: 2025-10-14T04:03:55.852Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 160
File: crates/libtortillas/src/peer/actor.rs:76-76
Timestamp: 2025-10-14T04:03:55.852Z
Learning: In BitTorrent P2P networking (libtortillas crate), peer network errors (I/O errors, connection resets, unexpected EOF) occur at very high frequency as normal protocol behavior. These should be logged at trace level to avoid excessive log volume, not at debug/warn/error levels.

Applied to files:

  • crates/libtortillas/src/peer/actor.rs
  • crates/libtortillas/src/torrent/messages.rs
📚 Learning: 2025-09-28T00:33:09.406Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 151
File: crates/libtortillas/src/torrent/messages.rs:270-274
Timestamp: 2025-09-28T00:33:09.406Z
Learning: In the tortillas codebase torrent/messages.rs file, the pattern matching `if let Some((_, mut blocks)) = previous_blocks` where `previous_blocks` comes from `self.block_map.remove(&index)` compiles correctly and works as intended.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
🧬 Code graph analysis (2)
crates/libtortillas/src/torrent/actor.rs (4)
crates/libtortillas/src/hashes.rs (1)
  • index (164-166)
crates/libtortillas/src/torrent/piece_manager.rs (3)
  • path (140-142)
  • info (17-17)
  • info (147-149)
crates/libtortillas/src/torrent/util.rs (2)
  • write_block_to_file (63-78)
  • validate_piece_file (95-113)
crates/libtortillas/src/metainfo/file.rs (3)
  • piece_count (140-142)
  • total_length (144-149)
  • read (36-39)
crates/libtortillas/src/torrent/messages.rs (3)
crates/libtortillas/src/engine/mod.rs (1)
  • actor (165-167)
crates/libtortillas/src/torrent/mod.rs (1)
  • actor (89-91)
crates/libtortillas/src/hashes.rs (1)
  • index (164-166)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (7)
crates/libtortillas/src/torrent/messages.rs (2)

78-81: LGTM!

The new PeerReady(PeerId) variant is well-documented and provides a clear signal for peer startup completion, enabling coordinated piece requests after the peer actor initializes.


216-218: LGTM!

Clean delegation to the centralized incoming_piece method improves code organization and maintainability.

crates/libtortillas/src/torrent/actor.rs (4)

17-17: LGTM!

Import addition for tokio::fs is needed for the async file operations in the new piece handling methods.


287-308: LGTM with caveat.

The logic is sound - the entry is guaranteed to exist after the insert or was already present. The unwrap() at line 306 is safe in this context.

Note: The block_index bounds issue should be addressed at the caller level (incoming_piece) as noted above.


379-430: LGTM with minor observation.

The validation and recovery logic is solid:

  • Hash validation protects against corrupt data
  • Piece manager rejection triggers proper cleanup and re-request
  • Fire-and-forget cleanup task is appropriate for non-critical file deletion

The tokio::fs::read at line 411 is properly async, avoiding blocking.


432-459: LGTM!

The logic correctly:

  • Finds the next unreceived block via position(|b| !*b)
  • Defaults to block 0 for new pieces with no block_map entry
  • Accounts for the final block being smaller than BLOCK_SIZE

The edge case where all blocks are received (returning 0) is avoided by callers checking is_piece_complete first.

crates/libtortillas/src/peer/actor.rs (1)

333-337: All verification passed. The original review comment is accurate and complete.

The code at lines 333-337 correctly sends the PeerReady notification after successfully transmitting the Bitfield. The error variant PeerActorError::SupervisorCommunicationFailed is properly defined in the codebase, and peer.id.unwrap() is safe here as confirmed by both the code comment ("At this point, the peer has already been handshaked with") and the learnings context showing that peers are pre-handshaked by the engine before reaching this point.

The implementation follows the documented protocol: the PeerReady message should be sent after PeerActor::on_start completes, which is exactly what happens here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
crates/libtortillas/src/torrent/actor.rs (1)

256-300: Add validation for offset alignment and block size.

The current bounds check at line 273 validates block_index < expected_blocks, but doesn't verify:

  1. That offset is aligned to BLOCK_SIZE boundaries
  2. That block.len() <= BLOCK_SIZE
  3. That offset + block.len() <= piece_length

A malicious or buggy peer could send misaligned offsets (e.g., offset=100, not a multiple of 16384) or oversized blocks, causing data corruption or incorrect block tracking.

Apply this diff:

     let block_index = offset / BLOCK_SIZE;
+    
+    if offset % BLOCK_SIZE != 0 {
+       warn!(index, offset, "Received block with misaligned offset");
+       return;
+    }
+    
+    if block.len() > BLOCK_SIZE {
+       warn!(index, offset, block_len = block.len(), "Received block larger than BLOCK_SIZE");
+       return;
+    }
+    
+    if offset + block.len() > piece_length {
+       warn!(index, offset, block_len = block.len(), piece_length, "Received block exceeds piece boundary");
+       return;
+    }
+    
     if block_index >= expected_blocks {
🧹 Nitpick comments (1)
crates/libtortillas/src/torrent/actor.rs (1)

325-342: Document the unimplemented InFile strategy.

While PieceStorageStrategy::InFile is unimplemented, it would be helpful to add a comment or TODO explaining when this will be implemented, since it's the default strategy (as noted in the enum documentation at line 63).

Consider adding:

         PieceStorageStrategy::InFile => {
-            unimplemented!()
+            unimplemented!("InFile storage strategy not yet implemented; use Disk strategy for now")
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a027446 and 7d3d936.

📒 Files selected for processing (2)
  • crates/libtortillas/src/torrent/actor.rs (2 hunks)
  • crates/libtortillas/src/torrent/messages.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 125
File: crates/libtortillas/src/torrent/actor.rs:151-171
Timestamp: 2025-08-28T06:33:16.003Z
Learning: In the torrent actor's append_peer function in crates/libtortillas/src/torrent/actor.rs, when a PeerStream is provided, the peer has already been pre-handshaked by the engine. The engine handles the initial handshake validation, info hash verification, and peer ID extraction before passing the stream to the torrent actor. Therefore, the peer.id should already be populated when a stream is provided.
Learnt from: kurealnum
Repo: artrixdotdev/tortillas PR: 99
File: crates/libtortillas/src/engine/mod.rs:153-155
Timestamp: 2025-08-18T18:25:24.831Z
Learning: The peer stream handling logic in the Engine's EngineMessage::IncomingPeer handler in crates/libtortillas/src/engine/mod.rs is temporary and planned to be migrated to separate code in the future, so extensive refactoring may not be worthwhile in this location.
📚 Learning: 2025-08-18T18:25:24.831Z
Learnt from: kurealnum
Repo: artrixdotdev/tortillas PR: 99
File: crates/libtortillas/src/engine/mod.rs:153-155
Timestamp: 2025-08-18T18:25:24.831Z
Learning: The peer stream handling logic in the Engine's EngineMessage::IncomingPeer handler in crates/libtortillas/src/engine/mod.rs is temporary and planned to be migrated to separate code in the future, so extensive refactoring may not be worthwhile in this location.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
  • crates/libtortillas/src/torrent/actor.rs
📚 Learning: 2025-08-28T06:33:16.003Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 125
File: crates/libtortillas/src/torrent/actor.rs:151-171
Timestamp: 2025-08-28T06:33:16.003Z
Learning: In the torrent actor's append_peer function in crates/libtortillas/src/torrent/actor.rs, when a PeerStream is provided, the peer has already been pre-handshaked by the engine. The engine handles the initial handshake validation, info hash verification, and peer ID extraction before passing the stream to the torrent actor. Therefore, the peer.id should already be populated when a stream is provided.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
  • crates/libtortillas/src/torrent/actor.rs
📚 Learning: 2025-10-14T04:03:55.852Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 160
File: crates/libtortillas/src/peer/actor.rs:76-76
Timestamp: 2025-10-14T04:03:55.852Z
Learning: In BitTorrent P2P networking (libtortillas crate), peer network errors (I/O errors, connection resets, unexpected EOF) occur at very high frequency as normal protocol behavior. These should be logged at trace level to avoid excessive log volume, not at debug/warn/error levels.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
  • crates/libtortillas/src/torrent/actor.rs
📚 Learning: 2025-09-28T00:33:09.406Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 151
File: crates/libtortillas/src/torrent/messages.rs:270-274
Timestamp: 2025-09-28T00:33:09.406Z
Learning: In the tortillas codebase torrent/messages.rs file, the pattern matching `if let Some((_, mut blocks)) = previous_blocks` where `previous_blocks` comes from `self.block_map.remove(&index)` compiles correctly and works as intended.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
  • crates/libtortillas/src/torrent/actor.rs
📚 Learning: 2025-09-28T00:33:09.406Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 151
File: crates/libtortillas/src/torrent/messages.rs:270-274
Timestamp: 2025-09-28T00:33:09.406Z
Learning: In the tortillas codebase, HashMap::remove operations on block_map work correctly with tuple destructuring patterns like Some((_, mut blocks)) in the torrent message handling code.

Applied to files:

  • crates/libtortillas/src/torrent/actor.rs
🧬 Code graph analysis (2)
crates/libtortillas/src/torrent/messages.rs (2)
crates/libtortillas/src/torrent/mod.rs (2)
  • actor (89-91)
  • state (274-286)
crates/libtortillas/src/torrent/actor.rs (1)
  • is_ready (637-639)
crates/libtortillas/src/torrent/actor.rs (3)
crates/libtortillas/src/torrent/piece_manager.rs (3)
  • info (17-17)
  • info (147-149)
  • path (140-142)
crates/libtortillas/src/torrent/util.rs (2)
  • write_block_to_file (63-78)
  • validate_piece_file (95-113)
crates/libtortillas/src/metainfo/file.rs (3)
  • piece_count (140-142)
  • total_length (144-149)
  • read (36-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (8)
crates/libtortillas/src/torrent/messages.rs (2)

78-80: LGTM! Well-documented new message variant.

The PeerReady(PeerId) variant is clearly documented and enables the torrent actor to coordinate piece requests after a peer signals readiness.


216-218: LGTM! Clean delegation to centralized piece handling.

The delegation to self.incoming_piece() centralizes the piece processing logic, which improves maintainability.

crates/libtortillas/src/torrent/actor.rs (6)

17-17: LGTM! Necessary import for file operations.

The addition of fs from tokio enables the new disk-based piece storage operations.


302-323: LGTM! Proper initialization and marking logic.

The method correctly initializes the block map on first access and safely marks blocks as received.


344-393: LGTM! Comprehensive piece completion flow.

The piece completion logic correctly:

  • Validates and sends the piece to the manager
  • Updates the bitfield and next_piece index
  • Notifies peers and trackers
  • Handles the transition to Seeding when complete (line 380 uses correct >= piece_count check)

395-446: LGTM! Robust validation and error handling.

The validation flow correctly:

  • Validates piece hashes before accepting
  • Cleans up invalid pieces
  • Re-requests pieces on validation or manager rejection
  • Properly restores block map state for retry

448-475: LGTM! Correct calculation of next block coordinates.

The method properly:

  • Finds the next unreceived block in the block map
  • Handles the final block being smaller than BLOCK_SIZE
  • Returns coordinates suitable for the NeedPiece message

477-491: LGTM! Safe bounds checking in helper methods.

Both is_duplicate_block and is_piece_complete use safe access patterns (.get(), .and_then()) that prevent panics on invalid indices.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
crates/libtortillas/src/torrent/messages.rs (2)

76-92: Consider adding explicit Debug formatting for PeerReady (and tiny doc nit)

Right now TorrentMessage::PeerReady falls into the catch‑all _ => "TorrentMessage" arm in the Debug impl, which makes logs less informative. You could add an explicit arm for easier debugging, for example:

 impl fmt::Debug for TorrentMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
       match self {
+         TorrentMessage::PeerReady(peer_id) => write!(f, "PeerReady({peer_id:?})"),
          TorrentMessage::InfoBytes(bytes) => write!(f, "InfoBytes({bytes:?})"),
          TorrentMessage::KillPeer(peer_id) => write!(f, "KillPeer({peer_id:?})"),
          TorrentMessage::KillTracker(tracker) => write!(f, "KillTracker({tracker:?})"),
          TorrentMessage::AddPeer(peer) => write!(f, "AddPeer({peer:?})"),
          TorrentMessage::IncomingPiece(index, offset, data) => {
             write!(f, "IncomingPiece({index}, {offset}, {})", data.len())
          }
          TorrentMessage::Announce(peers) => write!(f, "Announce({peers:?})"),
          _ => write!(f, "TorrentMessage"), // Add more later,
       }
    }
 }

Also, optional grammar nit in the doc: “is ran” → “has run”.


194-215: PeerReady guard semantics look sane; consider softening the send failure path

The PeerReady handling is nicely constrained (known peer, alive, Downloading state, and is_ready()), and delegating IncomingPiece to self.incoming_piece(..).await makes the control flow clearer.

Two small recommendations:

  1. Avoid panicking on a transient send failure

There’s a race window where the peer can die between actor.is_alive() and .tell(..).await, in which case the expect will tear down the torrent actor. If you’d rather keep the torrent running and just skip this peer, you could log the failure at trace level instead of panicking, e.g.:

-               actor
-                  .tell(PeerTell::NeedPiece(piece_idx, block_offset, block_length))
-                  .await
-                  .expect("Failed to send piece request to peer");
-               trace!(peer_id = %id, piece_idx, block_offset, block_length, "Requested piece from new peer");
+               if let Err(err) =
+                  actor
+                     .tell(PeerTell::NeedPiece(piece_idx, block_offset, block_length))
+                     .await
+               {
+                  trace!(
+                     peer_id = %id,
+                     ?err,
+                     piece_idx,
+                     block_offset,
+                     block_length,
+                     "Failed to send piece request to peer; skipping peer"
+                  );
+               } else {
+                  trace!(
+                     peer_id = %id,
+                     piece_idx,
+                     block_offset,
+                     block_length,
+                     "Requested piece from new peer"
+                  );
+               }
  1. Double‑check that dropping PeerReady outside Downloading/is_ready() is intended

The guard plus “Ignoring PeerReady…” trace makes behavior explicit, but it also means peers that connect before you transition to Downloading (or before is_ready() is true) won’t get an initial request from this hook; they’ll rely on other scheduling paths (start(), etc.). If that’s the desired contract, this is fine; otherwise, you may want to queue or re‑evaluate peers when the state flips to Downloading.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d3d936 and 5f62cad.

📒 Files selected for processing (1)
  • crates/libtortillas/src/torrent/messages.rs (3 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 125
File: crates/libtortillas/src/torrent/actor.rs:151-171
Timestamp: 2025-08-28T06:33:16.003Z
Learning: In the torrent actor's append_peer function in crates/libtortillas/src/torrent/actor.rs, when a PeerStream is provided, the peer has already been pre-handshaked by the engine. The engine handles the initial handshake validation, info hash verification, and peer ID extraction before passing the stream to the torrent actor. Therefore, the peer.id should already be populated when a stream is provided.
Learnt from: kurealnum
Repo: artrixdotdev/tortillas PR: 99
File: crates/libtortillas/src/engine/mod.rs:153-155
Timestamp: 2025-08-18T18:25:24.831Z
Learning: The peer stream handling logic in the Engine's EngineMessage::IncomingPeer handler in crates/libtortillas/src/engine/mod.rs is temporary and planned to be migrated to separate code in the future, so extensive refactoring may not be worthwhile in this location.
📚 Learning: 2025-08-18T18:25:24.831Z
Learnt from: kurealnum
Repo: artrixdotdev/tortillas PR: 99
File: crates/libtortillas/src/engine/mod.rs:153-155
Timestamp: 2025-08-18T18:25:24.831Z
Learning: The peer stream handling logic in the Engine's EngineMessage::IncomingPeer handler in crates/libtortillas/src/engine/mod.rs is temporary and planned to be migrated to separate code in the future, so extensive refactoring may not be worthwhile in this location.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
📚 Learning: 2025-08-28T06:33:16.003Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 125
File: crates/libtortillas/src/torrent/actor.rs:151-171
Timestamp: 2025-08-28T06:33:16.003Z
Learning: In the torrent actor's append_peer function in crates/libtortillas/src/torrent/actor.rs, when a PeerStream is provided, the peer has already been pre-handshaked by the engine. The engine handles the initial handshake validation, info hash verification, and peer ID extraction before passing the stream to the torrent actor. Therefore, the peer.id should already be populated when a stream is provided.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
📚 Learning: 2025-10-14T04:03:55.852Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 160
File: crates/libtortillas/src/peer/actor.rs:76-76
Timestamp: 2025-10-14T04:03:55.852Z
Learning: In BitTorrent P2P networking (libtortillas crate), peer network errors (I/O errors, connection resets, unexpected EOF) occur at very high frequency as normal protocol behavior. These should be logged at trace level to avoid excessive log volume, not at debug/warn/error levels.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
📚 Learning: 2025-09-28T00:33:09.406Z
Learnt from: artrixdotdev
Repo: artrixdotdev/tortillas PR: 151
File: crates/libtortillas/src/torrent/messages.rs:270-274
Timestamp: 2025-09-28T00:33:09.406Z
Learning: In the tortillas codebase torrent/messages.rs file, the pattern matching `if let Some((_, mut blocks)) = previous_blocks` where `previous_blocks` comes from `self.block_map.remove(&index)` compiles correctly and works as intended.

Applied to files:

  • crates/libtortillas/src/torrent/messages.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (1)
crates/libtortillas/src/torrent/messages.rs (1)

15-26: Imports alignment with new behavior looks good

The expanded tracing import and the added util/Tracker imports line up with usages in this file (piece storage dir creation, tracker types, and new trace logging). No issues here.

@artrixdotdev artrixdotdev merged commit 5b53072 into main Dec 4, 2025
3 checks passed
@artrixdotdev artrixdotdev deleted the feat/request-piece-on-connect branch December 4, 2025 01:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request med prio Medium Priority refactor Neither fixes a bug nor adds a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Send new peers piece requests on connect

1 participant