Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughVersion 4.2.13 centralizes config defaults, introduces TorrentUpdateData and AnnounceEntry, migrates tracker persistence and DB backends to update summaries, adds a Windows RIO UDP backend with UdpReply-based dispatch, and updates HTTP/WebSocket handlers and tests for the new snapshot-based flows. ChangesWindows RIO UDP and Tracker Data Refactoring
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/tracker/impls/torrent_tracker_handlers.rs (2)
244-269:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't enqueue a zeroed torrent update for a no-op
stoppedannounce.The wildcard arm on Line 263 collapses “peer/torrent not found” and “torrent removed entirely” into the same
AnnounceEntry::default(), and the unconditionaladd_torrent_updateright after that writes zeros in both cases. A repeated or bogusstoppedrequest can therefore create or overwrite a zero-count cache/DB record for an info hash that was never present.Suggested fix
- let torrent_entry = match data.remove_torrent_peer( + let (torrent_entry, should_update) = match data.remove_torrent_peer( announce_query.info_hash, announce_query.peer_id, is_persistent, false ) { - (Some(_), Some(new_torrent)) => { + (Some(_), Some(new_torrent)) => { if users_enabled && let Some(user_id) = user_key && let Some(mut user) = data.get_user(user_id) { user.uploaded += announce_query.uploaded; user.downloaded += announce_query.downloaded; user.updated = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); user.torrents_active.remove(&announce_query.info_hash); data.add_user(user_id, user.clone()); if is_users_persistent { data.add_user_update(user_id, user, UpdatesAction::Add); } } - new_torrent + (new_torrent, true) } - _ => AnnounceEntry::default() + (Some(_), None) => (AnnounceEntry::default(), true), + _ => (AnnounceEntry::default(), false), }; - if needs_update { + if needs_update && should_update { let _ = data.add_torrent_update( announce_query.info_hash, TorrentUpdateData::from(&torrent_entry), UpdatesAction::Add );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/tracker/impls/torrent_tracker_handlers.rs` around lines 244 - 269, The code unconditionally enqueues a zeroed TorrentUpdate when remove_torrent_peer falls through to AnnounceEntry::default(), causing bogus zeroed records on no-op stopped announces; change the logic around the match on data.remove_torrent_peer (the branch that currently returns AnnounceEntry::default()) to detect the “not found / no-op stopped” case and skip calling data.add_torrent_update when there is no real torrent to update (i.e., only call data.add_torrent_update when the match yields Some(new_torrent) or when torrent_entry represents a real torrent state change), using announce_query.stopped or announce_query.event to distinguish stop no-ops from genuine removals and preserving the existing needs_update behavior otherwise.
291-315:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse
needs_updatein thecompletedpath as well.Started and Stopped both propagate
TorrentUpdateDatawhen persistence or cache is enabled, but Completed only does so whenis_persistentis true. In cache-only deployments that drops completed/count updates entirely until some later announce touches the torrent.Suggested fix
- if is_persistent { + if needs_update { let _ = data.add_torrent_update( announce_query.info_hash, TorrentUpdateData::from(&rtc_entry), UpdatesAction::Add ); } @@ - if is_persistent { + if needs_update { let _ = data.add_torrent_update( announce_query.info_hash, TorrentUpdateData::from(&torrent_entry), UpdatesAction::Add ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/tracker/impls/torrent_tracker_handlers.rs` around lines 291 - 315, The completed branch only writes TorrentUpdateData when is_persistent is true; change those guards to include the cache-update flag by using needs_update (e.g. if needs_update || is_persistent { ... }) for the torrent update (the add_torrent_update call using TorrentUpdateData::from(&rtc_entry)) and similarly include needs_update when deciding to call data.add_user_update for the user update (e.g. if is_users_persistent || needs_update { ... }). Ensure you update both the completed-path checks so cache-only deployments also propagate the completed/count updates.src/database/impls/database_connector_mysql.rs (1)
246-253:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftPersist total seed/peer counts here, not just the IPv4 buckets.
column_seedsandcolumn_peersare the only persisted counters for a torrent, but this now writescounts.seeds_ipv4/counts.peers_ipv4directly. Any IPv6 or RTC peers disappear from the stored totals, so scrape data and post-restart state will undercount after a sync. The same regression is repeated in the PgSQL and SQLitesave_torrentsimplementations in this PR.Also applies to: 280-287
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/database/impls/database_connector_mysql.rs` around lines 246 - 253, The code is persisting only IPv4 buckets (counts.seeds_ipv4/counts.peers_ipv4) into the aggregate columns; change the upsert to write the total seed/peer counters instead (use the overall counters on the counts struct, e.g. counts.seeds and counts.peers or counts.seeds_total/counts.peers_total as available) when calling build_upsert_torrent_query for &structure.column_seeds and &structure.column_peers; make the identical change in the save_torrents implementations for PgSQL and SQLite so the persisted totals include IPv6/RTC counts as well.src/tracker/impls/torrent_tracker_peers.rs (1)
141-150:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove the previous peer from both IP-family maps before re-inserting.
This replacement path only deletes the old entry from the incoming peer's current family. If the same
peer_idre-announces on IPv6 after previously existing on IPv4 (or the reverse), the stale entry survives in the old map and then a second entry is inserted into the new map, which inflates both peer lists and stats.Suggested fix
- let (seeds_removed, peers_removed) = if torrent_peer.peer_addr.is_ipv4() { - ( - i64::from(entry.seeds.remove(&peer_id).is_some()), - i64::from(entry.peers.remove(&peer_id).is_some()), - ) - } else { - ( - i64::from(entry.seeds_ipv6.remove(&peer_id).is_some()), - i64::from(entry.peers_ipv6.remove(&peer_id).is_some()), - ) - }; + let seeds_removed = i64::from(entry.seeds.remove(&peer_id).is_some()) + + i64::from(entry.seeds_ipv6.remove(&peer_id).is_some()); + let peers_removed = i64::from(entry.peers.remove(&peer_id).is_some()) + + i64::from(entry.peers_ipv6.remove(&peer_id).is_some());Also applies to: 193-206
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/tracker/impls/torrent_tracker_peers.rs` around lines 141 - 150, The code only removes the peer_id from the IP-family matching the incoming announce, leaving a stale entry in the opposite family; change the logic so you remove peer_id from both IPv4 and IPv6 maps before inserting the new record: call entry.seeds.remove(&peer_id), entry.peers.remove(&peer_id), entry.seeds_ipv6.remove(&peer_id) and entry.peers_ipv6.remove(&peer_id) (or perform a small helper like remove_peer_from_all_families(peer_id, &mut entry)) and then compute seeds_removed/peers_removed based on whether any of the removes returned true; apply the same fix to both places where this branch logic appears (the blocks handling removal/re-insert around torrent_peer and peer_id).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Cargo.toml`:
- Around line 8-9: Add the minimum supported Rust version to Cargo.toml by
setting the package rust-version field to match README.md (1.88.0); update the
Cargo.toml [package] section to include rust-version = "1.88.0" so Cargo/tooling
will enforce the MSRV and reject older toolchains (ensure the value exactly
matches the README.md stated version).
In `@src/udp/impls/parse_pool.rs`:
- Around line 55-75: The loop currently always awaits
tokio::time::sleep(poll_interval) between iterations causing a fixed delay even
when work is available; change the control flow so you only sleep when no work
was found. Concretely, in the loop that uses poll_interval, POLL_MIN, POLL_MAX,
BATCH_SIZE, payload and batch (and still honoring shutdown_handler.changed()),
attempt to drain up to BATCH_SIZE into batch and process/drain consecutive
non-empty batches immediately (i.e., loop draining and processing until a drain
yields an empty batch), only then set/increase poll_interval and await
tokio::time::sleep(poll_interval); reset poll_interval to POLL_MIN when work was
processed and back off (multiply/min) only after a poll found no work. This
removes the sleep between back-to-back batches while preserving backoff when
idle.
In `@src/udp/impls/rio_recv.rs`:
- Around line 117-137: The Winsock initialization in is_available() and setup()
calls WSAStartup but never calls WSACleanup, leaking reference counts; update
is_available(), setup(), and any early-return paths to ensure WSACleanup is
called after a successful WSAStartup (use an RAII guard that calls WSACleanup in
Drop or explicitly call WSACleanup on every exit), e.g., wrap the WSAStartup
result in a small struct (WinsockGuard) or ensure after load_rio_table/socket
cleanup you call WSACleanup before returning, and ensure RioContext::drop
remains responsible only for socket/RIO resources while the new guard handles
WSACleanup.
In `@src/udp/impls/udp_server.rs`:
- Around line 163-169: The code currently panics on thread spawn failure via
.expect("failed to spawn RIO receive thread"); change this to handle the Result
from std::thread::Builder::new().spawn(...) gracefully: match or if let Err(e)
=> log the error (using the existing logger) and avoid aborting, and only
proceed to await rx.changed() when the thread was spawned successfully;
reference the spawn call created by std::thread::Builder::new().name("udp-rio")
and the action inside crate::udp::impls::rio_recv::run(bind_address, ...) so you
log the spawn error and skip or recover instead of panicking, ensuring
rx.changed().await.ok() is not awaited unconditionally when spawn failed.
---
Outside diff comments:
In `@src/database/impls/database_connector_mysql.rs`:
- Around line 246-253: The code is persisting only IPv4 buckets
(counts.seeds_ipv4/counts.peers_ipv4) into the aggregate columns; change the
upsert to write the total seed/peer counters instead (use the overall counters
on the counts struct, e.g. counts.seeds and counts.peers or
counts.seeds_total/counts.peers_total as available) when calling
build_upsert_torrent_query for &structure.column_seeds and
&structure.column_peers; make the identical change in the save_torrents
implementations for PgSQL and SQLite so the persisted totals include IPv6/RTC
counts as well.
In `@src/tracker/impls/torrent_tracker_handlers.rs`:
- Around line 244-269: The code unconditionally enqueues a zeroed TorrentUpdate
when remove_torrent_peer falls through to AnnounceEntry::default(), causing
bogus zeroed records on no-op stopped announces; change the logic around the
match on data.remove_torrent_peer (the branch that currently returns
AnnounceEntry::default()) to detect the “not found / no-op stopped” case and
skip calling data.add_torrent_update when there is no real torrent to update
(i.e., only call data.add_torrent_update when the match yields Some(new_torrent)
or when torrent_entry represents a real torrent state change), using
announce_query.stopped or announce_query.event to distinguish stop no-ops from
genuine removals and preserving the existing needs_update behavior otherwise.
- Around line 291-315: The completed branch only writes TorrentUpdateData when
is_persistent is true; change those guards to include the cache-update flag by
using needs_update (e.g. if needs_update || is_persistent { ... }) for the
torrent update (the add_torrent_update call using
TorrentUpdateData::from(&rtc_entry)) and similarly include needs_update when
deciding to call data.add_user_update for the user update (e.g. if
is_users_persistent || needs_update { ... }). Ensure you update both the
completed-path checks so cache-only deployments also propagate the
completed/count updates.
In `@src/tracker/impls/torrent_tracker_peers.rs`:
- Around line 141-150: The code only removes the peer_id from the IP-family
matching the incoming announce, leaving a stale entry in the opposite family;
change the logic so you remove peer_id from both IPv4 and IPv6 maps before
inserting the new record: call entry.seeds.remove(&peer_id),
entry.peers.remove(&peer_id), entry.seeds_ipv6.remove(&peer_id) and
entry.peers_ipv6.remove(&peer_id) (or perform a small helper like
remove_peer_from_all_families(peer_id, &mut entry)) and then compute
seeds_removed/peers_removed based on whether any of the removes returned true;
apply the same fix to both places where this branch logic appears (the blocks
handling removal/re-insert around torrent_peer and peer_id).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 97906b7f-54e2-4958-adbb-c0cfe7459bc5
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (50)
Cargo.tomlREADME.mddocker/Dockerfiledocker/build.batsrc/api/api_torrents.rssrc/common/common.rssrc/config/config.rssrc/config/enums/udp_receive_method.rssrc/config/impls.rssrc/config/impls/configuration.rssrc/config/impls/sentry_config.rssrc/config/mod.rssrc/config/structs/sentry_config.rssrc/config/structs/tracker_config.rssrc/database/impls/database_connector.rssrc/database/impls/database_connector_mysql.rssrc/database/impls/database_connector_pgsql.rssrc/database/impls/database_connector_sqlite.rssrc/database/traits/database_backend.rssrc/http/http.rssrc/stats/impls/torrent_tracker.rssrc/tracker/impls.rssrc/tracker/impls/announce_entry.rssrc/tracker/impls/torrent_counts.rssrc/tracker/impls/torrent_sharding.rssrc/tracker/impls/torrent_tracker_handlers.rssrc/tracker/impls/torrent_tracker_import.rssrc/tracker/impls/torrent_tracker_peers.rssrc/tracker/impls/torrent_tracker_rtctorrent.rssrc/tracker/impls/torrent_tracker_torrents.rssrc/tracker/impls/torrent_tracker_torrents_updates.rssrc/tracker/impls/torrent_update_data.rssrc/tracker/structs.rssrc/tracker/structs/announce_entry.rssrc/tracker/structs/torrent_counts.rssrc/tracker/structs/torrent_sharding.rssrc/tracker/structs/torrent_update_data.rssrc/tracker/types/torrents_updates.rssrc/udp/enums.rssrc/udp/enums/udp_reply.rssrc/udp/impls.rssrc/udp/impls/io_uring_recv.rssrc/udp/impls/parse_pool.rssrc/udp/impls/rio_recv.rssrc/udp/impls/udp_server.rssrc/udp/structs/udp_packet.rssrc/udp/structs/udp_server.rssrc/websocket/websocket.rstests/tracker_tests.rstorrust-actix.desktop
💤 Files with no reviewable changes (1)
- src/tracker/structs/torrent_counts.rs
v4.2.13
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Dependencies