Repository navigation
fetch data from leader #770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1206,7 +1206,28 @@ void RaftReplDev::fetch_data_from_remote(std::vector< repl_req_ptr_t > rreqs) { | |
| shared< flatbuffers::FlatBufferBuilder > builder = std::make_shared< flatbuffers::FlatBufferBuilder >(); | ||
| RD_LOGD(NO_TRACE_ID, "Data Channel : FetchData from remote: rreq.size={}, my server_id={}", rreqs.size(), | ||
| server_id()); | ||
| auto const& originator = rreqs.front()->remote_blkid().server_id; | ||
|
|
||
| int32_t fetch_from_peer_id{0}; | ||
|
|
||
| if (RaftReplDev::is_fetch_data_only_from_originator()) { | ||
| // in raft_repl_dev UT, we do not implement the header parser, so it has to fetch data from originator. | ||
| fetch_from_peer_id = rreqs.front()->remote_blkid().server_id; | ||
| } else { | ||
| auto const& srv_configs = raft_server()->get_config()->get_servers(); | ||
| std::vector< int32_t > peer_ids; | ||
| for (const auto& srv_config : srv_configs) { | ||
| auto peer_id = srv_config->get_id(); | ||
| if (peer_id != m_raft_server_id) peer_ids.emplace_back(peer_id); | ||
| } | ||
| const auto available_peer_size = peer_ids.size(); | ||
| if (!available_peer_size) { | ||
| RD_LOGE(NO_TRACE_ID, "Data Channel: No available peer to fetch data from, ignoring this fetch call"); | ||
| return; | ||
| } | ||
|
|
||
| // here we use time as the random generator to select a random peer | ||
| fetch_from_peer_id = peer_ids[static_cast< long int >(std::time(nullptr)) % available_peer_size]; | ||
| } | ||
|
|
||
| for (auto const& rreq : rreqs) { | ||
| entries.push_back(CreateRequestEntry(*builder, rreq->lsn(), rreq->term(), rreq->dsn(), | ||
|
|
@@ -1215,15 +1236,9 @@ void RaftReplDev::fetch_data_from_remote(std::vector< repl_req_ptr_t > rreqs) { | |
| rreq->remote_blkid().server_id /* blkid_originator */, | ||
| builder->CreateVector(rreq->remote_blkid().blkid.serialize().cbytes(), | ||
| rreq->remote_blkid().blkid.serialized_size()))); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the originator != leader, will the remote_blkid change (due to garbage and vchunk->pchunk mapping)?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HO will read from the given remote blkid and verify data, if failed, it will try to read from index.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for your explanation. I'm not sure if we need to add some logic in HO to handle scenarios like chunk reclamation or disk bad here. Nevertheless, it looks good to me.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. chunk reclaim is fine as validate_blob will fail. Bad drive (or degrade mode) is a concern..... lets create an issue and wait zhiteng's POC on single-disk-mode....
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created a issue eBay/HomeObject#330 in HO |
||
| // relax this assert if there is a case in same batch originator can be different (can't think of one now) | ||
| // but if there were to be such case, we need to group rreqs by originator and send them in separate | ||
| // batches; | ||
| RD_DBG_ASSERT_EQ(rreq->remote_blkid().server_id, originator, "Unexpected originator for rreq={}", | ||
| rreq->to_string()); | ||
|
|
||
| RD_LOGT(rreq->traceID(), | ||
| "Fetching data from originator={}, remote: rreq=[{}], remote_blkid={}, my server_id={}", originator, | ||
| rreq->to_string(), rreq->remote_blkid().blkid.to_string(), server_id()); | ||
| RD_LOGT(rreq->traceID(), "Fetching data from peer={}, remote: rreq=[{}], remote_blkid={}, my server_id={}", | ||
| fetch_from_peer_id, rreq->to_string(), rreq->remote_blkid().blkid.to_string(), server_id()); | ||
| } | ||
|
|
||
| builder->FinishSizePrefixed( | ||
|
|
@@ -1233,16 +1248,14 @@ void RaftReplDev::fetch_data_from_remote(std::vector< repl_req_ptr_t > rreqs) { | |
| COUNTER_INCREMENT(m_metrics, fetch_total_entries_cnt, rreqs.size()); | ||
| COUNTER_INCREMENT(m_metrics, outstanding_data_fetch_cnt, 1); | ||
|
|
||
| // leader can change, on the receiving side, we need to check if the leader is still the one who originated the | ||
| // blkid; | ||
| auto const fetch_start_time = Clock::now(); | ||
| group_msg_service() | ||
| ->data_service_request_bidirectional( | ||
| originator, FETCH_DATA, | ||
| fetch_from_peer_id, FETCH_DATA, | ||
| sisl::io_blob_list_t{ | ||
| sisl::io_blob{builder->GetBufferPointer(), builder->GetSize(), false /* is_aligned */}}) | ||
| .via(&folly::InlineExecutor::instance()) | ||
| .thenValue([this, builder, rreqs = std::move(rreqs), fetch_start_time](auto response) { | ||
| .thenValue([this, builder, fetch_from_peer_id, rreqs = std::move(rreqs), fetch_start_time](auto response) { | ||
| COUNTER_DECREMENT(m_metrics, outstanding_data_fetch_cnt, 1); | ||
| auto const fetch_latency_us = get_elapsed_time_us(fetch_start_time); | ||
| HISTOGRAM_OBSERVE(m_metrics, rreq_data_fetch_latency_us, fetch_latency_us); | ||
|
|
@@ -1253,9 +1266,9 @@ void RaftReplDev::fetch_data_from_remote(std::vector< repl_req_ptr_t > rreqs) { | |
| // if we are here, it means the original who sent the log entries are down. | ||
| // we need to handle error and when the other member becomes leader, it will resend the log entries; | ||
| RD_LOGE(NO_TRACE_ID, | ||
| "Not able to fetching data from originator={}, error={}, probably originator is down. Will " | ||
| "retry when new leader start appending log entries", | ||
| rreqs.front()->remote_blkid().server_id, response.error()); | ||
| "Not able to fetching data from peer={}, error={}, probably leader is down. Will retry when " | ||
| "new leader start appending log entries", | ||
| fetch_from_peer_id, response.error()); | ||
| for (auto const& rreq : rreqs) { | ||
| // TODO: Set the data_received promise with error, so that waiting threads can be unblocked and | ||
| // reject the request. Without that, it will timeout and then reject it. | ||
|
|
@@ -1313,17 +1326,19 @@ void RaftReplDev::on_fetch_data_received(intrusive< sisl::GenericRpcData >& rpc_ | |
| // accumulate the sgs for later use (send back to the requester)); | ||
| sgs_vec.push_back(sgs); | ||
|
|
||
| if (originator != server_id()) { | ||
| const auto is_originator = (originator == server_id()); | ||
|
|
||
| if (is_originator) { | ||
| RD_LOGD(NO_TRACE_ID, "Data Channel: FetchData received: dsn={} lsn={}", req->dsn(), lsn); | ||
| } else { | ||
| RD_LOGD(NO_TRACE_ID, "non-originator FetchData received: dsn={} lsn={} originator={}, my_server_id={}", | ||
| req->dsn(), lsn, originator, server_id()); | ||
| } else { | ||
| RD_LOGT(NO_TRACE_ID, "Data Channel: FetchData received: dsn={} lsn={}", req->dsn(), lsn); | ||
| } | ||
|
|
||
| auto const& header = req->user_header(); | ||
| sisl::blob user_header = sisl::blob{header->Data(), header->size()}; | ||
| RD_LOGT(NO_TRACE_ID, "Data Channel: FetchData handled, my_blkid={}", local_blkid.to_string()); | ||
| futs.emplace_back(std::move(m_listener->on_fetch_data(lsn, user_header, local_blkid, sgs))); | ||
| futs.emplace_back(std::move(m_listener->on_fetch_data(lsn, user_header, local_blkid, sgs, is_originator))); | ||
| } | ||
|
|
||
| folly::collectAllUnsafe(futs).thenValue( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -234,6 +234,10 @@ class RaftReplDev : public ReplDev, | |
| std::atomic_uint64_t m_pending_init_req_num; | ||
| std::atomic< bool > m_in_quience; | ||
|
|
||
| // TODO::remove this after we change raft_repl_dev UT to support fetching data from non-originitor | ||
| inline static bool m_fetch_data_only_from_originator = | ||
| false; // If true, fetch data only from originator, not from other peers | ||
|
|
||
| public: | ||
| friend class RaftStateMachine; | ||
|
|
||
|
|
@@ -434,6 +438,10 @@ class RaftReplDev : public ReplDev, | |
| // clear reqs that has allocated blks on the given chunk. | ||
| void clear_chunk_req(chunk_num_t chunk_id); | ||
|
|
||
| static void enable_fetch_data_only_from_originator(bool enable) { m_fetch_data_only_from_originator = enable; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggest add It will be cleaner. |
||
|
|
||
| static bool is_fetch_data_only_from_originator() { return m_fetch_data_only_from_originator; } | ||
|
|
||
| protected: | ||
| //////////////// All nuraft::state_mgr overrides /////////////////////// | ||
| nuraft::ptr< nuraft::cluster_config > load_config() override; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fetch data might fail and then generate some garbage here if the selected peer doesn't have data in the following case, but anyway, raft will retry:
T1: leader push blob=100 to F1 and F2
T2: F1 and F2 cannot alloc blk because the related shard not committed
T3: F1 append log, fetch data from F2
T4: F2 append log, fetch data from F1
then F1 and F2 will failed to get data, waiting raft retry and select the leader
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks this is a good point.
Maybe we should only fetch data when we are in resync mode ? in that case (only consider 3 copies) leader and another follower both committed.
Lets track this and verify in SH testing, and decide based on metrics,