swarm: re-announce promptly when the swarm is starved
The peer top-up loop re-announced to trackers only at the full advertised interval (~30 min), so a torrent that dropped to a handful of peers would sit there for up to half an hour with only the 5-minute DHT refresh to help — looking like the re-announce system was dead. Now, when fewer than LOW_PEER_THRESHOLD (10) peers are connected, the next tracker announce is scheduled at the tracker's min_interval floor (never below 60s) instead of the full interval, so a thin swarm actually tries to recover. Once peers recover the full interval is used again. Capture the tracker's min_interval (was being dropped) to stay announce-compliant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a0265cc1ea
commit
096535292d
3 changed files with 38 additions and 14 deletions
|
|
@ -58,6 +58,10 @@ static uint32_t target_peer_count(void) {
|
|||
return (uint32_t)NAUT_MIN(value, MAX_TARGET_PEERS);
|
||||
}
|
||||
|
||||
/* Below this many connected peers the swarm is "starved": re-announce as often
|
||||
* as the tracker's min interval allows instead of waiting the full interval. */
|
||||
#define LOW_PEER_THRESHOLD 10
|
||||
|
||||
static double tracker_delay_seconds(int32_t interval) {
|
||||
if (interval <= 0) return TRACKER_DEFAULT_INTERVAL;
|
||||
if (interval < (int32_t)TRACKER_MIN_INTERVAL)
|
||||
|
|
@ -65,6 +69,20 @@ static double tracker_delay_seconds(int32_t interval) {
|
|||
return (double)interval;
|
||||
}
|
||||
|
||||
/* Seconds to wait before the next tracker announce. Normally the tracker's full
|
||||
* advertised interval, but when the swarm is starved (< LOW_PEER_THRESHOLD
|
||||
* connected peers) we re-announce sooner — down to the tracker's min_interval,
|
||||
* never below our 60s floor — so a thin swarm can actually recover. */
|
||||
static double next_announce_delay(int32_t interval, int32_t min_interval,
|
||||
uint32_t peers_connected) {
|
||||
double full = interval > 0 ? tracker_delay_seconds(interval)
|
||||
: TRACKER_FAILURE_RETRY_INTERVAL;
|
||||
if (peers_connected >= LOW_PEER_THRESHOLD) return full;
|
||||
double floor_s = min_interval > 0 ? (double)min_interval : TRACKER_MIN_INTERVAL;
|
||||
if (floor_s < TRACKER_MIN_INTERVAL) floor_s = TRACKER_MIN_INTERVAL;
|
||||
return floor_s < full ? floor_s : full;
|
||||
}
|
||||
|
||||
static void random_bytes(uint8_t *output, size_t length) {
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
size_t offset = 0;
|
||||
|
|
@ -385,6 +403,7 @@ static bool discover_trackers(const uint8_t info_hash[20], uint64_t total_length
|
|||
naut_tracker_event event,
|
||||
endpoint_t **eps, size_t *neps, size_t *cap,
|
||||
int32_t *announce_interval,
|
||||
int32_t *announce_min_interval,
|
||||
naut_swarm_tracker_stats *tracker_stats,
|
||||
uint32_t tracker_count) {
|
||||
naut_announce_req req;
|
||||
|
|
@ -399,6 +418,7 @@ static bool discover_trackers(const uint8_t info_hash[20], uint64_t total_length
|
|||
req.numwant = 100;
|
||||
memcpy(&req.key, peerid + 8, sizeof req.key);
|
||||
if (announce_interval) *announce_interval = 0;
|
||||
if (announce_min_interval) *announce_min_interval = 0;
|
||||
|
||||
size_t tier_start = 0;
|
||||
while (tier_start < num_trackers) {
|
||||
|
|
@ -451,6 +471,8 @@ static bool discover_trackers(const uint8_t info_hash[20], uint64_t total_length
|
|||
tier_succeeded = true;
|
||||
if (announce_interval && response.interval > 0)
|
||||
*announce_interval = response.interval;
|
||||
if (announce_min_interval && response.min_interval > 0)
|
||||
*announce_min_interval = response.min_interval;
|
||||
if (tracker_stat) {
|
||||
tracker_set_status(tracker_stat, "working", "");
|
||||
tracker_stat->seeds = response.seeders;
|
||||
|
|
@ -574,7 +596,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
endpoint_t *endpoints = NULL;
|
||||
size_t neps = 0, epcap = 0;
|
||||
uint32_t target_peers = target_peer_count();
|
||||
int32_t tracker_interval = 0;
|
||||
int32_t tracker_interval = 0, tracker_min_interval = 0;
|
||||
naut_swarm_tracker_stats tracker_stats[NAUT_SWARM_MAX_TRACKER_STATS];
|
||||
uint32_t tracker_count = 0;
|
||||
if (config->num_peers > 0) {
|
||||
|
|
@ -608,8 +630,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
if (!discover_trackers(hash, 0, trackers, num_trackers,
|
||||
tracker_tiers, peerid, 0, 0,
|
||||
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
||||
&tracker_interval, tracker_stats,
|
||||
tracker_count) ||
|
||||
&tracker_interval, &tracker_min_interval,
|
||||
tracker_stats, tracker_count) ||
|
||||
(neps < target_peers &&
|
||||
!discover_dht(hash, &endpoints, &neps, &epcap))) {
|
||||
NAUT_ERROR("out of memory collecting discovered peers");
|
||||
|
|
@ -731,8 +753,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
mi.tracker_tiers, peerid,
|
||||
resumed_bytes, resumed_left,
|
||||
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
||||
&tracker_interval, tracker_stats,
|
||||
tracker_count) ||
|
||||
&tracker_interval, &tracker_min_interval,
|
||||
tracker_stats, tracker_count) ||
|
||||
(neps < target_peers &&
|
||||
!discover_dht(mi.infohash_v1, &endpoints, &neps, &epcap))) {
|
||||
NAUT_ERROR("out of memory collecting discovered peers");
|
||||
|
|
@ -787,10 +809,11 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
feed_engine(eng, torrent_id, endpoints, neps, &fed);
|
||||
|
||||
double t0 = now();
|
||||
/* Use the discovered endpoint count as the initial peer proxy: if we start
|
||||
* thin, schedule a quick re-announce instead of waiting the full interval. */
|
||||
double next_tracker_announce =
|
||||
t0 + (tracker_interval > 0
|
||||
? tracker_delay_seconds(tracker_interval)
|
||||
: TRACKER_FAILURE_RETRY_INTERVAL);
|
||||
t0 + next_announce_delay(tracker_interval, tracker_min_interval,
|
||||
(uint32_t)neps);
|
||||
double next_dht_lookup = t0 + DHT_REFRESH_INTERVAL;
|
||||
naut_err run_error = NAUT_OK;
|
||||
bool cancelled = false;
|
||||
|
|
@ -827,21 +850,20 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
uint64_t downloaded = naut_download_bytes_done(d);
|
||||
uint64_t left = (uint64_t)mi.total_length > downloaded
|
||||
? (uint64_t)mi.total_length - downloaded : 0;
|
||||
int32_t interval = 0;
|
||||
int32_t interval = 0, min_interval = 0;
|
||||
if (!discover_trackers(mi.infohash_v1,
|
||||
(uint64_t)mi.total_length,
|
||||
mi.trackers, mi.num_trackers,
|
||||
mi.tracker_tiers, peerid,
|
||||
downloaded, left, NAUT_TEV_NONE,
|
||||
&endpoints, &neps, &epcap,
|
||||
&interval, tracker_stats,
|
||||
tracker_count)) {
|
||||
&interval, &min_interval,
|
||||
tracker_stats, tracker_count)) {
|
||||
run_error = NAUT_ERR_NOMEM;
|
||||
break;
|
||||
}
|
||||
next_tracker_announce = t + (interval > 0
|
||||
? tracker_delay_seconds(interval)
|
||||
: TRACKER_FAILURE_RETRY_INTERVAL);
|
||||
next_tracker_announce = t + next_announce_delay(
|
||||
interval, min_interval, ts.peers_connected);
|
||||
feed_engine(eng, torrent_id, endpoints, neps, &fed);
|
||||
}
|
||||
if (t >= next_dht_lookup) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
int32_t interval;
|
||||
int32_t min_interval; /* tracker's floor, 0 if not advertised */
|
||||
int32_t seeders, leechers; /* -1 if absent */
|
||||
naut_peer_addr *peers;
|
||||
size_t num_peers;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ static naut_err collect_peers(const tracker_peer *peers, size_t count,
|
|||
const tracker_announce_response *resp,
|
||||
naut_tracker_response *out) {
|
||||
out->interval = (int32_t)resp->interval;
|
||||
out->min_interval = (int32_t)resp->min_interval;
|
||||
out->seeders = (int32_t)resp->complete;
|
||||
out->leechers = (int32_t)resp->incomplete;
|
||||
out->peers = NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue