| 628 | } |
| 629 | |
| 630 | void node_impl::fetch_items_loop() |
| 631 | { |
| 632 | VERIFY_CORRECT_THREAD(); |
| 633 | while (!_fetch_item_loop_done.canceled()) |
| 634 | { |
| 635 | _items_to_fetch_updated = false; |
| 636 | dlog("beginning an iteration of fetch items (${count} items to fetch)", |
| 637 | ("count", _items_to_fetch.size())); |
| 638 | |
| 639 | fc::time_point oldest_timestamp_to_fetch = fc::time_point::now() |
| 640 | - fc::seconds(_recent_block_interval_seconds * GRAPHENE_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS); |
| 641 | fc::time_point next_peer_unblocked_time = fc::time_point::maximum(); |
| 642 | |
| 643 | // we need to construct a list of items to request from each peer first, |
| 644 | // then send the messages (in two steps, to avoid yielding while iterating) |
| 645 | // we want to evenly distribute our requests among our peers. |
| 646 | struct requested_item_count_index {}; |
| 647 | struct peer_and_items_to_fetch |
| 648 | { |
| 649 | peer_connection_ptr peer; |
| 650 | std::vector<item_id> item_ids; |
| 651 | peer_and_items_to_fetch(const peer_connection_ptr& peer) : peer(peer) {} |
| 652 | bool operator<(const peer_and_items_to_fetch& rhs) const { return peer < rhs.peer; } |
| 653 | size_t number_of_items() const { return item_ids.size(); } |
| 654 | }; |
| 655 | using fetch_messages_to_send_set = boost::multi_index_container< peer_and_items_to_fetch, bmi::indexed_by< |
| 656 | bmi::ordered_unique< |
| 657 | bmi::member<peer_and_items_to_fetch, peer_connection_ptr, &peer_and_items_to_fetch::peer> >, |
| 658 | bmi::ordered_non_unique< bmi::tag<requested_item_count_index>, |
| 659 | bmi::const_mem_fun<peer_and_items_to_fetch, size_t, &peer_and_items_to_fetch::number_of_items> > |
| 660 | > >; |
| 661 | fetch_messages_to_send_set items_by_peer; |
| 662 | |
| 663 | // initialize the fetch_messages_to_send with an empty set of items for all idle peers |
| 664 | { |
| 665 | fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex()); |
| 666 | for (const peer_connection_ptr& peer : _active_connections) |
| 667 | if (peer->idle()) |
| 668 | items_by_peer.insert(peer_and_items_to_fetch(peer)); |
| 669 | } |
| 670 | |
| 671 | // now loop over all items we want to fetch |
| 672 | for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();) |
| 673 | { |
| 674 | if (item_iter->timestamp < oldest_timestamp_to_fetch) |
| 675 | { |
| 676 | // this item has probably already fallen out of our peers' caches, we'll just ignore it. |
| 677 | // this can happen during flooding, and the _items_to_fetch could otherwise get clogged |
| 678 | // with a bunch of items that we'll never be able to request from any peer |
| 679 | wlog("Unable to fetch item ${item} before its likely expiration time, " |
| 680 | "removing it from our list of items to fetch", |
| 681 | ("item", item_iter->item)); |
| 682 | item_iter = _items_to_fetch.erase(item_iter); |
| 683 | } |
| 684 | else |
| 685 | { |
| 686 | // find a peer that has it, we'll use the one who has the least requests going to it to load balance |
| 687 | bool item_fetched = false; |
nothing calls this directly
no test coverage detected