| 1091 | } |
| 1092 | |
| 1093 | void NodeImpl::fetch_items_loop() |
| 1094 | { |
| 1095 | VERIFY_CORRECT_THREAD(); |
| 1096 | while (!_fetch_item_loop_done.canceled()) |
| 1097 | { |
| 1098 | _items_to_fetch_updated = false; |
| 1099 | dlog("beginning an iteration of fetch items (${count} items to fetch)", |
| 1100 | ("count", _items_to_fetch.size())); |
| 1101 | |
| 1102 | fc::time_point oldest_timestamp_to_fetch = fc::time_point::now() - fc::seconds(10 * 5); |
| 1103 | fc::time_point next_peer_unblocked_time = fc::time_point::maximum(); |
| 1104 | |
| 1105 | // we need to construct a list of items to request from each peer first, |
| 1106 | // then send the messages (in two steps, to avoid yielding while iterating) |
| 1107 | // we want to evenly distribute our requests among our peers. |
| 1108 | struct requested_item_count_index {}; |
| 1109 | struct peer_and_items_to_fetch |
| 1110 | { |
| 1111 | PeerConnectionPtr peer; |
| 1112 | std::vector<ItemId> item_ids; |
| 1113 | peer_and_items_to_fetch(const PeerConnectionPtr& peer) : peer(peer) {} |
| 1114 | bool operator<(const peer_and_items_to_fetch& rhs) const { return peer < rhs.peer; } |
| 1115 | size_t number_of_items() const { return item_ids.size(); } |
| 1116 | }; |
| 1117 | typedef boost::multi_index_container<peer_and_items_to_fetch, |
| 1118 | boost::multi_index::indexed_by<boost::multi_index::ordered_unique<boost::multi_index::member<peer_and_items_to_fetch, PeerConnectionPtr, &peer_and_items_to_fetch::peer> >, |
| 1119 | boost::multi_index::ordered_non_unique<boost::multi_index::tag<requested_item_count_index>, |
| 1120 | boost::multi_index::const_mem_fun<peer_and_items_to_fetch, size_t, &peer_and_items_to_fetch::number_of_items> > > > fetch_messages_to_send_set; |
| 1121 | fetch_messages_to_send_set items_by_peer; |
| 1122 | |
| 1123 | // initialize the fetch_messages_to_send with an empty set of items for all idle peers |
| 1124 | for (const PeerConnectionPtr& peer : _active_connections) |
| 1125 | if (peer->idle()) |
| 1126 | items_by_peer.insert(peer_and_items_to_fetch(peer)); |
| 1127 | |
| 1128 | // now loop over all items we want to fetch |
| 1129 | for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();) |
| 1130 | { |
| 1131 | if (item_iter->time_stamp < oldest_timestamp_to_fetch) |
| 1132 | { |
| 1133 | // this item has probably already fallen out of our peers' caches, we'll just ignore it. |
| 1134 | // this can happen during flooding, and the _items_to_fetch could otherwise get clogged |
| 1135 | // with a bunch of items that we'll never be able to request from any peer |
| 1136 | wlog("Unable to fetch item ${item} before its likely expiration time, removing it from our list of items to fetch", ("item", item_iter->item)); |
| 1137 | item_iter = _items_to_fetch.erase(item_iter); |
| 1138 | } |
| 1139 | else |
| 1140 | { |
| 1141 | // find a peer that has it, we'll use the one who has the least requests going to it to load balance |
| 1142 | bool item_fetched = false; |
| 1143 | for (auto peer_iter = items_by_peer.get<requested_item_count_index>().begin(); peer_iter != items_by_peer.get<requested_item_count_index>().end(); ++peer_iter) |
| 1144 | { |
| 1145 | const PeerConnectionPtr& peer = peer_iter->peer; |
| 1146 | // if they have the item and we haven't already decided to ask them for too many other items |
| 1147 | if (peer_iter->item_ids.size() < ALP_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION && |
| 1148 | peer->inventory_peer_advertised_to_us.find(item_iter->item) != peer->inventory_peer_advertised_to_us.end()) |
| 1149 | { |
| 1150 | if (item_iter->item.item_type == thinkyoung::net::trx_message_type && peer->is_transaction_fetching_inhibited()) |
nothing calls this directly
no test coverage detected