-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
It seems persisted data about torrents is not loaded from database #1264
Comments
How to reproduce
cd console/tracker-client/
cargo run --bin udp_tracker_client announce udp://127.0.0.1:6969 9c38422213e30bff212b30c360d26f9a02136422 | jq You should see something like:
Update the number of downloads manually:
You can also use the tracker API: http://0.0.0.0:1212/api/v1/torrent/9c38422213e30bff212b30c360d26f9a02136422?token=MyAccessToken {
"info_hash": "9c38422213e30bff212b30c360d26f9a02136422",
"seeders": 1,
"completed": 2,
"leechers": 0,
"peers": [
{
"peer_id": {
"id": "0x2d71423030303030303030303030303030303031",
"client": null
},
"peer_addr": "0.0.0.0:17548",
"updated": 1741179730781,
"updated_milliseconds_ago": 1741179730781,
"uploaded": 0,
"downloaded": 0,
"left": 0,
"event": "Completed"
}
]
} The |
I think the naive implementation would be to load the persistent torrents initially when the tracker starts but that has some problems:
I have to think a good solution. cc @da2ce7 |
… after upserting a peer When you upsert a new peer (announce requests) the swarm stats migth change if the peer announced that has completed the download. If that case we return `true` given the caller the opportunity to know if stats have changed. In the current implementation we get stats before and after upserintg the peer, and we compare them to check if they have changed. However stats migth have change due to a parallel (race condition) announce from another peer. This is the only way to know exactly if this announce request has altered the stats (incremented the number of downloads).
Now the `upsert_peer` fn returns `true`if the stats have changed. IN the other hand, stats migth have changed ebcuase there was a different paralell announce request (race condtions) so it migth not be needed. This removes unnecessary database queries to persits the stats.
…ted downloads for one torrent This will be used to preset the counter from the database when a noew torrent entry is added to the torrent repo after begin removing for a while (peerless torrents are removed). The process is: - A new torrent is announced and added to the torrent repo. - The downloads counter is 0. - One of the peers finishes downloading and the counter increases to 1. - The torrent is removed from the repo becuase none of the peer announce for a while. - A new peer announce the torrent. We have to preset the counter to 1 even if that peer has not completed downloading yet.
Keep the "loaded" torrents in a separate structure, move them into the normal structure if they get announced. The "loaded" structure can optionally expire after a configurable time. 😊 |
Hi @da2ce7 there are two problems with that approach:
What I'm implementing in the PR is:
All of that it's done only if persisting stats is enabled ( I think I will merge the PR once that patch is implemented. However I think we can implement a better solution (I will open a new issue for that):
|
… of downloads for a torrent
By using the new database trait method `increase_number_of_downloads`.
…sited However, we still have to load the value counter from the database the first time the otrrent is added to the repository.
This fixed a bug: the number of donwloads for a torrent is not loaded from the database when the torrent is added to the in-memory torrent repository. It should be done when stats are enabled with the configuration option: persistent_torrent_completed_stat = true The patch is applied only to the torrent repository implementation we are using in prodcution (`CrossbeamSkipList<EntryMutexStd>`). The other implementations have this comment: ``` // todo: load persistent torrent data if provided ``` It was not implemented for the others becuase I'm considering taking the counter (for the number of downloads) out of the in-memory repository. And increase it by suing events triggered from the core tracker. I will open a new issue for that. If that's implemented we will need to remove this patch for the repository, meaning reverting cahnges in this commit.
Hi @da2ce7 I've fixed the problem with a temporary path. I will open a new issue with a proposal for a better solution. In the mean time at least the bug it's fixed. I have made two main changes:
pub async fn announce(
&self,
info_hash: &InfoHash,
peer: &mut peer::Peer,
remote_client_ip: &IpAddr,
peers_wanted: &PeersWanted,
) -> Result<AnnounceData, AnnounceError> {
self.whitelist_authorization.authorize(info_hash).await?;
let opt_persistent_torrent = if self.config.tracker_policy.persistent_torrent_completed_stat {
self.db_torrent_repository.load(info_hash)?
} else {
None
};
peer.change_ip(&assign_ip_address_to_peer(remote_client_ip, self.config.net.external_ip));
let number_of_downloads_increased =
self.in_memory_torrent_repository
.upsert_peer(info_hash, peer, opt_persistent_torrent);
if self.config.tracker_policy.persistent_torrent_completed_stat && number_of_downloads_increased {
self.db_torrent_repository.increase_number_of_downloads(info_hash)?;
}
Ok(self.build_announce_data(info_hash, peer, peers_wanted))
} In the core announce handler we check if the torrent is new:
Only if the torrent is new. impl Repository<EntryMutexStd> for CrossbeamSkipList<EntryMutexStd>
where
EntryMutexStd: EntrySync,
EntrySingle: Entry,
{
/// Upsert a peer into the swarm of a torrent.
///
/// Optionally, it can also preset the number of downloads of the torrent
/// only if it's the first time the torrent is being inserted.
///
/// # Arguments
///
/// * `info_hash` - The info hash of the torrent.
/// * `peer` - The peer to upsert.
/// * `opt_persistent_torrent` - The optional persisted data about a torrent
/// (number of downloads for the torrent).
///
/// # Returns
///
/// Returns `true` if the number of downloads was increased because the peer
/// completed the download.
fn upsert_peer(&self, info_hash: &InfoHash, peer: &peer::Peer, opt_persistent_torrent: Option<PersistentTorrent>) -> bool {
if let Some(existing_entry) = self.torrents.get(info_hash) {
existing_entry.value().upsert_peer(peer)
} else {
let new_entry = if let Some(number_of_downloads) = opt_persistent_torrent {
EntryMutexStd::new(
EntrySingle {
swarm: PeerList::default(),
downloaded: number_of_downloads,
}
.into(),
)
} else {
EntryMutexStd::default()
};
let inserted_entry = self.torrents.get_or_insert(*info_hash, new_entry);
inserted_entry.value().upsert_peer(peer)
}
}
} NOTES
With the new implementation the handler would be: pub async fn announce(
&self,
info_hash: &InfoHash,
peer: &mut peer::Peer,
remote_client_ip: &IpAddr,
peers_wanted: &PeersWanted,
) -> Result<AnnounceData, AnnounceError> {
self.whitelist_authorization.authorize(info_hash).await?;
peer.change_ip(&assign_ip_address_to_peer(remote_client_ip, self.config.net.external_ip));
self.in_memory_torrent_repository.upsert_peer(info_hash, peer);
Ok(self.build_announce_data(info_hash, peer, peers_wanted))
} Notice there is no code related to stats at all and |
06ef1d5 fix: [#1264] number of downloads preset when torrent is persisted (Jose Celano) 8f67f12 fix: [#1264] partially. The correct number of downloads is persited (Jose Celano) 6beec3a refactor: [#1264] add a database method to increase the number of downloads for a torrent (Jose Celano) 8a4dba3 refactor: [#1264] rename variables (Jose Celano) 2fb1c6f reafactor: [#1264] add a DB methog to get the number of completed downloads for one torrent (Jose Celano) c5db71a refactor: [#1264] remove unnecessary fn call (Jose Celano) 820329b refactor: [#1264] return wether swarm stats have change or not after upserting a peer (Jose Celano) Pull request description: ### Problem **The number of downloads for a torrent is not persisted correctly**. Even when persistence is enabled, it does not work. There are two cases when the counter is reset: - When you restart the tracker. - When a torrent entry is removed from the torrents repository because the last peer was removed (peerless torrents). This only happens if "removing peerless torrents" is enabled in the configuration. Check the [issue](#1264) to know how to reproduce the bug. ### Solution Every time a new torrent entry is added in the torrent repository we need to load stats (also called persistent torrent, which only includes number of downloads currently) from the database. If persistence for stats is enabled. ### Other considerations **1. Unused code** There is a method to load the counters for all torrents but it's not used in production code. ```rust fn import_persistent(&self, persistent_torrents: &PersistentTorrents) { for (info_hash, completed) in persistent_torrents { if self.torrents.contains_key(info_hash) { continue; } let entry = EntryMutexStd::new( EntrySingle { swarm: PeerList::default(), downloaded: *completed, } .into(), ); self.torrents.insert(*info_hash, entry); } } ``` I guess it was added to load the information from the database when the tracker is restarted. However that's not enough because torrents are removed from the repository when they do not have peers (peerless torrents). That would work only if removing peerless torrents is disabled. **2. Better solution** I think there is a better solution than the one this PR applies. This PR is a temporary patch to fix the bug asap. I would differentiate to different counters: 1. number of downloads per torrent while it was active in the swarm (since the tracker was restarted) 2. historical number of downloads per torrent per web service (since the service was put into production) The counter 1 is what we have before merging this PR. The counter is reset every time the torrent is removed from the swarm. We can keep like it's now before merging this PR. This counter is handled at the in-memory torrent repository. The new counter (counter 2) would be handled by the announce handler. After processing the announce request if the number of downloads has increased the handler also increases the historical counter. This new value should be the one exposed in the API. ```json { "torrents": 379508, "seeders": 150440, "completed": 58992, <- this should be the historical "leechers": 264865, "tcp4_connections_handled": 15, "tcp4_announces_handled": 15, "tcp4_scrapes_handled": 0, "tcp6_connections_handled": 0, "tcp6_announces_handled": 0, "tcp6_scrapes_handled": 0, "udp_requests_aborted": 556458, "udp_requests_banned": 24773536, "udp_banned_ips_total": 29868, "udp_avg_connect_processing_time_ns": 478212, "udp_avg_announce_processing_time_ns": 8724637, "udp_avg_scrape_processing_time_ns": 296950, "udp4_requests": 298728340, "udp4_connections_handled": 104369821, "udp4_announces_handled": 158354656, "udp4_scrapes_handled": 3664492, "udp4_responses": 273954123, "udp4_errors_handled": 7540716, "udp6_requests": 0, "udp6_connections_handled": 0, "udp6_announces_handled": 0, "udp6_scrapes_handled": 0, "udp6_responses": 0, "udp6_errors_handled": 0 } ``` This patches solves the problem but pre-loading the historical data every time the torrent is added to the repository. ACKs for top commit: josecelano: ACK 06ef1d5 Tree-SHA512: 2d1ad14d26efa981d24b1264640b5e2deaca621092ec61638e85e21dc83f92ad53a4041cb97e4e798611f3bfed610c7794b68027e43a847751c8ec022abc6b19
It seems persisted data about torrents (number of downloads) is not loaded from database when the tracker stars. The
start
function loads the authentication keys and the whitelist:However I don't see where we load torrent data. The method
TorrentsManager::load_torrents_from_database
is only used in test code.Maybe that was the expected behavior. However that means the number of downloaded torrents in the database is reset every time the tracker is restarted because the
persist_stats
function only overwrite the in-memory counter. That in-memory counter is not preloaded with the database value when the tracker starts.cc @da2ce7
The text was updated successfully, but these errors were encountered: