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);
|
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) {
|
static double tracker_delay_seconds(int32_t interval) {
|
||||||
if (interval <= 0) return TRACKER_DEFAULT_INTERVAL;
|
if (interval <= 0) return TRACKER_DEFAULT_INTERVAL;
|
||||||
if (interval < (int32_t)TRACKER_MIN_INTERVAL)
|
if (interval < (int32_t)TRACKER_MIN_INTERVAL)
|
||||||
|
|
@ -65,6 +69,20 @@ static double tracker_delay_seconds(int32_t interval) {
|
||||||
return (double)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) {
|
static void random_bytes(uint8_t *output, size_t length) {
|
||||||
int fd = open("/dev/urandom", O_RDONLY);
|
int fd = open("/dev/urandom", O_RDONLY);
|
||||||
size_t offset = 0;
|
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,
|
naut_tracker_event event,
|
||||||
endpoint_t **eps, size_t *neps, size_t *cap,
|
endpoint_t **eps, size_t *neps, size_t *cap,
|
||||||
int32_t *announce_interval,
|
int32_t *announce_interval,
|
||||||
|
int32_t *announce_min_interval,
|
||||||
naut_swarm_tracker_stats *tracker_stats,
|
naut_swarm_tracker_stats *tracker_stats,
|
||||||
uint32_t tracker_count) {
|
uint32_t tracker_count) {
|
||||||
naut_announce_req req;
|
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;
|
req.numwant = 100;
|
||||||
memcpy(&req.key, peerid + 8, sizeof req.key);
|
memcpy(&req.key, peerid + 8, sizeof req.key);
|
||||||
if (announce_interval) *announce_interval = 0;
|
if (announce_interval) *announce_interval = 0;
|
||||||
|
if (announce_min_interval) *announce_min_interval = 0;
|
||||||
|
|
||||||
size_t tier_start = 0;
|
size_t tier_start = 0;
|
||||||
while (tier_start < num_trackers) {
|
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;
|
tier_succeeded = true;
|
||||||
if (announce_interval && response.interval > 0)
|
if (announce_interval && response.interval > 0)
|
||||||
*announce_interval = response.interval;
|
*announce_interval = response.interval;
|
||||||
|
if (announce_min_interval && response.min_interval > 0)
|
||||||
|
*announce_min_interval = response.min_interval;
|
||||||
if (tracker_stat) {
|
if (tracker_stat) {
|
||||||
tracker_set_status(tracker_stat, "working", "");
|
tracker_set_status(tracker_stat, "working", "");
|
||||||
tracker_stat->seeds = response.seeders;
|
tracker_stat->seeds = response.seeders;
|
||||||
|
|
@ -574,7 +596,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
||||||
endpoint_t *endpoints = NULL;
|
endpoint_t *endpoints = NULL;
|
||||||
size_t neps = 0, epcap = 0;
|
size_t neps = 0, epcap = 0;
|
||||||
uint32_t target_peers = target_peer_count();
|
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];
|
naut_swarm_tracker_stats tracker_stats[NAUT_SWARM_MAX_TRACKER_STATS];
|
||||||
uint32_t tracker_count = 0;
|
uint32_t tracker_count = 0;
|
||||||
if (config->num_peers > 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,
|
if (!discover_trackers(hash, 0, trackers, num_trackers,
|
||||||
tracker_tiers, peerid, 0, 0,
|
tracker_tiers, peerid, 0, 0,
|
||||||
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
||||||
&tracker_interval, tracker_stats,
|
&tracker_interval, &tracker_min_interval,
|
||||||
tracker_count) ||
|
tracker_stats, tracker_count) ||
|
||||||
(neps < target_peers &&
|
(neps < target_peers &&
|
||||||
!discover_dht(hash, &endpoints, &neps, &epcap))) {
|
!discover_dht(hash, &endpoints, &neps, &epcap))) {
|
||||||
NAUT_ERROR("out of memory collecting discovered peers");
|
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,
|
mi.tracker_tiers, peerid,
|
||||||
resumed_bytes, resumed_left,
|
resumed_bytes, resumed_left,
|
||||||
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
||||||
&tracker_interval, tracker_stats,
|
&tracker_interval, &tracker_min_interval,
|
||||||
tracker_count) ||
|
tracker_stats, tracker_count) ||
|
||||||
(neps < target_peers &&
|
(neps < target_peers &&
|
||||||
!discover_dht(mi.infohash_v1, &endpoints, &neps, &epcap))) {
|
!discover_dht(mi.infohash_v1, &endpoints, &neps, &epcap))) {
|
||||||
NAUT_ERROR("out of memory collecting discovered peers");
|
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);
|
feed_engine(eng, torrent_id, endpoints, neps, &fed);
|
||||||
|
|
||||||
double t0 = now();
|
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 =
|
double next_tracker_announce =
|
||||||
t0 + (tracker_interval > 0
|
t0 + next_announce_delay(tracker_interval, tracker_min_interval,
|
||||||
? tracker_delay_seconds(tracker_interval)
|
(uint32_t)neps);
|
||||||
: TRACKER_FAILURE_RETRY_INTERVAL);
|
|
||||||
double next_dht_lookup = t0 + DHT_REFRESH_INTERVAL;
|
double next_dht_lookup = t0 + DHT_REFRESH_INTERVAL;
|
||||||
naut_err run_error = NAUT_OK;
|
naut_err run_error = NAUT_OK;
|
||||||
bool cancelled = false;
|
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 downloaded = naut_download_bytes_done(d);
|
||||||
uint64_t left = (uint64_t)mi.total_length > downloaded
|
uint64_t left = (uint64_t)mi.total_length > downloaded
|
||||||
? (uint64_t)mi.total_length - downloaded : 0;
|
? (uint64_t)mi.total_length - downloaded : 0;
|
||||||
int32_t interval = 0;
|
int32_t interval = 0, min_interval = 0;
|
||||||
if (!discover_trackers(mi.infohash_v1,
|
if (!discover_trackers(mi.infohash_v1,
|
||||||
(uint64_t)mi.total_length,
|
(uint64_t)mi.total_length,
|
||||||
mi.trackers, mi.num_trackers,
|
mi.trackers, mi.num_trackers,
|
||||||
mi.tracker_tiers, peerid,
|
mi.tracker_tiers, peerid,
|
||||||
downloaded, left, NAUT_TEV_NONE,
|
downloaded, left, NAUT_TEV_NONE,
|
||||||
&endpoints, &neps, &epcap,
|
&endpoints, &neps, &epcap,
|
||||||
&interval, tracker_stats,
|
&interval, &min_interval,
|
||||||
tracker_count)) {
|
tracker_stats, tracker_count)) {
|
||||||
run_error = NAUT_ERR_NOMEM;
|
run_error = NAUT_ERR_NOMEM;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
next_tracker_announce = t + (interval > 0
|
next_tracker_announce = t + next_announce_delay(
|
||||||
? tracker_delay_seconds(interval)
|
interval, min_interval, ts.peers_connected);
|
||||||
: TRACKER_FAILURE_RETRY_INTERVAL);
|
|
||||||
feed_engine(eng, torrent_id, endpoints, neps, &fed);
|
feed_engine(eng, torrent_id, endpoints, neps, &fed);
|
||||||
}
|
}
|
||||||
if (t >= next_dht_lookup) {
|
if (t >= next_dht_lookup) {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t interval;
|
int32_t interval;
|
||||||
|
int32_t min_interval; /* tracker's floor, 0 if not advertised */
|
||||||
int32_t seeders, leechers; /* -1 if absent */
|
int32_t seeders, leechers; /* -1 if absent */
|
||||||
naut_peer_addr *peers;
|
naut_peer_addr *peers;
|
||||||
size_t num_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,
|
const tracker_announce_response *resp,
|
||||||
naut_tracker_response *out) {
|
naut_tracker_response *out) {
|
||||||
out->interval = (int32_t)resp->interval;
|
out->interval = (int32_t)resp->interval;
|
||||||
|
out->min_interval = (int32_t)resp->min_interval;
|
||||||
out->seeders = (int32_t)resp->complete;
|
out->seeders = (int32_t)resp->complete;
|
||||||
out->leechers = (int32_t)resp->incomplete;
|
out->leechers = (int32_t)resp->incomplete;
|
||||||
out->peers = NULL;
|
out->peers = NULL;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue