-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
pd: validate PD list #1201
pd: validate PD list #1201
Conversation
|
||
/// `validate_pd_list` validates PD list, it vists PDs members-API one by one. | ||
/// It returns Ok(()), only when all results are equal. | ||
pub fn validate_pd_list(endpoints: Vec<String>) -> Result<(), String> { |
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.
Can it be checked in try_connect
of PdClientCore?
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.
Yes, it can. Maybe it should be validated in try_connect
too?
71cb499
to
b1060b8
Compare
@huachaohuang |
let mut stream = try!(rpc_connect(ep.as_str())); | ||
let mut req = new_request(cluster_id, CommandType::GetPDMembers); | ||
req.set_get_pd_members(GetPDMembersRequest::new()); | ||
let (id, mut resp) = try!(send_msg(&mut stream, VALIDATION_MSG_ID, &req)); |
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.
What if some nodes are down? For example, 1, 2, 3 are still online, but 4, 5 are down, then the validate_and_connect may not succeed.
} | ||
} | ||
|
||
Err(box_err!("failed to connect to {:?}", hosts)) | ||
// Check all fields. | ||
let sample = members_resps.pop().unwrap(); |
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.
Is it safe to unwrap here?
bed7e3e
to
4b70a2c
Compare
PTAL @siddontang @BusyJay |
} | ||
|
||
let len = endpoints.len(); | ||
let mut endpoints_set = HashSet::with_capacity(len); |
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.
you can use sort + dedup to check duplicated urls.
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.
I think hashset is ok here.
use super::metrics::*; | ||
|
||
const MAX_PD_SEND_RETRY_COUNT: usize = 100; | ||
const SOCKET_READ_TIMEOUT: u64 = 3; | ||
const SOCKET_WRITE_TIMEOUT: u64 = 3; | ||
|
||
const PD_RPC_PREFIX: &'static str = "/pd/rpc"; | ||
const MSG_ID_VALIDATE: u64 = 0; |
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.
ID_VALIDATE is not proper here. You use it not for msg id, but for cluster id, and it is only used in GetPDMembers.
Maybe NONE_CLUSTER_ID is better here.
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.
oh, my fault. You use it both for cluster and msg.
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.
so you should use another cluster var for cluster id.
Btw, the ID here is not INVALID
.
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.
Oops, my bad, it is really confusing.
} | ||
} | ||
|
||
Ok(()) |
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.
we must check all PDs have same cluster ID too.
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.
Btw, I think checking cluster ID is enough.
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.
Checking cluster ID should be enough, but additional checks prevent ID collision.
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.
The collision probability is too small, we must start two pd clusters at same time and with the same random number.
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.
First of all, I agree the probability is small.
// Generate a random cluster ID.
ts := uint64(time.Now().Unix())
clusterID := (ts << 32) + uint64(rand.Uint32())
Above is how PD generates it's cluster id.
Actually, we will always get the same random number, because PD does not seed the global random source.
So we may get collision if PDs are started at the same second!
Play the clusterID demo at https://play.golang.org/p/vCMUxx9q0E
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.
Maybe we can use time + hash(initial-cluster) ?
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.
@siddontang Good idea!
Another approach is:
// Generate a random cluster ID.
rand.Seed(time.Now().UnixNano())
ts := uint64(time.Now().Unix())
clusterID := (ts << 32) + uint64(rand.Uint32())
PTAL @huachaohuang |
} | ||
} | ||
|
||
Ok(()) |
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.
Seems if all endpoints failed, this will just return OK?
.filter(|s| !s.is_empty()) | ||
.collect(); | ||
|
||
try!(validate_endpoints(&endpoints)); |
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.
We need to add some retries here, see line 231.
If PD and TiKV start at the same time, TiKV may fail if PD is not ready yet.
LGTM |
Ok(id) => { | ||
client.cluster_id = id; | ||
return Ok(client); | ||
cluster_id = id; |
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.
break the loop
// Check cluster ID. | ||
let cid = resp.take_header().get_cluster_id(); | ||
if let Some(sample) = cluster_id { | ||
if sample != cid { |
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.
any test to cover cluster mismatched?
} | ||
|
||
// Check all fields. | ||
let mut members = resp.take_get_pd_members().take_members().into_vec(); |
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.
I am confused that why do we need to check this. Is it necessary?
Or you can give some test for it and let me how this works.
Ping @overvenus |
e0ab476
to
f96117a
Compare
PTAL @siddontang @BusyJay |
}; | ||
let endpoints = [endpoints_1, endpoints_2]; | ||
|
||
assert_eq!(RpcClient::validate_endpoints(&endpoints).is_err(), true); |
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.
assert!(.is_err())
@huachaohuang I suggest mocking a PD server, so we don't need to start the real PD in CI. But I think we can use real PD now. |
PTAL @huachaohuang Rest LGTM |
CI failed @overvenus |
* src/storage: scheduler set current region for blacklist Signed-off-by: Evan Zhou <[email protected]> * set region id before async_write Signed-off-by: Evan Zhou <[email protected]> --------- Signed-off-by: Evan Zhou <[email protected]>
validate_pd_list
only accepts PD endpoints that are in the same PD cluster. It treats a removed PD as an invalied PD node.It only validates once at the beginning of raftkv startup.It validates the list every time when the tikv trys to connect a PD.Let's say there are two PD clusters, one consists three nodes, and the other is a standlone node. Details:
Following are cases tested so far:
Explanation:
tuple.0
is the command line argument--pd
tuple.1
is the result ofvalidate_pd_list
PTAL @siddontang @BusyJay
Close #1186