mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 12:28:42 +00:00
dht: optimisations in search behavior
A candidate flag is added to new search nodes when a search is already synced. The flag is waived when a token is retreived from the node. Candidate nodes behave normally but are not considered to evaluate the search state (synced etc.). The behavior of searches if also changed so GET_NODES updates are now done the same way, no matter if the search is currently synced. This allow candidate nodes to behave normally and makes token update asynchronous, causing less frequent performance hickups.
This commit is contained in:
1 parent
3383ce7a11
commit
f1ff9a1a87
3 files changed
+290
-223
No files matched your search
+23
-6
@@ -68,6 +68,7 @@ struct Node {
|
||||
}
|
||||
bool isExpired(time_point now) const;
|
||||
bool isGood(time_point now) const;
|
||||
bool isMessagePending(time_point now) const;
|
||||
NodeExport exportNode() const { return NodeExport {id, ss, sslen}; }
|
||||
sa_family_t getFamily() const { return ss.ss_family; }
|
||||
|
||||
@@ -400,8 +401,11 @@ private:
|
||||
time_point reply_time {}; /* the time of the last confirmation */
|
||||
RequestStatus() {};
|
||||
RequestStatus(time_point q, time_point a = {}) : request_time(q), reply_time(a) {};
|
||||
bool expired(time_point now) {
|
||||
return (reply_time < request_time && now - request_time <= Node::MAX_RESPONSE_TIME);
|
||||
bool expired(time_point now) const {
|
||||
return reply_time < request_time && now > request_time + Node::MAX_RESPONSE_TIME;
|
||||
}
|
||||
bool pending(time_point now) const {
|
||||
return reply_time < request_time && now - request_time <= Node::MAX_RESPONSE_TIME;
|
||||
}
|
||||
};
|
||||
typedef std::map<Value::Id, RequestStatus> AnnounceStatusMap;
|
||||
@@ -446,8 +450,6 @@ private:
|
||||
if (listenStatus.reply_time > listenStatus.request_time)
|
||||
return listenStatus.reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
|
||||
return listenStatus.request_time + Node::MAX_RESPONSE_TIME;
|
||||
//time_point min_t = listenStatus.request_time + MAX_RESPONSE_TIME;
|
||||
//return listenStatus.reply_time.time_since_epoch().count() ? std::max(listenStatus.reply_time + NODE_EXPIRE_TIME - REANNOUNCE_MARGIN, min_t) : min_t;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> node {};
|
||||
@@ -458,11 +460,20 @@ private:
|
||||
|
||||
Blob token {};
|
||||
|
||||
/**
|
||||
* A search node is candidate if the search is/was synced and this node is a new candidate for inclusion
|
||||
*
|
||||
*/
|
||||
bool candidate {false};
|
||||
|
||||
// Generic temporary flag.
|
||||
// Must be reset to false after use by the algorithm.
|
||||
bool pending {false};
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "get" operation data
|
||||
*/
|
||||
struct Get {
|
||||
time_point start;
|
||||
Value::Filter filter;
|
||||
@@ -470,11 +481,17 @@ private:
|
||||
DoneCallback done_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "put" operation data
|
||||
*/
|
||||
struct Announce {
|
||||
std::shared_ptr<Value> value;
|
||||
DoneCallback callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "listen" operation data
|
||||
*/
|
||||
struct LocalListener {
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
@@ -674,7 +691,7 @@ private:
|
||||
time_point now;
|
||||
time_point mybucket_grow_time {}, mybucket6_grow_time {};
|
||||
time_point expire_stuff_time {};
|
||||
time_point search_time {};
|
||||
time_point search_time {time_point::max()};
|
||||
time_point confirm_nodes_time {};
|
||||
time_point rotate_secrets_time {};
|
||||
std::queue<time_point> rate_limit_time {};
|
||||
@@ -795,7 +812,7 @@ private:
|
||||
/**
|
||||
* If update is true, this method will also send message to synced but non-updated search nodes.
|
||||
*/
|
||||
bool searchSendGetValues(Search& sr, SearchNode *n = nullptr, bool update = false);
|
||||
bool searchSendGetValues(Search& sr, SearchNode *n = nullptr, bool update = true);
|
||||
|
||||
void searchStep(Search& sr);
|
||||
void dumpSearch(const Search& sr, std::ostream& out) const;
|
||||
|
||||
@@ -65,7 +65,7 @@ using time_point = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
|
||||
static /*constexpr*/const time_point TIME_INVALID = {};
|
||||
static /*constexpr*/const time_point TIME_MAX {duration{std::numeric_limits<duration::rep>::max()}};
|
||||
static /*constexpr*/const time_point TIME_MAX {time_point::max()};
|
||||
|
||||
template <typename Duration = duration>
|
||||
class uniform_duration_distribution : public std::uniform_int_distribution<typename Duration::rep> {
|
||||
|
||||
+266
-216
@@ -336,6 +336,12 @@ Node::isExpired(time_point now) const
|
||||
return pinged >= 3 && reply_time < pinged_time && pinged_time + MAX_RESPONSE_TIME < now;
|
||||
}
|
||||
|
||||
bool
|
||||
Node::isMessagePending(time_point now) const
|
||||
{
|
||||
return reply_time < pinged_time && pinged_time + MAX_RESPONSE_TIME > now;
|
||||
}
|
||||
|
||||
void
|
||||
Node::update(const sockaddr* sa, socklen_t salen)
|
||||
{
|
||||
@@ -343,7 +349,7 @@ Node::update(const sockaddr* sa, socklen_t salen)
|
||||
sslen = salen;
|
||||
}
|
||||
|
||||
/** To be called when a message was sent to the noe */
|
||||
/** To be called when a message was sent to the node */
|
||||
void
|
||||
Node::requested(time_point now)
|
||||
{
|
||||
@@ -523,9 +529,7 @@ Dht::trySearchInsert(const std::shared_ptr<Node>& node)
|
||||
if (s.af != family) continue;
|
||||
if (s.insertNode(node, now)) {
|
||||
inserted = true;
|
||||
auto tm = s.getNextStepTime(types, now);
|
||||
if (tm != TIME_INVALID && (search_time == TIME_INVALID || search_time > tm))
|
||||
search_time = tm;
|
||||
search_time = std::min(search_time, s.getNextStepTime(types, now));
|
||||
}
|
||||
}
|
||||
return inserted;
|
||||
@@ -553,7 +557,7 @@ Dht::newNode(const InfoHash& id, const sockaddr *sa, socklen_t salen, int confir
|
||||
if (n->id != id) continue;
|
||||
/* Known node. Update stuff. */
|
||||
|
||||
if (confirm || n->time < now - Node::NODE_EXPIRE_TIME) {
|
||||
if (confirm || n->time + Node::NODE_EXPIRE_TIME < now) {
|
||||
n->update(sa, salen);
|
||||
if (confirm) {
|
||||
n->received(now, confirm >= 2);
|
||||
@@ -674,6 +678,7 @@ Dht::Search::removeExpiredNode(time_point now)
|
||||
e = std::prev(e);
|
||||
const Node& n = *e->node;
|
||||
if (n.isExpired(now) and n.time + Node::NODE_EXPIRE_TIME < now) {
|
||||
//std::cout << "Removing expired node " << n.id << " from IPv" << (af==AF_INET?'4':'6') << " search " << id << std::endl;
|
||||
nodes.erase(e);
|
||||
return true;
|
||||
}
|
||||
@@ -695,7 +700,7 @@ Dht::Search::insertNode(std::shared_ptr<Node> node, time_point now, const Blob&
|
||||
const auto& nid = node->id;
|
||||
|
||||
// Fast track for the case where the node is not relevant for this search
|
||||
if (nodes.size() == SEARCH_NODES && id.xorCmp(nid, nodes.back().node->id) > 0 && node->isExpired(now))
|
||||
if (nodes.size() >= SEARCH_NODES && id.xorCmp(nid, nodes.back().node->id) > 0 && node->isExpired(now))
|
||||
return false;
|
||||
|
||||
// Reset search timer if it was empty
|
||||
@@ -705,7 +710,10 @@ Dht::Search::insertNode(std::shared_ptr<Node> node, time_point now, const Blob&
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
unsigned num_candidates = 0;
|
||||
auto n = std::find_if(nodes.begin(), nodes.end(), [&](const SearchNode& sn) {
|
||||
if (sn.candidate)
|
||||
num_candidates++;
|
||||
if (sn.node == node) {
|
||||
found = true;
|
||||
return true;
|
||||
@@ -713,7 +721,7 @@ Dht::Search::insertNode(std::shared_ptr<Node> node, time_point now, const Blob&
|
||||
return id.xorCmp(nid, sn.node->id) < 0;
|
||||
});
|
||||
if (!found) {
|
||||
if (nodes.size() == SEARCH_NODES) {
|
||||
if (nodes.size()-num_candidates >= SEARCH_NODES) {
|
||||
if (node->isExpired(now))
|
||||
return false;
|
||||
if (n == nodes.end()) {
|
||||
@@ -723,14 +731,27 @@ Dht::Search::insertNode(std::shared_ptr<Node> node, time_point now, const Blob&
|
||||
n = nodes.end();
|
||||
}
|
||||
}
|
||||
bool synced = isSynced(now);
|
||||
n = nodes.insert(n, SearchNode(node));
|
||||
if (nodes.size() > SEARCH_NODES and not removeExpiredNode(now))
|
||||
nodes.pop_back();
|
||||
node->time = now;
|
||||
if (synced) {
|
||||
n->candidate = true;
|
||||
//std::cout << "Adding candidate node " << node->id << " to IPv" << (af==AF_INET?'4':'6') << " synced search " << id << std::endl;
|
||||
}/* else {
|
||||
std::cout << "Adding real node " << node->id << " to IPv" << (af==AF_INET?'4':'6') << " synced search " << id << std::endl;
|
||||
}*/
|
||||
while (nodes.size()-num_candidates > SEARCH_NODES)
|
||||
if (not removeExpiredNode(now))
|
||||
nodes.pop_back();
|
||||
expired = false;
|
||||
}
|
||||
if (not token.empty()) {
|
||||
n->getStatus.reply_time = now;
|
||||
n->getStatus.request_time = TIME_INVALID;
|
||||
if (n->candidate) {
|
||||
n->candidate = false;
|
||||
//std::cout << "Confirm candidate node " << node->id << " to synced search " << id << std::endl;
|
||||
}
|
||||
if (token.size() <= 64)
|
||||
n->token = token;
|
||||
expired = false;
|
||||
@@ -784,7 +805,7 @@ Dht::searchSendGetValues(Search& sr, SearchNode* pn, bool update)
|
||||
}
|
||||
|
||||
DHT_WARN("Sending get_values to %s (%s) for %s (p %d last get %lf)",
|
||||
print_addr((sockaddr*)&n->node->ss, n->node->sslen).c_str(),
|
||||
print_addr(n->node->ss, n->node->sslen).c_str(),
|
||||
n->node->id.toString().c_str(),
|
||||
sr.id.toString().c_str(),
|
||||
n->node->pinged,
|
||||
@@ -793,6 +814,9 @@ Dht::searchSendGetValues(Search& sr, SearchNode* pn, bool update)
|
||||
sendGetValues((sockaddr*)&n->node->ss, n->node->sslen, TransId {TransPrefix::GET_VALUES, sr.tid}, sr.id, -1, n->node->reply_time >= now - UDP_REPLY_TIME);
|
||||
n->getStatus.request_time = now;
|
||||
pinged(*n->node);
|
||||
|
||||
//std::cout << "Sending GET to " << n->node->id << " for IPv" << (sr.af==AF_INET?'4':'6') << " search " << sr.id << " (now pinged " << n->node->pinged << ")" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -802,6 +826,7 @@ void
|
||||
Dht::searchStep(Search& sr)
|
||||
{
|
||||
DHT_WARN("Search IPv%c %s searchStep.", sr.af == AF_INET ? '4' : '6', sr.id.toString().c_str());
|
||||
sr.step_time = now;
|
||||
|
||||
/* Check if the first TARGET_NODES (8) live nodes have replied. */
|
||||
if (sr.isSynced(now)) {
|
||||
@@ -820,128 +845,123 @@ Dht::searchStep(Search& sr)
|
||||
}
|
||||
if (sr.callbacks.empty() && sr.announce.empty() && sr.listeners.empty())
|
||||
sr.done = true;
|
||||
else if (not sr.callbacks.empty()) {
|
||||
unsigned i = 0;
|
||||
for (auto& sn : sr.nodes) {
|
||||
i += searchSendGetValues(sr, &sn, true) ? 1 : 0;
|
||||
if (i >= 3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// true if this node is part of the target nodes cluter.
|
||||
bool in = sr.id.xorCmp(myid, sr.nodes.back().node->id) < 0;
|
||||
}
|
||||
|
||||
DHT_DEBUG("searchStep (synced%s).", in ? ", in" : "");
|
||||
// true if this node is part of the target nodes cluter.
|
||||
bool in = sr.id.xorCmp(myid, sr.nodes.back().node->id) < 0;
|
||||
|
||||
if (not sr.listeners.empty()) {
|
||||
unsigned i = 0;
|
||||
for (auto& n : sr.nodes) {
|
||||
if (n.node->isExpired(now))
|
||||
continue;
|
||||
if (n.getListenTime() <= now) {
|
||||
DHT_DEBUG("Sending listen to %s (%s).", print_addr((sockaddr*)&n.node->ss, n.node->sslen).c_str(), n.node->id.toString().c_str());
|
||||
sendListen((sockaddr*)&n.node->ss, n.node->sslen, TransId {TransPrefix::LISTEN, sr.tid}, sr.id, n.token, n.node->reply_time >= now - UDP_REPLY_TIME);
|
||||
n.pending = true;
|
||||
n.listenStatus.request_time = now;
|
||||
}
|
||||
if (++i == LISTEN_NODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
DHT_DEBUG("searchStep (synced%s).", in ? ", in" : "");
|
||||
DHT_DEBUG("%s", getSearchesLog(sr.af).c_str());
|
||||
|
||||
// Announce requests
|
||||
for (auto& a : sr.announce) {
|
||||
if (!a.value) {
|
||||
if (not sr.listeners.empty()) {
|
||||
unsigned i = 0, t = 0;
|
||||
for (auto& n : sr.nodes) {
|
||||
if (n.node->isExpired(now) or (n.candidate and t >= LISTEN_NODES))
|
||||
continue;
|
||||
DHT_ERROR("Trying to announce a null value !");
|
||||
}
|
||||
unsigned i = 0;
|
||||
auto vid = a.value->id;
|
||||
const auto& type = getType(a.value->type);
|
||||
if (in) {
|
||||
DHT_WARN("Storing local value");
|
||||
storageStore(sr.id, a.value);
|
||||
}
|
||||
for (auto& n : sr.nodes) {
|
||||
if (n.node->isExpired(now))
|
||||
continue;
|
||||
auto a_status = n.acked.find(vid);
|
||||
auto at = n.getAnnounceTime(a_status, type);
|
||||
if ( at <= now ) {
|
||||
DHT_DEBUG("Sending announce_value to %s (%s).", print_addr((sockaddr*)&n.node->ss, n.node->sslen).c_str(), n.node->id.toString().c_str());
|
||||
sendAnnounceValue((sockaddr*)&n.node->ss, n.node->sslen,
|
||||
TransId {TransPrefix::ANNOUNCE_VALUES, sr.tid}, sr.id, *a.value,
|
||||
n.token, n.node->reply_time >= now - UDP_REPLY_TIME);
|
||||
if (a_status == n.acked.end()) {
|
||||
n.acked[vid] = { now };
|
||||
} else {
|
||||
a_status->second.request_time = now;
|
||||
}
|
||||
// use the "pending" flag so we update the "pinged"
|
||||
// fields after sending the announce requests for every value to announce
|
||||
n.pending = true;
|
||||
}
|
||||
if (++i == TARGET_NODES)
|
||||
break;
|
||||
if (n.getListenTime() <= now) {
|
||||
DHT_DEBUG("Sending listen to %s (%s).", print_addr(n.node->ss, n.node->sslen).c_str(), n.node->id.toString().c_str());
|
||||
sendListen((sockaddr*)&n.node->ss, n.node->sslen, TransId {TransPrefix::LISTEN, sr.tid}, sr.id, n.token, n.node->reply_time >= now - UDP_REPLY_TIME);
|
||||
n.pending = true;
|
||||
n.listenStatus.request_time = now;
|
||||
}
|
||||
t++;
|
||||
if (not n.candidate and ++i == LISTEN_NODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Announce requests
|
||||
for (auto& a : sr.announce) {
|
||||
if (!a.value) {
|
||||
continue;
|
||||
DHT_ERROR("Trying to announce a null value !");
|
||||
}
|
||||
unsigned i = 0, t = 0;
|
||||
auto vid = a.value->id;
|
||||
const auto& type = getType(a.value->type);
|
||||
if (in) {
|
||||
DHT_WARN("Storing local value");
|
||||
(sr.id, a.value);
|
||||
}
|
||||
for (auto& n : sr.nodes) {
|
||||
if (n.pending) {
|
||||
n.pending = false;
|
||||
pinged(*n.node);
|
||||
if (n.node->isExpired(now) or (n.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
auto a_status = n.acked.find(vid);
|
||||
auto at = n.getAnnounceTime(a_status, type);
|
||||
if ( at <= now ) {
|
||||
DHT_DEBUG("Sending announce_value to %s (%s).", print_addr(n.node->ss, n.node->sslen).c_str(), n.node->id.toString().c_str());
|
||||
sendAnnounceValue((sockaddr*)&n.node->ss, n.node->sslen,
|
||||
TransId {TransPrefix::ANNOUNCE_VALUES, sr.tid}, sr.id, *a.value,
|
||||
n.token, n.node->reply_time >= now - UDP_REPLY_TIME);
|
||||
if (a_status == n.acked.end()) {
|
||||
n.acked[vid] = { now };
|
||||
} else {
|
||||
a_status->second.request_time = now;
|
||||
}
|
||||
// use the "pending" flag so we update the "pinged"
|
||||
// fields after sending the announce requests for every value to announce
|
||||
n.pending = true;
|
||||
}
|
||||
t++;
|
||||
if (not n.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
if (sr.announce.empty() && sr.listeners.empty())
|
||||
sr.done = true;
|
||||
DHT_DEBUG("Searchstep done.");
|
||||
}
|
||||
for (auto& n : sr.nodes) {
|
||||
if (n.pending) {
|
||||
n.pending = false;
|
||||
pinged(*n.node);
|
||||
}
|
||||
}
|
||||
if (sr.callbacks.empty() && sr.announce.empty() && sr.listeners.empty())
|
||||
sr.done = true;
|
||||
DHT_DEBUG("Searchstep done.");
|
||||
|
||||
} else {
|
||||
if (sr.get_step_time + SEARCH_GET_STEP >= now) {
|
||||
DHT_WARN("searchStep: too early");
|
||||
return;
|
||||
}
|
||||
DHT_DEBUG("searchStep.");
|
||||
|
||||
unsigned i = 0, e = 0;
|
||||
for (auto& sn : sr.nodes) {
|
||||
if (sn.node->isExpired(now)) {
|
||||
e++;
|
||||
continue;
|
||||
}
|
||||
i += searchSendGetValues(sr, &sn) ? 1 : 0;
|
||||
if (i >= 3)
|
||||
break;
|
||||
}
|
||||
if (i > 0)
|
||||
sr.get_step_time = now;
|
||||
else if (e == sr.nodes.size()) {
|
||||
sr.expired = true;
|
||||
if (sr.announce.empty() && sr.listeners.empty()) {
|
||||
// Listening or announcing requires keeping the cluster up to date.
|
||||
sr.done = true;
|
||||
}
|
||||
{
|
||||
auto get_cbs = std::move(sr.callbacks);
|
||||
for (const auto& g : get_cbs) {
|
||||
if (g.done_cb)
|
||||
g.done_cb(false, {});
|
||||
}
|
||||
}
|
||||
{
|
||||
std::vector<DoneCallback> a_cbs;
|
||||
a_cbs.reserve(sr.announce.size());
|
||||
for (const auto& a : sr.announce)
|
||||
if (a.callback)
|
||||
a_cbs.emplace_back(std::move(a.callback));
|
||||
for (const auto& a : a_cbs)
|
||||
a(false, {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
sr.step_time = now;
|
||||
|
||||
if (sr.get_step_time + SEARCH_GET_STEP >= now) {
|
||||
DHT_WARN("searchStep: too early");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned i = 0;
|
||||
bool sent;
|
||||
do {
|
||||
if (sent = searchSendGetValues(sr))
|
||||
i++;
|
||||
}
|
||||
while (sent and i < 3);
|
||||
DHT_DEBUG("searchStep, sent %u.", i);
|
||||
|
||||
if (i > 0)
|
||||
sr.get_step_time = now;
|
||||
else if (std::count_if(sr.nodes.begin(), sr.nodes.end(), [&](const SearchNode& sn) {
|
||||
return sn.node->isExpired(now);
|
||||
}) == sr.nodes.size())
|
||||
{
|
||||
sr.expired = true;
|
||||
if (sr.announce.empty() && sr.listeners.empty()) {
|
||||
// Listening or announcing requires keeping the cluster up to date.
|
||||
sr.done = true;
|
||||
}
|
||||
{
|
||||
auto get_cbs = std::move(sr.callbacks);
|
||||
for (const auto& g : get_cbs) {
|
||||
if (g.done_cb)
|
||||
g.done_cb(false, {});
|
||||
}
|
||||
}
|
||||
{
|
||||
std::vector<DoneCallback> a_cbs;
|
||||
a_cbs.reserve(sr.announce.size());
|
||||
for (const auto& a : sr.announce)
|
||||
if (a.callback)
|
||||
a_cbs.emplace_back(std::move(a.callback));
|
||||
for (const auto& a : a_cbs)
|
||||
a(false, {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -985,9 +1005,9 @@ Dht::Search::isSynced(time_point now) const
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.node->isExpired(now))
|
||||
if (n.node->isExpired(now) or n.candidate)
|
||||
continue;
|
||||
if (!n.isSynced(now))
|
||||
if (not n.isSynced(now))
|
||||
return false;
|
||||
if (++i == TARGET_NODES)
|
||||
break;
|
||||
@@ -1008,9 +1028,9 @@ bool
|
||||
Dht::Search::isDone(const Get& get, time_point now) const
|
||||
{
|
||||
unsigned i = 0;
|
||||
auto limit = std::max(get.start, now - Node::NODE_EXPIRE_TIME);
|
||||
const auto limit = std::max(get.start, now - Node::NODE_EXPIRE_TIME);
|
||||
for (const auto& sn : nodes) {
|
||||
if (sn.node->isExpired(now))
|
||||
if (sn.node->isExpired(now) or sn.candidate)
|
||||
continue;
|
||||
if (sn.getStatus.reply_time < limit)
|
||||
return false;
|
||||
@@ -1023,31 +1043,33 @@ Dht::Search::isDone(const Get& get, time_point now) const
|
||||
time_point
|
||||
Dht::Search::getUpdateTime(time_point now) const
|
||||
{
|
||||
time_point ut = TIME_MAX;
|
||||
time_point ut = time_point::max();
|
||||
const auto last_get = getLastGetTime();
|
||||
unsigned i = 0, d = 0;
|
||||
unsigned i = 0, t = 0, d = 0;
|
||||
for (const auto& sn : nodes) {
|
||||
if (sn.node->isExpired(now))
|
||||
if (sn.node->isExpired(now) or (sn.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
if (sn.getStatus.reply_time < now - Node::NODE_EXPIRE_TIME) { // not isSynced
|
||||
return std::min(ut, get_step_time + SEARCH_GET_STEP);
|
||||
}
|
||||
else if (sn.getStatus.reply_time < last_get) {
|
||||
ut = std::min(ut, sn.getStatus.request_time + Node::MAX_RESPONSE_TIME);
|
||||
if (sn.getStatus.reply_time < std::max(now - Node::NODE_EXPIRE_TIME, last_get)) {
|
||||
// not isSynced
|
||||
ut = std::min(ut, std::max(
|
||||
sn.getStatus.request_time + Node::MAX_RESPONSE_TIME,
|
||||
get_step_time + SEARCH_GET_STEP));
|
||||
if (not sn.candidate)
|
||||
d++;
|
||||
} else {
|
||||
d++;
|
||||
ut = std::min(ut, std::max(
|
||||
sn.getStatus.request_time + Node::MAX_RESPONSE_TIME,
|
||||
sn.getStatus.reply_time + Node::NODE_EXPIRE_TIME));
|
||||
}
|
||||
if (++i == TARGET_NODES)
|
||||
t++;
|
||||
if (not sn.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
if (not callbacks.empty() and i == d) {
|
||||
if (not callbacks.empty() and d == 0) {
|
||||
// If all synced/updated but some callbacks remain, step now to clear them
|
||||
return now;
|
||||
}
|
||||
return ut == TIME_MAX ? TIME_INVALID : ut;
|
||||
return ut;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1057,9 +1079,9 @@ Dht::Search::isAnnounced(Value::Id id, const ValueType& type, time_point now) co
|
||||
return false;
|
||||
unsigned i = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.node->isExpired(now))
|
||||
if (n.candidate or n.node->isExpired(now))
|
||||
continue;
|
||||
if (!n.isAnnounced(id, type, now))
|
||||
if (not n.isAnnounced(id, type, now))
|
||||
return false;
|
||||
if (++i == TARGET_NODES)
|
||||
break;
|
||||
@@ -1074,7 +1096,7 @@ Dht::Search::isListening(time_point now) const
|
||||
return false;
|
||||
unsigned i = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.node->isExpired(now))
|
||||
if (n.candidate or n.node->isExpired(now))
|
||||
continue;
|
||||
if (!n.isListening(now))
|
||||
return false;
|
||||
@@ -1088,20 +1110,19 @@ time_point
|
||||
Dht::Search::getAnnounceTime(const std::map<ValueType::Id, ValueType>& types, time_point now) const
|
||||
{
|
||||
if (nodes.empty())
|
||||
return {};
|
||||
time_point ret {};
|
||||
return time_point::max();
|
||||
time_point ret {time_point::max()};
|
||||
for (const auto& a : announce) {
|
||||
if (!a.value) continue;
|
||||
auto type_it = types.find(a.value->type);
|
||||
const ValueType& type = (type_it == types.end()) ? ValueType::USER_DATA : type_it->second;
|
||||
unsigned i = 0;
|
||||
unsigned i = 0, t = 0;
|
||||
for (const auto& n : nodes) {
|
||||
if (n.node->isExpired(now))
|
||||
if (n.node->isExpired(now) or (n.candidate and t >= TARGET_NODES))
|
||||
continue;
|
||||
auto at = n.getAnnounceTime(a.value->id, type);
|
||||
if (at != TIME_INVALID && (ret == TIME_INVALID || ret > at))
|
||||
ret = at;
|
||||
if (++i == TARGET_NODES)
|
||||
ret = std::min(ret, n.getAnnounceTime(a.value->id, type));
|
||||
t++;
|
||||
if (not n.candidate and ++i == TARGET_NODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1112,15 +1133,16 @@ time_point
|
||||
Dht::Search::getListenTime(time_point now) const
|
||||
{
|
||||
if (listeners.empty())
|
||||
return {};
|
||||
time_point listen_time {};
|
||||
unsigned i = 0;
|
||||
return time_point::max();
|
||||
time_point listen_time {time_point::max()};
|
||||
unsigned i = 0, t = 0;
|
||||
for (const auto& sn : nodes) {
|
||||
if (sn.node->isExpired(now))
|
||||
if (sn.node->isExpired(now) or (sn.candidate and t >= LISTEN_NODES))
|
||||
continue;
|
||||
auto lt = sn.getListenTime();
|
||||
listen_time = listen_time == TIME_INVALID ? lt : std::min(listen_time, lt);
|
||||
if (++i == LISTEN_NODES)
|
||||
listen_time = std::min(listen_time, lt);
|
||||
t++;
|
||||
if (not sn.candidate and ++i == LISTEN_NODES)
|
||||
break;
|
||||
}
|
||||
return listen_time;
|
||||
@@ -1129,33 +1151,33 @@ Dht::Search::getListenTime(time_point now) const
|
||||
time_point
|
||||
Dht::Search::getNextStepTime(const std::map<ValueType::Id, ValueType>& types, time_point now) const
|
||||
{
|
||||
auto next_step = time_point::max();
|
||||
if (expired or done)
|
||||
return {};
|
||||
|
||||
if (!isSynced(now))
|
||||
return get_step_time + SEARCH_GET_STEP;
|
||||
|
||||
auto next_step = TIME_MAX;
|
||||
return next_step;
|
||||
|
||||
auto ut = getUpdateTime(now);
|
||||
if (ut != TIME_INVALID) {
|
||||
if (ut != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " update time in " << print_dt(ut - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, ut);
|
||||
}
|
||||
|
||||
auto at = getAnnounceTime(types, now);
|
||||
if (at != TIME_INVALID) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " announce time in " << print_dt(at - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, at);
|
||||
if (isSynced(now))
|
||||
{
|
||||
auto at = getAnnounceTime(types, now);
|
||||
if (at != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " announce time in " << print_dt(at - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, at);
|
||||
}
|
||||
|
||||
auto lt = getListenTime(now);
|
||||
if (lt != time_point::max()) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " listen time in " << print_dt(lt - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, lt);
|
||||
}
|
||||
}
|
||||
|
||||
auto lt = getListenTime(now);
|
||||
if (lt != TIME_INVALID) {
|
||||
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " listen time in " << print_dt(lt - now) << " s" << std::endl;
|
||||
next_step = std::min(next_step, lt);
|
||||
}
|
||||
|
||||
return next_step == TIME_MAX ? TIME_INVALID : next_step;
|
||||
return next_step;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1259,7 +1281,7 @@ Dht::announce(const InfoHash& id, sa_family_t af, const std::shared_ptr<Value>&
|
||||
a_sr->callback = callback;
|
||||
}
|
||||
auto tm = sr->getNextStepTime(types, now);
|
||||
if (tm != TIME_INVALID && (search_time == TIME_INVALID || search_time > tm)) {
|
||||
if (tm < search_time) {
|
||||
DHT_DEBUG("search_time is now in %lf", print_dt(tm-clock::now()));
|
||||
search_time = tm;
|
||||
} else {
|
||||
@@ -1285,9 +1307,7 @@ Dht::listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter
|
||||
sr->done = false;
|
||||
auto token = ++sr->listener_token;
|
||||
sr->listeners.emplace(token, LocalListener{f, cb});
|
||||
auto tm = sr->getNextStepTime(types, now);
|
||||
if (tm != TIME_INVALID && (search_time == TIME_INVALID || search_time > tm))
|
||||
search_time = tm;
|
||||
search_time = std::min(search_time, sr->getNextStepTime(types, now));
|
||||
return token;
|
||||
}
|
||||
|
||||
@@ -1802,7 +1822,7 @@ void
|
||||
Dht::dumpSearch(const Search& sr, std::ostream& out) const
|
||||
{
|
||||
using namespace std::chrono;
|
||||
out << std::endl << "Search (IPv" << (sr.af == AF_INET6 ? "6" : "4") << ") " << sr.id;
|
||||
out << std::endl << "Search IPv" << (sr.af == AF_INET6 ? '6' : '4') << ' ' << sr.id << " G" << sr.callbacks.size();
|
||||
out << " age " << duration_cast<seconds>(now - sr.step_time).count() << " s tid " << sr.tid;
|
||||
if (sr.done)
|
||||
out << " [done]";
|
||||
@@ -1816,28 +1836,65 @@ Dht::dumpSearch(const Search& sr, std::ostream& out) const
|
||||
|
||||
for (const auto& n : sr.announce) {
|
||||
bool announced = sr.isAnnounced(n.value->id, getType(n.value->type), now);
|
||||
out << " Announcement: " << *n.value << (announced ? " [announced]" : "") << std::endl;
|
||||
out << "Announcement: " << *n.value << (announced ? " [announced]" : "") << std::endl;
|
||||
}
|
||||
|
||||
out << " Common bits InfoHash Conn. Get Put IP" << std::endl;
|
||||
unsigned i = 0;
|
||||
auto last_get = sr.getLastGetTime();
|
||||
for (const auto& n : sr.nodes) {
|
||||
out << " Node " << i++ << " id " << n.node->id << " bits " << InfoHash::commonBits(sr.id, n.node->id);
|
||||
if (n.node->pinged) {
|
||||
out << " [c " << n.node->pinged << " "
|
||||
<< duration_cast<seconds>(now - n.node->pinged_time).count() << "s "
|
||||
<< duration_cast<seconds>(now - n.node->reply_time).count() << "s]";
|
||||
out << std::setfill (' ') << std::setw(3) << InfoHash::commonBits(sr.id, n.node->id) << ' ' << n.node->id;
|
||||
out << ' ' << (findNode(n.node->id, AF_INET) || findNode(n.node->id, AF_INET6) ? '*' : ' ');
|
||||
out << ' ' << (n.candidate ? 'c' : ' ');
|
||||
out << " ["
|
||||
<< (n.node->isMessagePending(now) ? 'f':' ');
|
||||
if (n.node->pinged)
|
||||
out << n.node->pinged;
|
||||
else
|
||||
out << ' ';
|
||||
out << (n.node->isExpired(now) ? 'x' : ' ') << "]";
|
||||
|
||||
out << " ["
|
||||
<< (n.getStatus.pending(now) ? 'f' : (n.getStatus.expired(now) ? 'x' : ' '))
|
||||
<< (n.isSynced(now) ? 's' : '-')
|
||||
<< ((n.getStatus.reply_time > last_get) ? 'u' : '-') << "] ";
|
||||
|
||||
if (not sr.listeners.empty()) {
|
||||
if (n.listenStatus.request_time == time_point{})
|
||||
out << " ";
|
||||
else
|
||||
out << "["
|
||||
<< (n.listenStatus.pending(now) ? 'f' : (n.listenStatus.expired(now) ? 'x' : ' '))
|
||||
<< (n.isListening(now) ? 'l' : '-') << "] ";
|
||||
}
|
||||
out << " [g ";
|
||||
if (n.getStatus.request_time != TIME_INVALID)
|
||||
out << duration_cast<seconds>(now - n.getStatus.request_time).count() << "s ";
|
||||
out << duration_cast<seconds>(now - n.getStatus.reply_time).count() << "s]";
|
||||
if (findNode(n.node->id, AF_INET) || findNode(n.node->id, AF_INET6))
|
||||
out << " [known]";
|
||||
if (n.node->reply_time != TIME_INVALID)
|
||||
out << " [replied]";
|
||||
if (n.listenStatus.reply_time != TIME_INVALID)
|
||||
out << " [list]";
|
||||
out << (n.isSynced(now) ? " [synced]" : (n.node->isExpired(now) ? " [expired]" : " [not synced]"));
|
||||
|
||||
if (not sr.announce.empty()) {
|
||||
if (n.acked.empty()) {
|
||||
out << " ";
|
||||
for (const auto& a : sr.announce)
|
||||
out << ' ';
|
||||
} else {
|
||||
out << "[";
|
||||
for (const auto& a : sr.announce) {
|
||||
auto ack = n.acked.find(a.value->id);
|
||||
if (ack == n.acked.end()) {
|
||||
out << ' ';
|
||||
} else {
|
||||
auto& astatus = ack->second;
|
||||
if (astatus.reply_time + getType(a.value->type).expiration > now)
|
||||
out << 'a';
|
||||
else if (astatus.pending(now))
|
||||
out << 'f';
|
||||
else if (astatus.expired(now))
|
||||
out << 'x';
|
||||
else
|
||||
out << ' ';
|
||||
}
|
||||
}
|
||||
out << "] ";
|
||||
}
|
||||
}
|
||||
out << print_addr(n.node->ss, n.node->sslen);
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -1899,11 +1956,13 @@ Dht::getRoutingTablesLog(sa_family_t af) const
|
||||
}
|
||||
|
||||
std::string
|
||||
Dht::getSearchesLog(sa_family_t) const
|
||||
Dht::getSearchesLog(sa_family_t af) const
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "s:synched, u:updated, a:announced, c:candidate, f:cur req, x:expired, *:known" << std::endl;
|
||||
for (const auto& sr : searches)
|
||||
dumpSearch(sr, out);
|
||||
if (af == 0 or sr.af == af)
|
||||
dumpSearch(sr, out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
@@ -2120,6 +2179,8 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout << "Message from " << id << " IPv" << (from->sa_family==AF_INET?'4':'6') << std::endl;
|
||||
|
||||
switch (message) {
|
||||
case MessageType::Error:
|
||||
if (tid.length != 4) return;
|
||||
@@ -2208,14 +2269,9 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc
|
||||
/* Since we received a reply, the number of
|
||||
requests in flight has decreased. Let's push
|
||||
another request. */
|
||||
/*if (sr->isSynced(now)) {
|
||||
DHT_DEBUG("Trying to accelerate search!");
|
||||
search_time = now;
|
||||
//sr->step_time = 0;
|
||||
} else {*/
|
||||
if (searchSendGetValues(*sr))
|
||||
sr->get_step_time = now;
|
||||
//}
|
||||
//std::cout << "Received reply from " << id << ", sending new message..." << std::endl;
|
||||
if (searchSendGetValues(*sr))
|
||||
sr->get_step_time = now;
|
||||
}
|
||||
}
|
||||
if (sr) {
|
||||
@@ -2416,19 +2472,17 @@ Dht::periodic(const uint8_t *buf, size_t buflen,
|
||||
expireSearches();
|
||||
}
|
||||
|
||||
if (search_time != TIME_INVALID && now > search_time) {
|
||||
search_time = TIME_INVALID;
|
||||
if (now > search_time) {
|
||||
search_time = time_point::max();
|
||||
for (auto& sr : searches) {
|
||||
auto tm = sr.getNextStepTime(types, now);
|
||||
if (tm == TIME_INVALID) continue;
|
||||
if (tm <= now) {
|
||||
auto step = sr.getNextStepTime(types, now);
|
||||
if (step <= now) {
|
||||
searchStep(sr);
|
||||
tm = sr.getNextStepTime(types, now);
|
||||
step = sr.getNextStepTime(types, now);
|
||||
}
|
||||
if (tm != TIME_INVALID && (search_time == TIME_INVALID || search_time > tm))
|
||||
search_time = tm;
|
||||
search_time = std::min(search_time, step);
|
||||
}
|
||||
if (search_time == TIME_INVALID)
|
||||
if (search_time == time_point::max())
|
||||
DHT_DEBUG("next search time : (none)");
|
||||
else
|
||||
DHT_DEBUG("next search time : %lf s%s", print_dt(search_time-now), (search_time < now)?" (ASAP)":"");
|
||||
@@ -2459,11 +2513,7 @@ Dht::periodic(const uint8_t *buf, size_t buflen,
|
||||
dumpTables();
|
||||
}
|
||||
|
||||
time_point tosleep = confirm_nodes_time;
|
||||
if (search_time != TIME_INVALID)
|
||||
tosleep = std::min(tosleep, search_time);
|
||||
|
||||
return tosleep;
|
||||
return std::min(confirm_nodes_time, search_time);
|
||||
}
|
||||
|
||||
std::vector<Dht::ValuesExport>
|
||||
|
||||
Reference in new issue
Block a user