merged DHT optimization from libtorrent_aio
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
* improve DHT lookup times
|
||||||
* uTP path MTU discovery improvements
|
* uTP path MTU discovery improvements
|
||||||
* optimized the torrent creator optimizer to scale significantly better with more files
|
* optimized the torrent creator optimizer to scale significantly better with more files
|
||||||
* fix uTP edge case where udp socket buffer fills up
|
* fix uTP edge case where udp socket buffer fills up
|
||||||
|
@@ -42,10 +42,11 @@ namespace libtorrent { namespace dht
|
|||||||
|
|
||||||
struct node_entry
|
struct node_entry
|
||||||
{
|
{
|
||||||
node_entry(node_id const& id_, udp::endpoint ep, bool pinged = false)
|
node_entry(node_id const& id_, udp::endpoint ep, int roundtriptime = 0xffff, bool pinged = false)
|
||||||
: addr(ep.address())
|
: addr(ep.address())
|
||||||
, port(ep.port())
|
, port(ep.port())
|
||||||
, timeout_count(pinged ? 0 : 0xffff)
|
, timeout_count(pinged ? 0 : 0xffff)
|
||||||
|
, rtt(roundtriptime)
|
||||||
, id(id_)
|
, id(id_)
|
||||||
{
|
{
|
||||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||||
@@ -57,6 +58,7 @@ struct node_entry
|
|||||||
: addr(ep.address())
|
: addr(ep.address())
|
||||||
, port(ep.port())
|
, port(ep.port())
|
||||||
, timeout_count(0xffff)
|
, timeout_count(0xffff)
|
||||||
|
, rtt(0xffff)
|
||||||
, id(0)
|
, id(0)
|
||||||
{
|
{
|
||||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||||
@@ -66,6 +68,7 @@ struct node_entry
|
|||||||
|
|
||||||
node_entry()
|
node_entry()
|
||||||
: timeout_count(0xffff)
|
: timeout_count(0xffff)
|
||||||
|
, rtt(0xffff)
|
||||||
, id(0)
|
, id(0)
|
||||||
{
|
{
|
||||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||||
@@ -80,6 +83,11 @@ struct node_entry
|
|||||||
void reset_fail_count() { if (pinged()) timeout_count = 0; }
|
void reset_fail_count() { if (pinged()) timeout_count = 0; }
|
||||||
udp::endpoint ep() const { return udp::endpoint(addr, port); }
|
udp::endpoint ep() const { return udp::endpoint(addr, port); }
|
||||||
bool confirmed() const { return timeout_count == 0; }
|
bool confirmed() const { return timeout_count == 0; }
|
||||||
|
void update_rtt(int new_rtt)
|
||||||
|
{
|
||||||
|
if (rtt == 0xffff) rtt = new_rtt;
|
||||||
|
else rtt = int(rtt) / 3 + int(new_rtt) * 2 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: replace with a union of address_v4 and address_v6
|
// TODO: replace with a union of address_v4 and address_v6
|
||||||
address addr;
|
address addr;
|
||||||
@@ -87,6 +95,8 @@ struct node_entry
|
|||||||
// the number of times this node has failed to
|
// the number of times this node has failed to
|
||||||
// respond in a row
|
// respond in a row
|
||||||
boost::uint16_t timeout_count;
|
boost::uint16_t timeout_count;
|
||||||
|
// the average RTT of this node
|
||||||
|
boost::uint16_t rtt;
|
||||||
node_id id;
|
node_id id;
|
||||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||||
ptime first_seen;
|
ptime first_seen;
|
||||||
|
@@ -102,13 +102,13 @@ public:
|
|||||||
router_iterator router_begin() const { return m_router_nodes.begin(); }
|
router_iterator router_begin() const { return m_router_nodes.begin(); }
|
||||||
router_iterator router_end() const { return m_router_nodes.end(); }
|
router_iterator router_end() const { return m_router_nodes.end(); }
|
||||||
|
|
||||||
bool add_node(node_entry const& e);
|
bool add_node(node_entry e);
|
||||||
|
|
||||||
// this function is called every time the node sees
|
// this function is called every time the node sees
|
||||||
// a sign of a node being alive. This node will either
|
// a sign of a node being alive. This node will either
|
||||||
// be inserted in the k-buckets or be moved to the top
|
// be inserted in the k-buckets or be moved to the top
|
||||||
// of its bucket.
|
// of its bucket.
|
||||||
bool node_seen(node_id const& id, udp::endpoint ep);
|
bool node_seen(node_id const& id, udp::endpoint ep, int rtt);
|
||||||
|
|
||||||
// this may add a node to the routing table and mark it as
|
// this may add a node to the routing table and mark it as
|
||||||
// not pinged. If the bucket the node falls into is full,
|
// not pinged. If the bucket the node falls into is full,
|
||||||
|
@@ -232,9 +232,9 @@ namespace libtorrent { namespace dht
|
|||||||
|
|
||||||
// turns on and off individual components' logging
|
// turns on and off individual components' logging
|
||||||
|
|
||||||
// rpc_log().enable(false);
|
rpc_log().enable(false);
|
||||||
// node_log().enable(false);
|
node_log().enable(false);
|
||||||
// traversal_log().enable(false);
|
traversal_log().enable(false);
|
||||||
// dht_tracker_log.enable(false);
|
// dht_tracker_log.enable(false);
|
||||||
|
|
||||||
TORRENT_LOG(dht_tracker) << "starting DHT tracker with node id: " << m_dht.nid();
|
TORRENT_LOG(dht_tracker) << "starting DHT tracker with node id: " << m_dht.nid();
|
||||||
|
@@ -742,7 +742,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
|||||||
// the token was correct. That means this
|
// the token was correct. That means this
|
||||||
// node is not spoofing its address. So, let
|
// node is not spoofing its address. So, let
|
||||||
// the table get a chance to add it.
|
// the table get a chance to add it.
|
||||||
m_table.node_seen(id, m.addr);
|
m_table.node_seen(id, m.addr, 0xffff);
|
||||||
|
|
||||||
if (!m_map.empty() && int(m_map.size()) >= m_settings.max_torrents)
|
if (!m_map.empty() && int(m_map.size()) >= m_settings.max_torrents)
|
||||||
{
|
{
|
||||||
@@ -950,7 +950,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
|||||||
f = &i->second;
|
f = &i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_table.node_seen(id, m.addr);
|
m_table.node_seen(id, m.addr, 0xffff);
|
||||||
|
|
||||||
f->last_seen = time_now();
|
f->last_seen = time_now();
|
||||||
|
|
||||||
|
@@ -170,6 +170,7 @@ void routing_table::print_state(std::ostream& os) const
|
|||||||
, end(i->live_nodes.end()); j != end; ++j)
|
, end(i->live_nodes.end()); j != end; ++j)
|
||||||
{
|
{
|
||||||
os << " id: " << j->id
|
os << " id: " << j->id
|
||||||
|
<< " rtt: " << j->rtt
|
||||||
<< " ip: " << j->ep()
|
<< " ip: " << j->ep()
|
||||||
<< " fails: " << j->fail_count()
|
<< " fails: " << j->fail_count()
|
||||||
<< " pinged: " << j->pinged()
|
<< " pinged: " << j->pinged()
|
||||||
@@ -316,7 +317,7 @@ node_entry* routing_table::find_node(udp::endpoint const& ep, routing_table::tab
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool routing_table::add_node(node_entry const& e)
|
bool routing_table::add_node(node_entry e)
|
||||||
{
|
{
|
||||||
if (m_router_nodes.find(e.ep()) != m_router_nodes.end()) return false;
|
if (m_router_nodes.find(e.ep()) != m_router_nodes.end()) return false;
|
||||||
|
|
||||||
@@ -359,6 +360,7 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
if (existing->id == e.id)
|
if (existing->id == e.id)
|
||||||
{
|
{
|
||||||
existing->timeout_count = 0;
|
existing->timeout_count = 0;
|
||||||
|
existing->update_rtt(e.rtt);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,17 +417,30 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
if (j->addr != e.addr || j->port != e.port) return ret;
|
if (j->addr != e.addr || j->port != e.port) return ret;
|
||||||
|
|
||||||
// we already have the node in our bucket
|
// we already have the node in our bucket
|
||||||
// just move it to the back since it was
|
|
||||||
// the last node we had any contact with
|
|
||||||
// in this bucket
|
|
||||||
TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep());
|
TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep());
|
||||||
j->timeout_count = 0;
|
j->timeout_count = 0;
|
||||||
|
j->update_rtt(e.rtt);
|
||||||
// TORRENT_LOG(table) << "updating node: " << i->id << " " << i->addr;
|
// TORRENT_LOG(table) << "updating node: " << i->id << " " << i->addr;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::id, _1) == e.id)
|
// if this node exists in the replacement bucket. update it and
|
||||||
!= rb.end()) return ret;
|
// pull it out from there. We may add it back to the replacement
|
||||||
|
// bucket, but we may also replace a node in the main bucket, now
|
||||||
|
// that we have an updated RTT
|
||||||
|
j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::id, _1) == e.id);
|
||||||
|
if (j != rb.end())
|
||||||
|
{
|
||||||
|
// a new IP address just claimed this node-ID
|
||||||
|
// ignore it
|
||||||
|
if (j->addr != e.addr || j->port != e.port) return ret;
|
||||||
|
TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep());
|
||||||
|
j->timeout_count = 0;
|
||||||
|
j->update_rtt(e.rtt);
|
||||||
|
e = *j;
|
||||||
|
m_ips.erase(j->addr.to_v4().to_bytes());
|
||||||
|
rb.erase(j);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_settings.restrict_routing_ips)
|
if (m_settings.restrict_routing_ips)
|
||||||
{
|
{
|
||||||
@@ -447,7 +462,7 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
j = std::find_if(rb.begin(), rb.end(), boost::bind(&compare_ip_cidr, _1, e));
|
j = std::find_if(rb.begin(), rb.end(), boost::bind(&compare_ip_cidr, _1, e));
|
||||||
if (j != rb.end())
|
if (j != rb.end())
|
||||||
{
|
{
|
||||||
// same thing bug for the replacement bucket
|
// same thing but for the replacement bucket
|
||||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||||
TORRENT_LOG(table) << "ignoring (replacement) node: " << e.id << " " << e.addr
|
TORRENT_LOG(table) << "ignoring (replacement) node: " << e.id << " " << e.addr
|
||||||
<< " existing node: "
|
<< " existing node: "
|
||||||
@@ -457,10 +472,7 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the node was not present in our list
|
// if there's room in the main bucket, just insert it
|
||||||
// we will only insert it if there is room
|
|
||||||
// for it, or if some of our nodes have gone
|
|
||||||
// offline
|
|
||||||
if (int(b.size()) < m_bucket_size)
|
if (int(b.size()) < m_bucket_size)
|
||||||
{
|
{
|
||||||
if (b.empty()) b.reserve(m_bucket_size);
|
if (b.empty()) b.reserve(m_bucket_size);
|
||||||
@@ -474,6 +486,8 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
// i.e. we haven't confirmed that they respond to messages.
|
// i.e. we haven't confirmed that they respond to messages.
|
||||||
// Then we look for nodes marked as stale
|
// Then we look for nodes marked as stale
|
||||||
// in the k-bucket. If we find one, we can replace it.
|
// in the k-bucket. If we find one, we can replace it.
|
||||||
|
// as the last replacement strategy, we look for nodes with the
|
||||||
|
// highest RTT, and if it's higher than the new node, we replace it
|
||||||
|
|
||||||
// can we split the bucket?
|
// can we split the bucket?
|
||||||
bool can_split = false;
|
bool can_split = false;
|
||||||
@@ -483,7 +497,7 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
// only nodes that are pinged and haven't failed
|
// only nodes that are pinged and haven't failed
|
||||||
// can split the bucket, and we can only split
|
// can split the bucket, and we can only split
|
||||||
// the last bucket
|
// the last bucket
|
||||||
can_split = (boost::next(i) == m_buckets.end() && m_buckets.size() < 160);
|
can_split = (boost::next(i) == m_buckets.end() && m_buckets.size() < 159);
|
||||||
|
|
||||||
// if the node we're trying to insert is considered pinged,
|
// if the node we're trying to insert is considered pinged,
|
||||||
// we may replace other nodes that aren't pinged
|
// we may replace other nodes that aren't pinged
|
||||||
@@ -495,8 +509,7 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
// j points to a node that has not been pinged.
|
// j points to a node that has not been pinged.
|
||||||
// Replace it with this new one
|
// Replace it with this new one
|
||||||
m_ips.erase(j->addr.to_v4().to_bytes());
|
m_ips.erase(j->addr.to_v4().to_bytes());
|
||||||
b.erase(j);
|
*j = e;
|
||||||
b.push_back(e);
|
|
||||||
m_ips.insert(e.addr.to_v4().to_bytes());
|
m_ips.insert(e.addr.to_v4().to_bytes());
|
||||||
// TORRENT_LOG(table) << "replacing unpinged node: " << e.id << " " << e.addr;
|
// TORRENT_LOG(table) << "replacing unpinged node: " << e.id << " " << e.addr;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -517,12 +530,26 @@ bool routing_table::add_node(node_entry const& e)
|
|||||||
// i points to a node that has been marked
|
// i points to a node that has been marked
|
||||||
// as stale. Replace it with this new one
|
// as stale. Replace it with this new one
|
||||||
m_ips.erase(j->addr.to_v4().to_bytes());
|
m_ips.erase(j->addr.to_v4().to_bytes());
|
||||||
b.erase(j);
|
*j = e;
|
||||||
b.push_back(e);
|
|
||||||
m_ips.insert(e.addr.to_v4().to_bytes());
|
m_ips.insert(e.addr.to_v4().to_bytes());
|
||||||
// TORRENT_LOG(table) << "replacing stale node: " << e.id << " " << e.addr;
|
// TORRENT_LOG(table) << "replacing stale node: " << e.id << " " << e.addr;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in order to keep lookup times small, prefer nodes with low RTTs
|
||||||
|
|
||||||
|
j = std::max_element(b.begin(), b.end()
|
||||||
|
, boost::bind(&node_entry::rtt, _1)
|
||||||
|
< boost::bind(&node_entry::rtt, _2));
|
||||||
|
|
||||||
|
if (j != b.end() && j->rtt > e.rtt)
|
||||||
|
{
|
||||||
|
m_ips.erase(j->addr.to_v4().to_bytes());
|
||||||
|
*j = e;
|
||||||
|
m_ips.insert(e.addr.to_v4().to_bytes());
|
||||||
|
// TORRENT_LOG(table) << "replacing node with higher RTT: " << e.id << " " << e.addr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we can't split, try to insert into the replacement bucket
|
// if we can't split, try to insert into the replacement bucket
|
||||||
@@ -729,7 +756,7 @@ void routing_table::add_router_node(udp::endpoint router)
|
|||||||
// was spoofed or not (i.e. pinged == false)
|
// was spoofed or not (i.e. pinged == false)
|
||||||
void routing_table::heard_about(node_id const& id, udp::endpoint const& ep)
|
void routing_table::heard_about(node_id const& id, udp::endpoint const& ep)
|
||||||
{
|
{
|
||||||
add_node(node_entry(id, ep, false));
|
add_node(node_entry(id, ep));
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function is called every time the node sees
|
// this function is called every time the node sees
|
||||||
@@ -739,9 +766,9 @@ void routing_table::heard_about(node_id const& id, udp::endpoint const& ep)
|
|||||||
// the return value indicates if the table needs a refresh.
|
// the return value indicates if the table needs a refresh.
|
||||||
// if true, the node should refresh the table (i.e. do a find_node
|
// if true, the node should refresh the table (i.e. do a find_node
|
||||||
// on its own id)
|
// on its own id)
|
||||||
bool routing_table::node_seen(node_id const& id, udp::endpoint ep)
|
bool routing_table::node_seen(node_id const& id, udp::endpoint ep, int rtt)
|
||||||
{
|
{
|
||||||
return add_node(node_entry(id, ep, true));
|
return add_node(node_entry(id, ep, rtt, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool routing_table::need_bootstrap() const
|
bool routing_table::need_bootstrap() const
|
||||||
|
@@ -313,9 +313,11 @@ bool rpc_manager::incoming(msg const& m, node_id* id)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ptime now = time_now_hires();
|
||||||
|
|
||||||
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
#ifdef TORRENT_DHT_VERBOSE_LOGGING
|
||||||
std::ofstream reply_stats("round_trip_ms.log", std::ios::app);
|
std::ofstream reply_stats("round_trip_ms.log", std::ios::app);
|
||||||
reply_stats << m.addr << "\t" << total_milliseconds(time_now_hires() - o->sent())
|
reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent())
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -366,9 +368,11 @@ bool rpc_manager::incoming(msg const& m, node_id* id)
|
|||||||
o->reply(m);
|
o->reply(m);
|
||||||
*id = node_id(node_id_ent->string_ptr());
|
*id = node_id(node_id_ent->string_ptr());
|
||||||
|
|
||||||
|
int rtt = total_milliseconds(now - o->sent());
|
||||||
|
|
||||||
// we found an observer for this reply, hence the node is not spoofing
|
// we found an observer for this reply, hence the node is not spoofing
|
||||||
// add it to the routing table
|
// add it to the routing table
|
||||||
return m_table.node_seen(*id, m.addr);
|
return m_table.node_seen(*id, m.addr, rtt);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_duration rpc_manager::tick()
|
time_duration rpc_manager::tick()
|
||||||
|
Reference in New Issue
Block a user