mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Re-implement progressive backoff interval for friend finding
We now exponentially reduce the frequency of friend lookups based on how many lookups we've already made for an offline friend
This commit is contained in:
parent
af5dd43c98
commit
0dca481e1d
|
@ -73,7 +73,10 @@ typedef struct Onion_Friend {
|
||||||
|
|
||||||
uint64_t last_seen;
|
uint64_t last_seen;
|
||||||
uint64_t last_populated; // the last time we had a fully populated client nodes list
|
uint64_t last_populated; // the last time we had a fully populated client nodes list
|
||||||
|
uint64_t time_last_pinged; // the last time we pinged this friend with any node
|
||||||
|
|
||||||
uint32_t run_count;
|
uint32_t run_count;
|
||||||
|
uint32_t pings; // how many sucessful pings we've made for this friend
|
||||||
|
|
||||||
Last_Pinged last_pinged[MAX_STORED_PINGED_NODES];
|
Last_Pinged last_pinged[MAX_STORED_PINGED_NODES];
|
||||||
uint8_t last_pinged_index;
|
uint8_t last_pinged_index;
|
||||||
|
@ -751,9 +754,9 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
|
||||||
memcpy(node_list[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE);
|
memcpy(node_list[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
list_nodes[index].is_stored = is_stored;
|
node_list[index].is_stored = is_stored;
|
||||||
list_nodes[index].timestamp = mono_time_get(onion_c->mono_time);
|
node_list[index].timestamp = mono_time_get(onion_c->mono_time);
|
||||||
list_nodes[index].pings_since_last_response = 0;
|
node_list[index].pings_since_last_response = 0;
|
||||||
|
|
||||||
if (!stored) {
|
if (!stored) {
|
||||||
node_list[index].last_pinged = 0;
|
node_list[index].last_pinged = 0;
|
||||||
|
@ -1520,39 +1523,42 @@ int onion_set_friend_online(Onion_Client *onion_c, int friend_num, bool is_onlin
|
||||||
non_null()
|
non_null()
|
||||||
static void populate_path_nodes(Onion_Client *onion_c)
|
static void populate_path_nodes(Onion_Client *onion_c)
|
||||||
{
|
{
|
||||||
Node_format nodes_list[MAX_FRIEND_CLIENTS];
|
Node_format node_list[MAX_FRIEND_CLIENTS];
|
||||||
|
|
||||||
const unsigned int num_nodes = randfriends_nodes(onion_c->dht, nodes_list, MAX_FRIEND_CLIENTS);
|
const unsigned int num_nodes = randfriends_nodes(onion_c->dht, node_list, MAX_FRIEND_CLIENTS);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < num_nodes; ++i) {
|
for (unsigned int i = 0; i < num_nodes; ++i) {
|
||||||
onion_add_path_node(onion_c, &nodes_list[i].ip_port, nodes_list[i].public_key);
|
onion_add_path_node(onion_c, &node_list[i].ip_port, node_list[i].public_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static void populate_path_nodes_tcp(Onion_Client *onion_c)
|
static void populate_path_nodes_tcp(Onion_Client *onion_c)
|
||||||
{
|
{
|
||||||
Node_format nodes_list[MAX_SENT_NODES];
|
Node_format node_list[MAX_SENT_NODES];
|
||||||
|
|
||||||
const unsigned int num_nodes = copy_connected_tcp_relays(onion_c->c, nodes_list, MAX_SENT_NODES);
|
const unsigned int num_nodes = copy_connected_tcp_relays(onion_c->c, node_list, MAX_SENT_NODES);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < num_nodes; ++i) {
|
for (unsigned int i = 0; i < num_nodes; ++i) {
|
||||||
onion_add_bs_path_node(onion_c, &nodes_list[i].ip_port, nodes_list[i].public_key);
|
onion_add_bs_path_node(onion_c, &node_list[i].ip_port, node_list[i].public_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* How often we send announce requests to a friend per node when they're new to the list */
|
/* How often we ping new friends per node */
|
||||||
#define ANNOUNCE_FRIEND_NEW_INTERVAL 10
|
#define ANNOUNCE_FRIEND_NEW_INTERVAL 3
|
||||||
|
|
||||||
/* How often we send announce requests to a friend per node when they've been in our list for a while */
|
/* How long we consider a friend new based on the value of their run_count */
|
||||||
#define ANNOUNCE_FRIEND_OLD_INTERVAL (60 * 3)
|
#define ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING 5
|
||||||
|
|
||||||
/* The number of successful runs of do_friends() before we consider a friend old */
|
|
||||||
#define ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING 10
|
|
||||||
|
|
||||||
/* How often we try to re-populate the nodes lists if we don't meet a minimum threshhold of nodes */
|
/* How often we try to re-populate the nodes lists if we don't meet a minimum threshhold of nodes */
|
||||||
#define ANNOUNCE_POPULATE_TIMEOUT (60 * 10)
|
#define ANNOUNCE_POPULATE_TIMEOUT (60 * 10)
|
||||||
|
|
||||||
|
/* The max time between lookup requests for a friend per node */
|
||||||
|
#define ANNOUNCE_FRIEND_MAX_INTERVAL (60 * 60)
|
||||||
|
|
||||||
|
/* Max exponent when calculating the announce request interval */
|
||||||
|
#define MAX_RUN_COUNT_EXPONENT 12
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
|
static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
|
||||||
{
|
{
|
||||||
|
@ -1566,22 +1572,30 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool friend_is_new = o_friend->run_count <= ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING;
|
uint32_t interval;
|
||||||
const uint64_t tm = mono_time_get(onion_c->mono_time);
|
const uint64_t tm = mono_time_get(onion_c->mono_time);
|
||||||
uint64_t timeout = ANNOUNCE_FRIEND_OLD_INTERVAL;
|
const bool friend_is_new = o_friend->run_count <= ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING;
|
||||||
|
|
||||||
if (!friend_is_new) {
|
if (!friend_is_new) {
|
||||||
if (o_friend->last_seen == 0) {
|
if (o_friend->last_seen == 0) {
|
||||||
o_friend->last_seen = tm;
|
o_friend->last_seen = tm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// how often we ping a node for a friend depends on how many times we've already tried.
|
||||||
|
// the interval increases exponentially, as the longer a friend has been offline, the less
|
||||||
|
// likely the case is that they're online and failed to find us
|
||||||
|
const uint32_t c = 1 << min_u32(MAX_RUN_COUNT_EXPONENT, o_friend->run_count - 2);
|
||||||
|
interval = min_u32(c, ANNOUNCE_FRIEND_MAX_INTERVAL);
|
||||||
} else {
|
} else {
|
||||||
timeout = ANNOUNCE_FRIEND_NEW_INTERVAL;
|
interval = ANNOUNCE_FRIEND_NEW_INTERVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o_friend->is_online) {
|
if (o_friend->is_online) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(interval >= ANNOUNCE_FRIEND_NEW_INTERVAL); // an int overflow would be devastating
|
||||||
|
|
||||||
/* send packets to friend telling them our DHT public key. */
|
/* send packets to friend telling them our DHT public key. */
|
||||||
if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent,
|
if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent,
|
||||||
ONION_DHTPK_SEND_INTERVAL)) {
|
ONION_DHTPK_SEND_INTERVAL)) {
|
||||||
|
@ -1598,42 +1612,48 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t count = 0; // number of alive path nodes
|
uint16_t count = 0; // number of alive path nodes
|
||||||
uint16_t pings = 0; // number of pings we successfully send
|
|
||||||
|
|
||||||
Onion_Node *list_nodes = o_friend->clients_list;
|
Onion_Node *node_list = o_friend->clients_list;
|
||||||
|
|
||||||
for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) {
|
for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) {
|
||||||
if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) {
|
if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
if (list_nodes[i].last_pinged == 0) {
|
// we don't want new nodes to be pinged immediately
|
||||||
list_nodes[i].last_pinged = tm;
|
if (node_list[i].last_pinged == 0) {
|
||||||
|
node_list[i].last_pinged = tm;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// node hasn't responded in a while so we skip it
|
// node hasn't responded in a while so we skip it
|
||||||
if (list_nodes[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) {
|
if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, timeout)) {
|
// space requests out between nodes
|
||||||
|
if (!mono_time_is_timeout(onion_c->mono_time, o_friend->time_last_pinged, interval / (MAX_ONION_CLIENTS / 2))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_send_announce_request(onion_c, friendnum + 1, &list_nodes[i].ip_port,
|
if (!mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval)) {
|
||||||
list_nodes[i].public_key, nullptr, -1) == 0) {
|
continue;
|
||||||
list_nodes[i].last_pinged = tm;
|
|
||||||
++list_nodes[i].pings_since_last_response;
|
|
||||||
++pings;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pings > 0) {
|
if (client_send_announce_request(onion_c, friendnum + 1, &node_list[i].ip_port,
|
||||||
|
node_list[i].public_key, nullptr, -1) == 0) {
|
||||||
|
node_list[i].last_pinged = tm;
|
||||||
|
o_friend->time_last_pinged = tm;
|
||||||
|
++node_list[i].pings_since_last_response;
|
||||||
|
++o_friend->pings;
|
||||||
|
|
||||||
|
if (o_friend->pings % (MAX_ONION_CLIENTS / 2) == 0) {
|
||||||
++o_friend->run_count;
|
++o_friend->run_count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (count == MAX_ONION_CLIENTS) {
|
if (count == MAX_ONION_CLIENTS) {
|
||||||
if (!friend_is_new) {
|
if (!friend_is_new) {
|
||||||
|
@ -1644,7 +1664,7 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if path nodes list for this friend needs to be repopulated
|
// check if path nodes list for this friend needs to be repopulated
|
||||||
if (count < MAX_ONION_CLIENTS / 2
|
if (count <= MAX_ONION_CLIENTS / 2
|
||||||
|| mono_time_is_timeout(onion_c->mono_time, o_friend->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) {
|
|| mono_time_is_timeout(onion_c->mono_time, o_friend->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) {
|
||||||
const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES);
|
const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES);
|
||||||
const uint16_t n = min_u16(num_nodes, MAX_PATH_NODES / 4);
|
const uint16_t n = min_u16(num_nodes, MAX_PATH_NODES / 4);
|
||||||
|
@ -1681,40 +1701,41 @@ non_null()
|
||||||
static void do_announce(Onion_Client *onion_c)
|
static void do_announce(Onion_Client *onion_c)
|
||||||
{
|
{
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
Onion_Node *list_nodes = onion_c->clients_announce_list;
|
Onion_Node *node_list = onion_c->clients_announce_list;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
|
for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
|
||||||
if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) {
|
if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
/* Don't announce ourselves the first time this is run to new peers */
|
/* Don't announce ourselves the first time this is run to new peers */
|
||||||
if (list_nodes[i].last_pinged == 0) {
|
if (node_list[i].last_pinged == 0) {
|
||||||
list_nodes[i].last_pinged = 1;
|
node_list[i].last_pinged = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list_nodes[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) {
|
if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED;
|
unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED;
|
||||||
|
|
||||||
if (list_nodes[i].is_stored != 0 && path_exists(onion_c->mono_time, &onion_c->onion_paths_self, list_nodes[i].path_used)) {
|
if (node_list[i].is_stored != 0
|
||||||
|
&& path_exists(onion_c->mono_time, &onion_c->onion_paths_self, node_list[i].path_used)) {
|
||||||
interval = ANNOUNCE_INTERVAL_ANNOUNCED;
|
interval = ANNOUNCE_INTERVAL_ANNOUNCED;
|
||||||
|
|
||||||
const uint32_t pathnum = list_nodes[i].path_used % NUMBER_ONION_PATHS;
|
const uint32_t pathnum = node_list[i].path_used % NUMBER_ONION_PATHS;
|
||||||
|
|
||||||
/* A node/path is considered "stable", and can be pinged less
|
/* A node/path is considered "stable", and can be pinged less
|
||||||
* aggressively, if it has survived for at least TIME_TO_STABLE
|
* aggressively, if it has survived for at least TIME_TO_STABLE
|
||||||
* and the latest packets sent to it are not timing out.
|
* and the latest packets sent to it are not timing out.
|
||||||
*/
|
*/
|
||||||
if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].added_time, TIME_TO_STABLE)
|
if (mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE)
|
||||||
&& !(list_nodes[i].pings_since_last_response > 0
|
&& !(node_list[i].pings_since_last_response > 0
|
||||||
&& mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, ONION_NODE_TIMEOUT))
|
&& mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, ONION_NODE_TIMEOUT))
|
||||||
&& mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.path_creation_time[pathnum], TIME_TO_STABLE)
|
&& mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.path_creation_time[pathnum], TIME_TO_STABLE)
|
||||||
&& !(onion_c->onion_paths_self.last_path_used_times[pathnum] > 0
|
&& !(onion_c->onion_paths_self.last_path_used_times[pathnum] > 0
|
||||||
&& mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.last_path_used[pathnum], ONION_PATH_TIMEOUT))) {
|
&& mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.last_path_used[pathnum], ONION_PATH_TIMEOUT))) {
|
||||||
|
@ -1722,20 +1743,20 @@ static void do_announce(Onion_Client *onion_c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, interval)
|
if (mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval)
|
||||||
|| mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL)) {
|
|| mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL)) {
|
||||||
uint32_t path_to_use = list_nodes[i].path_used;
|
uint32_t path_to_use = node_list[i].path_used;
|
||||||
|
|
||||||
if (list_nodes[i].pings_since_last_response == ONION_NODE_MAX_PINGS - 1
|
if (node_list[i].pings_since_last_response == ONION_NODE_MAX_PINGS - 1
|
||||||
&& mono_time_is_timeout(onion_c->mono_time, list_nodes[i].added_time, TIME_TO_STABLE)) {
|
&& mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE)) {
|
||||||
/* Last chance for a long-lived node - try a random path */
|
/* Last chance for a long-lived node - try a random path */
|
||||||
path_to_use = -1;
|
path_to_use = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_send_announce_request(onion_c, 0, &list_nodes[i].ip_port, list_nodes[i].public_key,
|
if (client_send_announce_request(onion_c, 0, &node_list[i].ip_port, node_list[i].public_key,
|
||||||
list_nodes[i].ping_id, path_to_use) == 0) {
|
node_list[i].ping_id, path_to_use) == 0) {
|
||||||
list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time);
|
node_list[i].last_pinged = mono_time_get(onion_c->mono_time);
|
||||||
++list_nodes[i].pings_since_last_response;
|
++node_list[i].pings_since_last_response;
|
||||||
onion_c->last_announce = mono_time_get(onion_c->mono_time);
|
onion_c->last_announce = mono_time_get(onion_c->mono_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1747,7 +1768,7 @@ static void do_announce(Onion_Client *onion_c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if list needs to be re-populated
|
// check if list needs to be re-populated
|
||||||
if (count < MAX_ONION_CLIENTS_ANNOUNCE / 2
|
if (count <= MAX_ONION_CLIENTS_ANNOUNCE / 2
|
||||||
|| mono_time_is_timeout(onion_c->mono_time, onion_c->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) {
|
|| mono_time_is_timeout(onion_c->mono_time, onion_c->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) {
|
||||||
uint16_t num_nodes;
|
uint16_t num_nodes;
|
||||||
const Node_format *path_nodes;
|
const Node_format *path_nodes;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user