diff --git a/examples/single_mem_node/main.rs b/examples/single_mem_node/main.rs index cae1ec80f..a22836626 100644 --- a/examples/single_mem_node/main.rs +++ b/examples/single_mem_node/main.rs @@ -21,7 +21,7 @@ use std::time::{Duration, Instant}; use raft::prelude::*; use raft::storage::MemStorage; -type ProposeCallback = Box; +type ProposeCallback = Box; enum Msg { Propose { diff --git a/generate-proto.sh b/generate-proto.sh index ebe10c8cc..f518367a9 100755 --- a/generate-proto.sh +++ b/generate-proto.sh @@ -7,6 +7,8 @@ with open("src/eraftpb.rs") as reader: src = reader.read() res = re.sub('::protobuf::rt::read_proto3_enum_with_unknown_fields_into\(([^,]+), ([^,]+), &mut ([^,]+), [^\)]+\)\?', 'if \\\\1 == ::protobuf::wire_format::WireTypeVarint {\\\\3 = \\\\2.read_enum()?;} else { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); }', src) +res = re.sub('#!\[allow\(clippy\)\]', '#![allow(clippy::all)]', res) +res = re.sub('#!\[allow\(unused_results\)\]', '#![allow(unused_results, bare_trait_objects)]', res) with open("src/eraftpb.rs", "w") as writer: writer.write(res) diff --git a/proto/eraftpb.proto b/proto/eraftpb.proto index e7c25f50c..858bb8ee6 100644 --- a/proto/eraftpb.proto +++ b/proto/eraftpb.proto @@ -71,6 +71,7 @@ message Message { repeated Entry entries = 7; uint64 commit = 8; Snapshot snapshot = 9; + uint64 request_snapshot = 13; bool reject = 10; uint64 reject_hint = 11; bytes context = 12; diff --git a/src/eraftpb.rs b/src/eraftpb.rs index 18bae7dfa..badbe2a9f 100644 --- a/src/eraftpb.rs +++ b/src/eraftpb.rs @@ -1,9 +1,9 @@ -// This file is generated by rust-protobuf 2.0.2. Do not edit +// This file is generated by rust-protobuf 2.0.4. Do not edit // @generated // https://github.com/Manishearth/rust-clippy/issues/702 #![allow(unknown_lints)] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::all))] +#![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] @@ -16,7 +16,7 @@ #![allow(trivial_casts)] #![allow(unsafe_code)] #![allow(unused_imports)] -#![allow(unused_results)] +#![allow(unused_results, bare_trait_objects)] use protobuf::Message as Message_imported_for_functions; use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions; @@ -842,6 +842,7 @@ pub struct Message { pub entries: ::protobuf::RepeatedField, pub commit: u64, pub snapshot: ::protobuf::SingularPtrField, + pub request_snapshot: u64, pub reject: bool, pub reject_hint: u64, pub context: ::std::vec::Vec, @@ -1018,6 +1019,21 @@ impl Message { self.snapshot.as_ref().unwrap_or_else(|| Snapshot::default_instance()) } + // uint64 request_snapshot = 13; + + pub fn clear_request_snapshot(&mut self) { + self.request_snapshot = 0; + } + + // Param is passed by value, moved + pub fn set_request_snapshot(&mut self, v: u64) { + self.request_snapshot = v; + } + + pub fn get_request_snapshot(&self) -> u64 { + self.request_snapshot + } + // bool reject = 10; pub fn clear_reject(&mut self) { @@ -1145,6 +1161,13 @@ impl ::protobuf::Message for Message { 9 => { ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.snapshot)?; }, + 13 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_uint64()?; + self.request_snapshot = tmp; + }, 10 => { if wire_type != ::protobuf::wire_format::WireTypeVarint { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); @@ -1203,6 +1226,9 @@ impl ::protobuf::Message for Message { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } + if self.request_snapshot != 0 { + my_size += ::protobuf::rt::value_size(13, self.request_snapshot, ::protobuf::wire_format::WireTypeVarint); + } if self.reject != false { my_size += 2; } @@ -1249,6 +1275,9 @@ impl ::protobuf::Message for Message { os.write_raw_varint32(v.get_cached_size())?; v.write_to_with_cached_sizes(os)?; } + if self.request_snapshot != 0 { + os.write_uint64(13, self.request_snapshot)?; + } if self.reject != false { os.write_bool(10, self.reject)?; } @@ -1345,6 +1374,11 @@ impl ::protobuf::Message for Message { |m: &Message| { &m.snapshot }, |m: &mut Message| { &mut m.snapshot }, )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeUint64>( + "request_snapshot", + |m: &Message| { &m.request_snapshot }, + |m: &mut Message| { &mut m.request_snapshot }, + )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBool>( "reject", |m: &Message| { &m.reject }, @@ -1391,6 +1425,7 @@ impl ::protobuf::Clear for Message { self.clear_entries(); self.clear_commit(); self.clear_snapshot(); + self.clear_request_snapshot(); self.clear_reject(); self.clear_reject_hint(); self.clear_context(); @@ -2333,27 +2368,28 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x05index\x18\x02\x20\x01(\x04R\x05index\x12\x12\n\x04term\x18\x03\x20\ \x01(\x04R\x04term\"U\n\x08Snapshot\x12\x12\n\x04data\x18\x01\x20\x01(\ \x0cR\x04data\x125\n\x08metadata\x18\x02\x20\x01(\x0b2\x19.eraftpb.Snaps\ - hotMetadataR\x08metadata\"\xe7\x02\n\x07Message\x12/\n\x08msg_type\x18\ + hotMetadataR\x08metadata\"\x92\x03\n\x07Message\x12/\n\x08msg_type\x18\ \x01\x20\x01(\x0e2\x14.eraftpb.MessageTypeR\x07msgType\x12\x0e\n\x02to\ \x18\x02\x20\x01(\x04R\x02to\x12\x12\n\x04from\x18\x03\x20\x01(\x04R\x04\ from\x12\x12\n\x04term\x18\x04\x20\x01(\x04R\x04term\x12\x19\n\x08log_te\ rm\x18\x05\x20\x01(\x04R\x07logTerm\x12\x14\n\x05index\x18\x06\x20\x01(\ \x04R\x05index\x12(\n\x07entries\x18\x07\x20\x03(\x0b2\x0e.eraftpb.Entry\ R\x07entries\x12\x16\n\x06commit\x18\x08\x20\x01(\x04R\x06commit\x12-\n\ - \x08snapshot\x18\t\x20\x01(\x0b2\x11.eraftpb.SnapshotR\x08snapshot\x12\ - \x16\n\x06reject\x18\n\x20\x01(\x08R\x06reject\x12\x1f\n\x0breject_hint\ - \x18\x0b\x20\x01(\x04R\nrejectHint\x12\x18\n\x07context\x18\x0c\x20\x01(\ - \x0cR\x07context\"K\n\tHardState\x12\x12\n\x04term\x18\x01\x20\x01(\x04R\ - \x04term\x12\x12\n\x04vote\x18\x02\x20\x01(\x04R\x04vote\x12\x16\n\x06co\ - mmit\x18\x03\x20\x01(\x04R\x06commit\"=\n\tConfState\x12\x14\n\x05nodes\ - \x18\x01\x20\x03(\x04R\x05nodes\x12\x1a\n\x08learners\x18\x02\x20\x03(\ - \x04R\x08learners\"\x89\x01\n\nConfChange\x12\x0e\n\x02id\x18\x01\x20\ - \x01(\x04R\x02id\x128\n\x0bchange_type\x18\x02\x20\x01(\x0e2\x17.eraftpb\ - .ConfChangeTypeR\nchangeType\x12\x17\n\x07node_id\x18\x03\x20\x01(\x04R\ - \x06nodeId\x12\x18\n\x07context\x18\x04\x20\x01(\x0cR\x07context*1\n\tEn\ - tryType\x12\x0f\n\x0bEntryNormal\x10\0\x12\x13\n\x0fEntryConfChange\x10\ - \x01*\x8c\x03\n\x0bMessageType\x12\n\n\x06MsgHup\x10\0\x12\x0b\n\x07MsgB\ - eat\x10\x01\x12\x0e\n\nMsgPropose\x10\x02\x12\r\n\tMsgAppend\x10\x03\x12\ + \x08snapshot\x18\t\x20\x01(\x0b2\x11.eraftpb.SnapshotR\x08snapshot\x12)\ + \n\x10request_snapshot\x18\r\x20\x01(\x04R\x0frequestSnapshot\x12\x16\n\ + \x06reject\x18\n\x20\x01(\x08R\x06reject\x12\x1f\n\x0breject_hint\x18\ + \x0b\x20\x01(\x04R\nrejectHint\x12\x18\n\x07context\x18\x0c\x20\x01(\x0c\ + R\x07context\"K\n\tHardState\x12\x12\n\x04term\x18\x01\x20\x01(\x04R\x04\ + term\x12\x12\n\x04vote\x18\x02\x20\x01(\x04R\x04vote\x12\x16\n\x06commit\ + \x18\x03\x20\x01(\x04R\x06commit\"=\n\tConfState\x12\x14\n\x05nodes\x18\ + \x01\x20\x03(\x04R\x05nodes\x12\x1a\n\x08learners\x18\x02\x20\x03(\x04R\ + \x08learners\"\x89\x01\n\nConfChange\x12\x0e\n\x02id\x18\x01\x20\x01(\ + \x04R\x02id\x128\n\x0bchange_type\x18\x02\x20\x01(\x0e2\x17.eraftpb.Conf\ + ChangeTypeR\nchangeType\x12\x17\n\x07node_id\x18\x03\x20\x01(\x04R\x06no\ + deId\x12\x18\n\x07context\x18\x04\x20\x01(\x0cR\x07context*1\n\tEntryTyp\ + e\x12\x0f\n\x0bEntryNormal\x10\0\x12\x13\n\x0fEntryConfChange\x10\x01*\ + \x8c\x03\n\x0bMessageType\x12\n\n\x06MsgHup\x10\0\x12\x0b\n\x07MsgBeat\ + \x10\x01\x12\x0e\n\nMsgPropose\x10\x02\x12\r\n\tMsgAppend\x10\x03\x12\ \x15\n\x11MsgAppendResponse\x10\x04\x12\x12\n\x0eMsgRequestVote\x10\x05\ \x12\x1a\n\x16MsgRequestVoteResponse\x10\x06\x12\x0f\n\x0bMsgSnapshot\ \x10\x07\x12\x10\n\x0cMsgHeartbeat\x10\x08\x12\x18\n\x14MsgHeartbeatResp\ @@ -2363,176 +2399,192 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x0f\x12\x14\n\x10MsgReadIndexResp\x10\x10\x12\x15\n\x11MsgRequestPreVot\ e\x10\x11\x12\x1d\n\x19MsgRequestPreVoteResponse\x10\x12*A\n\x0eConfChan\ geType\x12\x0b\n\x07AddNode\x10\0\x12\x0e\n\nRemoveNode\x10\x01\x12\x12\ - \n\x0eAddLearnerNode\x10\x02J\x83\x1c\n\x06\x12\x04\0\0Z\x01\n\x08\n\x01\ + \n\x0eAddLearnerNode\x10\x02J\x9d!\n\x06\x12\x04\0\0e\x01\n\x08\n\x01\ \x0c\x12\x03\0\0\x12\n\x08\n\x01\x02\x12\x03\x01\x08\x0f\n\n\n\x02\x05\0\ \x12\x04\x03\0\x06\x01\n\n\n\x03\x05\0\x01\x12\x03\x03\x05\x0e\n\x0b\n\ \x04\x05\0\x02\0\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\ \x04\x04\x0f\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x04\x12\x13\n\x0b\n\x04\ \x05\0\x02\x01\x12\x03\x05\x04\x18\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\ - \x05\x04\x13\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x05\x16\x17\n\n\n\x02\ - \x04\0\x12\x04\x08\0\x12\x01\n\n\n\x03\x04\0\x01\x12\x03\x08\x08\r\n\x0b\ - \n\x04\x04\0\x02\0\x12\x03\t\x04\x1d\n\r\n\x05\x04\0\x02\0\x04\x12\x04\t\ - \x04\x08\x0f\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\t\x04\r\n\x0c\n\x05\x04\ - \0\x02\0\x01\x12\x03\t\x0e\x18\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\t\x1b\ - \x1c\n\x0b\n\x04\x04\0\x02\x01\x12\x03\n\x04\x14\n\r\n\x05\x04\0\x02\x01\ - \x04\x12\x04\n\x04\t\x1d\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\n\x04\n\n\ - \x0c\n\x05\x04\0\x02\x01\x01\x12\x03\n\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\ - \x03\x12\x03\n\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x0b\x04\x15\n\r\ - \n\x05\x04\0\x02\x02\x04\x12\x04\x0b\x04\n\x14\n\x0c\n\x05\x04\0\x02\x02\ - \x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x0b\x0b\x10\ - \n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x0b\x13\x14\n\x0b\n\x04\x04\0\x02\ - \x03\x12\x03\x0c\x04\x13\n\r\n\x05\x04\0\x02\x03\x04\x12\x04\x0c\x04\x0b\ - \x15\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x0c\x04\t\n\x0c\n\x05\x04\0\ - \x02\x03\x01\x12\x03\x0c\n\x0e\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x0c\ - \x11\x12\n\x0b\n\x04\x04\0\x02\x04\x12\x03\r\x04\x16\n\r\n\x05\x04\0\x02\ - \x04\x04\x12\x04\r\x04\x0c\x13\n\x0c\n\x05\x04\0\x02\x04\x05\x12\x03\r\ - \x04\t\n\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\r\n\x11\n\x0c\n\x05\x04\0\ - \x02\x04\x03\x12\x03\r\x14\x15\nm\n\x04\x04\0\x02\x05\x12\x03\x11\x04\ - \x16\x1a`\x20Deprecated!\x20It\x20is\x20kept\x20for\x20backward\x20compa\ - tibility.\n\x20TODO:\x20remove\x20it\x20in\x20the\x20next\x20major\x20re\ - lease.\n\n\r\n\x05\x04\0\x02\x05\x04\x12\x04\x11\x04\r\x16\n\x0c\n\x05\ - \x04\0\x02\x05\x05\x12\x03\x11\x04\x08\n\x0c\n\x05\x04\0\x02\x05\x01\x12\ - \x03\x11\t\x11\n\x0c\n\x05\x04\0\x02\x05\x03\x12\x03\x11\x14\x15\n\n\n\ - \x02\x04\x01\x12\x04\x14\0\x18\x01\n\n\n\x03\x04\x01\x01\x12\x03\x14\x08\ - \x18\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x15\x04\x1d\n\r\n\x05\x04\x01\x02\ - \0\x04\x12\x04\x15\x04\x14\x1a\n\x0c\n\x05\x04\x01\x02\0\x06\x12\x03\x15\ - \x04\r\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x15\x0e\x18\n\x0c\n\x05\x04\ - \x01\x02\0\x03\x12\x03\x15\x1b\x1c\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\ - \x16\x04\x15\n\r\n\x05\x04\x01\x02\x01\x04\x12\x04\x16\x04\x15\x1d\n\x0c\ - \n\x05\x04\x01\x02\x01\x05\x12\x03\x16\x04\n\n\x0c\n\x05\x04\x01\x02\x01\ - \x01\x12\x03\x16\x0b\x10\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x16\x13\ - \x14\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x17\x04\x14\n\r\n\x05\x04\x01\ - \x02\x02\x04\x12\x04\x17\x04\x16\x15\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\ - \x03\x17\x04\n\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x17\x0b\x0f\n\x0c\ - \n\x05\x04\x01\x02\x02\x03\x12\x03\x17\x12\x13\n\n\n\x02\x04\x02\x12\x04\ - \x1a\0\x1d\x01\n\n\n\x03\x04\x02\x01\x12\x03\x1a\x08\x10\n\x0b\n\x04\x04\ - \x02\x02\0\x12\x03\x1b\x04\x13\n\r\n\x05\x04\x02\x02\0\x04\x12\x04\x1b\ - \x04\x1a\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x1b\x04\t\n\x0c\n\x05\ - \x04\x02\x02\0\x01\x12\x03\x1b\n\x0e\n\x0c\n\x05\x04\x02\x02\0\x03\x12\ - \x03\x1b\x11\x12\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x1c\x04\"\n\r\n\x05\ - \x04\x02\x02\x01\x04\x12\x04\x1c\x04\x1b\x13\n\x0c\n\x05\x04\x02\x02\x01\ - \x06\x12\x03\x1c\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x1c\x15\ - \x1d\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x1c\x20!\n\n\n\x02\x05\x01\ - \x12\x04\x1f\03\x01\n\n\n\x03\x05\x01\x01\x12\x03\x1f\x05\x10\n\x0b\n\ - \x04\x05\x01\x02\0\x12\x03\x20\x04\x0f\n\x0c\n\x05\x05\x01\x02\0\x01\x12\ - \x03\x20\x04\n\n\x0c\n\x05\x05\x01\x02\0\x02\x12\x03\x20\r\x0e\n\x0b\n\ - \x04\x05\x01\x02\x01\x12\x03!\x04\x10\n\x0c\n\x05\x05\x01\x02\x01\x01\ - \x12\x03!\x04\x0b\n\x0c\n\x05\x05\x01\x02\x01\x02\x12\x03!\x0e\x0f\n\x0b\ - \n\x04\x05\x01\x02\x02\x12\x03\"\x04\x13\n\x0c\n\x05\x05\x01\x02\x02\x01\ - \x12\x03\"\x04\x0e\n\x0c\n\x05\x05\x01\x02\x02\x02\x12\x03\"\x11\x12\n\ - \x0b\n\x04\x05\x01\x02\x03\x12\x03#\x04\x12\n\x0c\n\x05\x05\x01\x02\x03\ - \x01\x12\x03#\x04\r\n\x0c\n\x05\x05\x01\x02\x03\x02\x12\x03#\x10\x11\n\ - \x0b\n\x04\x05\x01\x02\x04\x12\x03$\x04\x1a\n\x0c\n\x05\x05\x01\x02\x04\ - \x01\x12\x03$\x04\x15\n\x0c\n\x05\x05\x01\x02\x04\x02\x12\x03$\x18\x19\n\ - \x0b\n\x04\x05\x01\x02\x05\x12\x03%\x04\x17\n\x0c\n\x05\x05\x01\x02\x05\ - \x01\x12\x03%\x04\x12\n\x0c\n\x05\x05\x01\x02\x05\x02\x12\x03%\x15\x16\n\ - \x0b\n\x04\x05\x01\x02\x06\x12\x03&\x04\x1f\n\x0c\n\x05\x05\x01\x02\x06\ - \x01\x12\x03&\x04\x1a\n\x0c\n\x05\x05\x01\x02\x06\x02\x12\x03&\x1d\x1e\n\ - \x0b\n\x04\x05\x01\x02\x07\x12\x03'\x04\x14\n\x0c\n\x05\x05\x01\x02\x07\ - \x01\x12\x03'\x04\x0f\n\x0c\n\x05\x05\x01\x02\x07\x02\x12\x03'\x12\x13\n\ - \x0b\n\x04\x05\x01\x02\x08\x12\x03(\x04\x15\n\x0c\n\x05\x05\x01\x02\x08\ - \x01\x12\x03(\x04\x10\n\x0c\n\x05\x05\x01\x02\x08\x02\x12\x03(\x13\x14\n\ - \x0b\n\x04\x05\x01\x02\t\x12\x03)\x04\x1d\n\x0c\n\x05\x05\x01\x02\t\x01\ - \x12\x03)\x04\x18\n\x0c\n\x05\x05\x01\x02\t\x02\x12\x03)\x1b\x1c\n\x0b\n\ - \x04\x05\x01\x02\n\x12\x03*\x04\x18\n\x0c\n\x05\x05\x01\x02\n\x01\x12\ - \x03*\x04\x12\n\x0c\n\x05\x05\x01\x02\n\x02\x12\x03*\x15\x17\n\x0b\n\x04\ - \x05\x01\x02\x0b\x12\x03+\x04\x17\n\x0c\n\x05\x05\x01\x02\x0b\x01\x12\ - \x03+\x04\x11\n\x0c\n\x05\x05\x01\x02\x0b\x02\x12\x03+\x14\x16\n\x0b\n\ - \x04\x05\x01\x02\x0c\x12\x03,\x04\x18\n\x0c\n\x05\x05\x01\x02\x0c\x01\ - \x12\x03,\x04\x12\n\x0c\n\x05\x05\x01\x02\x0c\x02\x12\x03,\x15\x17\n\x0b\ - \n\x04\x05\x01\x02\r\x12\x03-\x04\x1b\n\x0c\n\x05\x05\x01\x02\r\x01\x12\ - \x03-\x04\x15\n\x0c\n\x05\x05\x01\x02\r\x02\x12\x03-\x18\x1a\n\x0b\n\x04\ - \x05\x01\x02\x0e\x12\x03.\x04\x17\n\x0c\n\x05\x05\x01\x02\x0e\x01\x12\ - \x03.\x04\x11\n\x0c\n\x05\x05\x01\x02\x0e\x02\x12\x03.\x14\x16\n\x0b\n\ - \x04\x05\x01\x02\x0f\x12\x03/\x04\x16\n\x0c\n\x05\x05\x01\x02\x0f\x01\ - \x12\x03/\x04\x10\n\x0c\n\x05\x05\x01\x02\x0f\x02\x12\x03/\x13\x15\n\x0b\ - \n\x04\x05\x01\x02\x10\x12\x030\x04\x1a\n\x0c\n\x05\x05\x01\x02\x10\x01\ - \x12\x030\x04\x14\n\x0c\n\x05\x05\x01\x02\x10\x02\x12\x030\x17\x19\n\x0b\ - \n\x04\x05\x01\x02\x11\x12\x031\x04\x1b\n\x0c\n\x05\x05\x01\x02\x11\x01\ - \x12\x031\x04\x15\n\x0c\n\x05\x05\x01\x02\x11\x02\x12\x031\x18\x1a\n\x0b\ - \n\x04\x05\x01\x02\x12\x12\x032\x04#\n\x0c\n\x05\x05\x01\x02\x12\x01\x12\ - \x032\x04\x1d\n\x0c\n\x05\x05\x01\x02\x12\x02\x12\x032\x20\"\n\n\n\x02\ - \x04\x03\x12\x045\0B\x01\n\n\n\x03\x04\x03\x01\x12\x035\x08\x0f\n\x0b\n\ - \x04\x04\x03\x02\0\x12\x036\x04\x1d\n\r\n\x05\x04\x03\x02\0\x04\x12\x046\ - \x045\x11\n\x0c\n\x05\x04\x03\x02\0\x06\x12\x036\x04\x0f\n\x0c\n\x05\x04\ - \x03\x02\0\x01\x12\x036\x10\x18\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x036\ - \x1b\x1c\n\x0b\n\x04\x04\x03\x02\x01\x12\x037\x04\x12\n\r\n\x05\x04\x03\ - \x02\x01\x04\x12\x047\x046\x1d\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x037\ - \x04\n\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x037\x0b\r\n\x0c\n\x05\x04\ - \x03\x02\x01\x03\x12\x037\x10\x11\n\x0b\n\x04\x04\x03\x02\x02\x12\x038\ - \x04\x14\n\r\n\x05\x04\x03\x02\x02\x04\x12\x048\x047\x12\n\x0c\n\x05\x04\ - \x03\x02\x02\x05\x12\x038\x04\n\n\x0c\n\x05\x04\x03\x02\x02\x01\x12\x038\ - \x0b\x0f\n\x0c\n\x05\x04\x03\x02\x02\x03\x12\x038\x12\x13\n\x0b\n\x04\ - \x04\x03\x02\x03\x12\x039\x04\x14\n\r\n\x05\x04\x03\x02\x03\x04\x12\x049\ - \x048\x14\n\x0c\n\x05\x04\x03\x02\x03\x05\x12\x039\x04\n\n\x0c\n\x05\x04\ - \x03\x02\x03\x01\x12\x039\x0b\x0f\n\x0c\n\x05\x04\x03\x02\x03\x03\x12\ - \x039\x12\x13\n\x0b\n\x04\x04\x03\x02\x04\x12\x03:\x04\x18\n\r\n\x05\x04\ - \x03\x02\x04\x04\x12\x04:\x049\x14\n\x0c\n\x05\x04\x03\x02\x04\x05\x12\ - \x03:\x04\n\n\x0c\n\x05\x04\x03\x02\x04\x01\x12\x03:\x0b\x13\n\x0c\n\x05\ - \x04\x03\x02\x04\x03\x12\x03:\x16\x17\n\x0b\n\x04\x04\x03\x02\x05\x12\ - \x03;\x04\x15\n\r\n\x05\x04\x03\x02\x05\x04\x12\x04;\x04:\x18\n\x0c\n\ - \x05\x04\x03\x02\x05\x05\x12\x03;\x04\n\n\x0c\n\x05\x04\x03\x02\x05\x01\ - \x12\x03;\x0b\x10\n\x0c\n\x05\x04\x03\x02\x05\x03\x12\x03;\x13\x14\n\x0b\ - \n\x04\x04\x03\x02\x06\x12\x03<\x04\x1f\n\x0c\n\x05\x04\x03\x02\x06\x04\ - \x12\x03<\x04\x0c\n\x0c\n\x05\x04\x03\x02\x06\x06\x12\x03<\r\x12\n\x0c\n\ - \x05\x04\x03\x02\x06\x01\x12\x03<\x13\x1a\n\x0c\n\x05\x04\x03\x02\x06\ - \x03\x12\x03<\x1d\x1e\n\x0b\n\x04\x04\x03\x02\x07\x12\x03=\x04\x16\n\r\n\ - \x05\x04\x03\x02\x07\x04\x12\x04=\x04<\x1f\n\x0c\n\x05\x04\x03\x02\x07\ - \x05\x12\x03=\x04\n\n\x0c\n\x05\x04\x03\x02\x07\x01\x12\x03=\x0b\x11\n\ - \x0c\n\x05\x04\x03\x02\x07\x03\x12\x03=\x14\x15\n\x0b\n\x04\x04\x03\x02\ - \x08\x12\x03>\x04\x1a\n\r\n\x05\x04\x03\x02\x08\x04\x12\x04>\x04=\x16\n\ - \x0c\n\x05\x04\x03\x02\x08\x06\x12\x03>\x04\x0c\n\x0c\n\x05\x04\x03\x02\ - \x08\x01\x12\x03>\r\x15\n\x0c\n\x05\x04\x03\x02\x08\x03\x12\x03>\x18\x19\ - \n\x0b\n\x04\x04\x03\x02\t\x12\x03?\x04\x15\n\r\n\x05\x04\x03\x02\t\x04\ - \x12\x04?\x04>\x1a\n\x0c\n\x05\x04\x03\x02\t\x05\x12\x03?\x04\x08\n\x0c\ - \n\x05\x04\x03\x02\t\x01\x12\x03?\t\x0f\n\x0c\n\x05\x04\x03\x02\t\x03\ - \x12\x03?\x12\x14\n\x0b\n\x04\x04\x03\x02\n\x12\x03@\x04\x1c\n\r\n\x05\ - \x04\x03\x02\n\x04\x12\x04@\x04?\x15\n\x0c\n\x05\x04\x03\x02\n\x05\x12\ - \x03@\x04\n\n\x0c\n\x05\x04\x03\x02\n\x01\x12\x03@\x0b\x16\n\x0c\n\x05\ - \x04\x03\x02\n\x03\x12\x03@\x19\x1b\n\x0b\n\x04\x04\x03\x02\x0b\x12\x03A\ - \x04\x17\n\r\n\x05\x04\x03\x02\x0b\x04\x12\x04A\x04@\x1c\n\x0c\n\x05\x04\ - \x03\x02\x0b\x05\x12\x03A\x04\t\n\x0c\n\x05\x04\x03\x02\x0b\x01\x12\x03A\ - \n\x11\n\x0c\n\x05\x04\x03\x02\x0b\x03\x12\x03A\x14\x16\n\n\n\x02\x04\ - \x04\x12\x04D\0H\x01\n\n\n\x03\x04\x04\x01\x12\x03D\x08\x11\n\x0b\n\x04\ - \x04\x04\x02\0\x12\x03E\x04\x14\n\r\n\x05\x04\x04\x02\0\x04\x12\x04E\x04\ - D\x13\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03E\x04\n\n\x0c\n\x05\x04\x04\ - \x02\0\x01\x12\x03E\x0b\x0f\n\x0c\n\x05\x04\x04\x02\0\x03\x12\x03E\x12\ - \x13\n\x0b\n\x04\x04\x04\x02\x01\x12\x03F\x04\x14\n\r\n\x05\x04\x04\x02\ - \x01\x04\x12\x04F\x04E\x14\n\x0c\n\x05\x04\x04\x02\x01\x05\x12\x03F\x04\ - \n\n\x0c\n\x05\x04\x04\x02\x01\x01\x12\x03F\x0b\x0f\n\x0c\n\x05\x04\x04\ - \x02\x01\x03\x12\x03F\x12\x13\n\x0b\n\x04\x04\x04\x02\x02\x12\x03G\x04\ - \x16\n\r\n\x05\x04\x04\x02\x02\x04\x12\x04G\x04F\x14\n\x0c\n\x05\x04\x04\ - \x02\x02\x05\x12\x03G\x04\n\n\x0c\n\x05\x04\x04\x02\x02\x01\x12\x03G\x0b\ - \x11\n\x0c\n\x05\x04\x04\x02\x02\x03\x12\x03G\x14\x15\n\n\n\x02\x04\x05\ - \x12\x04J\0M\x01\n\n\n\x03\x04\x05\x01\x12\x03J\x08\x11\n\x0b\n\x04\x04\ - \x05\x02\0\x12\x03K\x04\x1e\n\x0c\n\x05\x04\x05\x02\0\x04\x12\x03K\x04\ - \x0c\n\x0c\n\x05\x04\x05\x02\0\x05\x12\x03K\r\x13\n\x0c\n\x05\x04\x05\ - \x02\0\x01\x12\x03K\x14\x19\n\x0c\n\x05\x04\x05\x02\0\x03\x12\x03K\x1c\ - \x1d\n\x0b\n\x04\x04\x05\x02\x01\x12\x03L\x04!\n\x0c\n\x05\x04\x05\x02\ - \x01\x04\x12\x03L\x04\x0c\n\x0c\n\x05\x04\x05\x02\x01\x05\x12\x03L\r\x13\ - \n\x0c\n\x05\x04\x05\x02\x01\x01\x12\x03L\x14\x1c\n\x0c\n\x05\x04\x05\ - \x02\x01\x03\x12\x03L\x1f\x20\n\n\n\x02\x05\x02\x12\x04O\0S\x01\n\n\n\ - \x03\x05\x02\x01\x12\x03O\x05\x13\n\x0b\n\x04\x05\x02\x02\0\x12\x03P\x04\ - \x13\n\x0c\n\x05\x05\x02\x02\0\x01\x12\x03P\x04\x0b\n\x0c\n\x05\x05\x02\ - \x02\0\x02\x12\x03P\x11\x12\n\x0b\n\x04\x05\x02\x02\x01\x12\x03Q\x04\x13\ - \n\x0c\n\x05\x05\x02\x02\x01\x01\x12\x03Q\x04\x0e\n\x0c\n\x05\x05\x02\ - \x02\x01\x02\x12\x03Q\x11\x12\n\x0b\n\x04\x05\x02\x02\x02\x12\x03R\x04\ - \x17\n\x0c\n\x05\x05\x02\x02\x02\x01\x12\x03R\x04\x12\n\x0c\n\x05\x05\ - \x02\x02\x02\x02\x12\x03R\x15\x16\n\n\n\x02\x04\x06\x12\x04U\0Z\x01\n\n\ - \n\x03\x04\x06\x01\x12\x03U\x08\x12\n\x0b\n\x04\x04\x06\x02\0\x12\x03V\ - \x04\x12\n\r\n\x05\x04\x06\x02\0\x04\x12\x04V\x04U\x14\n\x0c\n\x05\x04\ - \x06\x02\0\x05\x12\x03V\x04\n\n\x0c\n\x05\x04\x06\x02\0\x01\x12\x03V\x0b\ - \r\n\x0c\n\x05\x04\x06\x02\0\x03\x12\x03V\x10\x11\n\x0b\n\x04\x04\x06\ - \x02\x01\x12\x03W\x04#\n\r\n\x05\x04\x06\x02\x01\x04\x12\x04W\x04V\x12\n\ - \x0c\n\x05\x04\x06\x02\x01\x06\x12\x03W\x04\x12\n\x0c\n\x05\x04\x06\x02\ - \x01\x01\x12\x03W\x13\x1e\n\x0c\n\x05\x04\x06\x02\x01\x03\x12\x03W!\"\n\ - \x0b\n\x04\x04\x06\x02\x02\x12\x03X\x04\x17\n\r\n\x05\x04\x06\x02\x02\ - \x04\x12\x04X\x04W#\n\x0c\n\x05\x04\x06\x02\x02\x05\x12\x03X\x04\n\n\x0c\ - \n\x05\x04\x06\x02\x02\x01\x12\x03X\x0b\x12\n\x0c\n\x05\x04\x06\x02\x02\ - \x03\x12\x03X\x15\x16\n\x0b\n\x04\x04\x06\x02\x03\x12\x03Y\x04\x16\n\r\n\ - \x05\x04\x06\x02\x03\x04\x12\x04Y\x04X\x17\n\x0c\n\x05\x04\x06\x02\x03\ - \x05\x12\x03Y\x04\t\n\x0c\n\x05\x04\x06\x02\x03\x01\x12\x03Y\n\x11\n\x0c\ - \n\x05\x04\x06\x02\x03\x03\x12\x03Y\x14\x15b\x06proto3\ + \x05\x04\x13\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x05\x16\x17\n\xdd\x04\ + \n\x02\x04\0\x12\x04\x12\0\x1c\x01\x1a\xd0\x04\x20The\x20entry\x20is\x20\ + a\x20type\x20of\x20change\x20that\x20needs\x20to\x20be\x20applied.\x20It\ + \x20contains\x20two\x20data\x20fields.\n\x20While\x20the\x20fields\x20ar\ + e\x20built\x20into\x20the\x20model;\x20their\x20usage\x20is\x20determine\ + d\x20by\x20the\x20entry_type.\n\n\x20For\x20normal\x20entries,\x20the\ + \x20data\x20field\x20should\x20contain\x20the\x20data\x20change\x20that\ + \x20should\x20be\x20applied.\n\x20The\x20context\x20field\x20can\x20be\ + \x20used\x20for\x20any\x20contextual\x20data\x20that\x20might\x20be\x20r\ + elevant\x20to\x20the\n\x20application\x20of\x20the\x20data.\n\n\x20For\ + \x20configuration\x20changes,\x20the\x20data\x20will\x20contain\x20the\ + \x20ConfChange\x20message\x20and\x20the\n\x20context\x20will\x20provide\ + \x20anything\x20needed\x20to\x20assist\x20the\x20configuration\x20change\ + .\x20The\x20context\n\x20if\x20for\x20the\x20user\x20to\x20set\x20and\ + \x20use\x20in\x20this\x20case.\n\n\n\n\x03\x04\0\x01\x12\x03\x12\x08\r\n\ + \x0b\n\x04\x04\0\x02\0\x12\x03\x13\x04\x1d\n\r\n\x05\x04\0\x02\0\x04\x12\ + \x04\x13\x04\x12\x0f\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x13\x04\r\n\x0c\ + \n\x05\x04\0\x02\0\x01\x12\x03\x13\x0e\x18\n\x0c\n\x05\x04\0\x02\0\x03\ + \x12\x03\x13\x1b\x1c\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x14\x04\x14\n\r\n\ + \x05\x04\0\x02\x01\x04\x12\x04\x14\x04\x13\x1d\n\x0c\n\x05\x04\0\x02\x01\ + \x05\x12\x03\x14\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x14\x0b\x0f\ + \n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x14\x12\x13\n\x0b\n\x04\x04\0\x02\ + \x02\x12\x03\x15\x04\x15\n\r\n\x05\x04\0\x02\x02\x04\x12\x04\x15\x04\x14\ + \x14\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x15\x04\n\n\x0c\n\x05\x04\0\ + \x02\x02\x01\x12\x03\x15\x0b\x10\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\ + \x15\x13\x14\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x16\x04\x13\n\r\n\x05\x04\ + \0\x02\x03\x04\x12\x04\x16\x04\x15\x15\n\x0c\n\x05\x04\0\x02\x03\x05\x12\ + \x03\x16\x04\t\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x16\n\x0e\n\x0c\n\ + \x05\x04\0\x02\x03\x03\x12\x03\x16\x11\x12\n\x0b\n\x04\x04\0\x02\x04\x12\ + \x03\x17\x04\x16\n\r\n\x05\x04\0\x02\x04\x04\x12\x04\x17\x04\x16\x13\n\ + \x0c\n\x05\x04\0\x02\x04\x05\x12\x03\x17\x04\t\n\x0c\n\x05\x04\0\x02\x04\ + \x01\x12\x03\x17\n\x11\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x17\x14\x15\ + \nm\n\x04\x04\0\x02\x05\x12\x03\x1b\x04\x16\x1a`\x20Deprecated!\x20It\ + \x20is\x20kept\x20for\x20backward\x20compatibility.\n\x20TODO:\x20remove\ + \x20it\x20in\x20the\x20next\x20major\x20release.\n\n\r\n\x05\x04\0\x02\ + \x05\x04\x12\x04\x1b\x04\x17\x16\n\x0c\n\x05\x04\0\x02\x05\x05\x12\x03\ + \x1b\x04\x08\n\x0c\n\x05\x04\0\x02\x05\x01\x12\x03\x1b\t\x11\n\x0c\n\x05\ + \x04\0\x02\x05\x03\x12\x03\x1b\x14\x15\n\n\n\x02\x04\x01\x12\x04\x1e\0\"\ + \x01\n\n\n\x03\x04\x01\x01\x12\x03\x1e\x08\x18\n\x0b\n\x04\x04\x01\x02\0\ + \x12\x03\x1f\x04\x1d\n\r\n\x05\x04\x01\x02\0\x04\x12\x04\x1f\x04\x1e\x1a\ + \n\x0c\n\x05\x04\x01\x02\0\x06\x12\x03\x1f\x04\r\n\x0c\n\x05\x04\x01\x02\ + \0\x01\x12\x03\x1f\x0e\x18\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x1f\x1b\ + \x1c\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x20\x04\x15\n\r\n\x05\x04\x01\ + \x02\x01\x04\x12\x04\x20\x04\x1f\x1d\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\ + \x03\x20\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x20\x0b\x10\n\x0c\ + \n\x05\x04\x01\x02\x01\x03\x12\x03\x20\x13\x14\n\x0b\n\x04\x04\x01\x02\ + \x02\x12\x03!\x04\x14\n\r\n\x05\x04\x01\x02\x02\x04\x12\x04!\x04\x20\x15\ + \n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03!\x04\n\n\x0c\n\x05\x04\x01\x02\ + \x02\x01\x12\x03!\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03!\x12\ + \x13\n\n\n\x02\x04\x02\x12\x04$\0'\x01\n\n\n\x03\x04\x02\x01\x12\x03$\ + \x08\x10\n\x0b\n\x04\x04\x02\x02\0\x12\x03%\x04\x13\n\r\n\x05\x04\x02\ + \x02\0\x04\x12\x04%\x04$\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03%\x04\ + \t\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03%\n\x0e\n\x0c\n\x05\x04\x02\x02\ + \0\x03\x12\x03%\x11\x12\n\x0b\n\x04\x04\x02\x02\x01\x12\x03&\x04\"\n\r\n\ + \x05\x04\x02\x02\x01\x04\x12\x04&\x04%\x13\n\x0c\n\x05\x04\x02\x02\x01\ + \x06\x12\x03&\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03&\x15\x1d\n\ + \x0c\n\x05\x04\x02\x02\x01\x03\x12\x03&\x20!\n\n\n\x02\x05\x01\x12\x04)\ + \0=\x01\n\n\n\x03\x05\x01\x01\x12\x03)\x05\x10\n\x0b\n\x04\x05\x01\x02\0\ + \x12\x03*\x04\x0f\n\x0c\n\x05\x05\x01\x02\0\x01\x12\x03*\x04\n\n\x0c\n\ + \x05\x05\x01\x02\0\x02\x12\x03*\r\x0e\n\x0b\n\x04\x05\x01\x02\x01\x12\ + \x03+\x04\x10\n\x0c\n\x05\x05\x01\x02\x01\x01\x12\x03+\x04\x0b\n\x0c\n\ + \x05\x05\x01\x02\x01\x02\x12\x03+\x0e\x0f\n\x0b\n\x04\x05\x01\x02\x02\ + \x12\x03,\x04\x13\n\x0c\n\x05\x05\x01\x02\x02\x01\x12\x03,\x04\x0e\n\x0c\ + \n\x05\x05\x01\x02\x02\x02\x12\x03,\x11\x12\n\x0b\n\x04\x05\x01\x02\x03\ + \x12\x03-\x04\x12\n\x0c\n\x05\x05\x01\x02\x03\x01\x12\x03-\x04\r\n\x0c\n\ + \x05\x05\x01\x02\x03\x02\x12\x03-\x10\x11\n\x0b\n\x04\x05\x01\x02\x04\ + \x12\x03.\x04\x1a\n\x0c\n\x05\x05\x01\x02\x04\x01\x12\x03.\x04\x15\n\x0c\ + \n\x05\x05\x01\x02\x04\x02\x12\x03.\x18\x19\n\x0b\n\x04\x05\x01\x02\x05\ + \x12\x03/\x04\x17\n\x0c\n\x05\x05\x01\x02\x05\x01\x12\x03/\x04\x12\n\x0c\ + \n\x05\x05\x01\x02\x05\x02\x12\x03/\x15\x16\n\x0b\n\x04\x05\x01\x02\x06\ + \x12\x030\x04\x1f\n\x0c\n\x05\x05\x01\x02\x06\x01\x12\x030\x04\x1a\n\x0c\ + \n\x05\x05\x01\x02\x06\x02\x12\x030\x1d\x1e\n\x0b\n\x04\x05\x01\x02\x07\ + \x12\x031\x04\x14\n\x0c\n\x05\x05\x01\x02\x07\x01\x12\x031\x04\x0f\n\x0c\ + \n\x05\x05\x01\x02\x07\x02\x12\x031\x12\x13\n\x0b\n\x04\x05\x01\x02\x08\ + \x12\x032\x04\x15\n\x0c\n\x05\x05\x01\x02\x08\x01\x12\x032\x04\x10\n\x0c\ + \n\x05\x05\x01\x02\x08\x02\x12\x032\x13\x14\n\x0b\n\x04\x05\x01\x02\t\ + \x12\x033\x04\x1d\n\x0c\n\x05\x05\x01\x02\t\x01\x12\x033\x04\x18\n\x0c\n\ + \x05\x05\x01\x02\t\x02\x12\x033\x1b\x1c\n\x0b\n\x04\x05\x01\x02\n\x12\ + \x034\x04\x18\n\x0c\n\x05\x05\x01\x02\n\x01\x12\x034\x04\x12\n\x0c\n\x05\ + \x05\x01\x02\n\x02\x12\x034\x15\x17\n\x0b\n\x04\x05\x01\x02\x0b\x12\x035\ + \x04\x17\n\x0c\n\x05\x05\x01\x02\x0b\x01\x12\x035\x04\x11\n\x0c\n\x05\ + \x05\x01\x02\x0b\x02\x12\x035\x14\x16\n\x0b\n\x04\x05\x01\x02\x0c\x12\ + \x036\x04\x18\n\x0c\n\x05\x05\x01\x02\x0c\x01\x12\x036\x04\x12\n\x0c\n\ + \x05\x05\x01\x02\x0c\x02\x12\x036\x15\x17\n\x0b\n\x04\x05\x01\x02\r\x12\ + \x037\x04\x1b\n\x0c\n\x05\x05\x01\x02\r\x01\x12\x037\x04\x15\n\x0c\n\x05\ + \x05\x01\x02\r\x02\x12\x037\x18\x1a\n\x0b\n\x04\x05\x01\x02\x0e\x12\x038\ + \x04\x17\n\x0c\n\x05\x05\x01\x02\x0e\x01\x12\x038\x04\x11\n\x0c\n\x05\ + \x05\x01\x02\x0e\x02\x12\x038\x14\x16\n\x0b\n\x04\x05\x01\x02\x0f\x12\ + \x039\x04\x16\n\x0c\n\x05\x05\x01\x02\x0f\x01\x12\x039\x04\x10\n\x0c\n\ + \x05\x05\x01\x02\x0f\x02\x12\x039\x13\x15\n\x0b\n\x04\x05\x01\x02\x10\ + \x12\x03:\x04\x1a\n\x0c\n\x05\x05\x01\x02\x10\x01\x12\x03:\x04\x14\n\x0c\ + \n\x05\x05\x01\x02\x10\x02\x12\x03:\x17\x19\n\x0b\n\x04\x05\x01\x02\x11\ + \x12\x03;\x04\x1b\n\x0c\n\x05\x05\x01\x02\x11\x01\x12\x03;\x04\x15\n\x0c\ + \n\x05\x05\x01\x02\x11\x02\x12\x03;\x18\x1a\n\x0b\n\x04\x05\x01\x02\x12\ + \x12\x03<\x04#\n\x0c\n\x05\x05\x01\x02\x12\x01\x12\x03<\x04\x1d\n\x0c\n\ + \x05\x05\x01\x02\x12\x02\x12\x03<\x20\"\n\n\n\x02\x04\x03\x12\x04?\0M\ + \x01\n\n\n\x03\x04\x03\x01\x12\x03?\x08\x0f\n\x0b\n\x04\x04\x03\x02\0\ + \x12\x03@\x04\x1d\n\r\n\x05\x04\x03\x02\0\x04\x12\x04@\x04?\x11\n\x0c\n\ + \x05\x04\x03\x02\0\x06\x12\x03@\x04\x0f\n\x0c\n\x05\x04\x03\x02\0\x01\ + \x12\x03@\x10\x18\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03@\x1b\x1c\n\x0b\n\ + \x04\x04\x03\x02\x01\x12\x03A\x04\x12\n\r\n\x05\x04\x03\x02\x01\x04\x12\ + \x04A\x04@\x1d\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03A\x04\n\n\x0c\n\ + \x05\x04\x03\x02\x01\x01\x12\x03A\x0b\r\n\x0c\n\x05\x04\x03\x02\x01\x03\ + \x12\x03A\x10\x11\n\x0b\n\x04\x04\x03\x02\x02\x12\x03B\x04\x14\n\r\n\x05\ + \x04\x03\x02\x02\x04\x12\x04B\x04A\x12\n\x0c\n\x05\x04\x03\x02\x02\x05\ + \x12\x03B\x04\n\n\x0c\n\x05\x04\x03\x02\x02\x01\x12\x03B\x0b\x0f\n\x0c\n\ + \x05\x04\x03\x02\x02\x03\x12\x03B\x12\x13\n\x0b\n\x04\x04\x03\x02\x03\ + \x12\x03C\x04\x14\n\r\n\x05\x04\x03\x02\x03\x04\x12\x04C\x04B\x14\n\x0c\ + \n\x05\x04\x03\x02\x03\x05\x12\x03C\x04\n\n\x0c\n\x05\x04\x03\x02\x03\ + \x01\x12\x03C\x0b\x0f\n\x0c\n\x05\x04\x03\x02\x03\x03\x12\x03C\x12\x13\n\ + \x0b\n\x04\x04\x03\x02\x04\x12\x03D\x04\x18\n\r\n\x05\x04\x03\x02\x04\ + \x04\x12\x04D\x04C\x14\n\x0c\n\x05\x04\x03\x02\x04\x05\x12\x03D\x04\n\n\ + \x0c\n\x05\x04\x03\x02\x04\x01\x12\x03D\x0b\x13\n\x0c\n\x05\x04\x03\x02\ + \x04\x03\x12\x03D\x16\x17\n\x0b\n\x04\x04\x03\x02\x05\x12\x03E\x04\x15\n\ + \r\n\x05\x04\x03\x02\x05\x04\x12\x04E\x04D\x18\n\x0c\n\x05\x04\x03\x02\ + \x05\x05\x12\x03E\x04\n\n\x0c\n\x05\x04\x03\x02\x05\x01\x12\x03E\x0b\x10\ + \n\x0c\n\x05\x04\x03\x02\x05\x03\x12\x03E\x13\x14\n\x0b\n\x04\x04\x03\ + \x02\x06\x12\x03F\x04\x1f\n\x0c\n\x05\x04\x03\x02\x06\x04\x12\x03F\x04\ + \x0c\n\x0c\n\x05\x04\x03\x02\x06\x06\x12\x03F\r\x12\n\x0c\n\x05\x04\x03\ + \x02\x06\x01\x12\x03F\x13\x1a\n\x0c\n\x05\x04\x03\x02\x06\x03\x12\x03F\ + \x1d\x1e\n\x0b\n\x04\x04\x03\x02\x07\x12\x03G\x04\x16\n\r\n\x05\x04\x03\ + \x02\x07\x04\x12\x04G\x04F\x1f\n\x0c\n\x05\x04\x03\x02\x07\x05\x12\x03G\ + \x04\n\n\x0c\n\x05\x04\x03\x02\x07\x01\x12\x03G\x0b\x11\n\x0c\n\x05\x04\ + \x03\x02\x07\x03\x12\x03G\x14\x15\n\x0b\n\x04\x04\x03\x02\x08\x12\x03H\ + \x04\x1a\n\r\n\x05\x04\x03\x02\x08\x04\x12\x04H\x04G\x16\n\x0c\n\x05\x04\ + \x03\x02\x08\x06\x12\x03H\x04\x0c\n\x0c\n\x05\x04\x03\x02\x08\x01\x12\ + \x03H\r\x15\n\x0c\n\x05\x04\x03\x02\x08\x03\x12\x03H\x18\x19\n\x0b\n\x04\ + \x04\x03\x02\t\x12\x03I\x04!\n\r\n\x05\x04\x03\x02\t\x04\x12\x04I\x04H\ + \x1a\n\x0c\n\x05\x04\x03\x02\t\x05\x12\x03I\x04\n\n\x0c\n\x05\x04\x03\ + \x02\t\x01\x12\x03I\x0b\x1b\n\x0c\n\x05\x04\x03\x02\t\x03\x12\x03I\x1e\ + \x20\n\x0b\n\x04\x04\x03\x02\n\x12\x03J\x04\x15\n\r\n\x05\x04\x03\x02\n\ + \x04\x12\x04J\x04I!\n\x0c\n\x05\x04\x03\x02\n\x05\x12\x03J\x04\x08\n\x0c\ + \n\x05\x04\x03\x02\n\x01\x12\x03J\t\x0f\n\x0c\n\x05\x04\x03\x02\n\x03\ + \x12\x03J\x12\x14\n\x0b\n\x04\x04\x03\x02\x0b\x12\x03K\x04\x1c\n\r\n\x05\ + \x04\x03\x02\x0b\x04\x12\x04K\x04J\x15\n\x0c\n\x05\x04\x03\x02\x0b\x05\ + \x12\x03K\x04\n\n\x0c\n\x05\x04\x03\x02\x0b\x01\x12\x03K\x0b\x16\n\x0c\n\ + \x05\x04\x03\x02\x0b\x03\x12\x03K\x19\x1b\n\x0b\n\x04\x04\x03\x02\x0c\ + \x12\x03L\x04\x17\n\r\n\x05\x04\x03\x02\x0c\x04\x12\x04L\x04K\x1c\n\x0c\ + \n\x05\x04\x03\x02\x0c\x05\x12\x03L\x04\t\n\x0c\n\x05\x04\x03\x02\x0c\ + \x01\x12\x03L\n\x11\n\x0c\n\x05\x04\x03\x02\x0c\x03\x12\x03L\x14\x16\n\n\ + \n\x02\x04\x04\x12\x04O\0S\x01\n\n\n\x03\x04\x04\x01\x12\x03O\x08\x11\n\ + \x0b\n\x04\x04\x04\x02\0\x12\x03P\x04\x14\n\r\n\x05\x04\x04\x02\0\x04\ + \x12\x04P\x04O\x13\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03P\x04\n\n\x0c\n\ + \x05\x04\x04\x02\0\x01\x12\x03P\x0b\x0f\n\x0c\n\x05\x04\x04\x02\0\x03\ + \x12\x03P\x12\x13\n\x0b\n\x04\x04\x04\x02\x01\x12\x03Q\x04\x14\n\r\n\x05\ + \x04\x04\x02\x01\x04\x12\x04Q\x04P\x14\n\x0c\n\x05\x04\x04\x02\x01\x05\ + \x12\x03Q\x04\n\n\x0c\n\x05\x04\x04\x02\x01\x01\x12\x03Q\x0b\x0f\n\x0c\n\ + \x05\x04\x04\x02\x01\x03\x12\x03Q\x12\x13\n\x0b\n\x04\x04\x04\x02\x02\ + \x12\x03R\x04\x16\n\r\n\x05\x04\x04\x02\x02\x04\x12\x04R\x04Q\x14\n\x0c\ + \n\x05\x04\x04\x02\x02\x05\x12\x03R\x04\n\n\x0c\n\x05\x04\x04\x02\x02\ + \x01\x12\x03R\x0b\x11\n\x0c\n\x05\x04\x04\x02\x02\x03\x12\x03R\x14\x15\n\ + \n\n\x02\x04\x05\x12\x04U\0X\x01\n\n\n\x03\x04\x05\x01\x12\x03U\x08\x11\ + \n\x0b\n\x04\x04\x05\x02\0\x12\x03V\x04\x1e\n\x0c\n\x05\x04\x05\x02\0\ + \x04\x12\x03V\x04\x0c\n\x0c\n\x05\x04\x05\x02\0\x05\x12\x03V\r\x13\n\x0c\ + \n\x05\x04\x05\x02\0\x01\x12\x03V\x14\x19\n\x0c\n\x05\x04\x05\x02\0\x03\ + \x12\x03V\x1c\x1d\n\x0b\n\x04\x04\x05\x02\x01\x12\x03W\x04!\n\x0c\n\x05\ + \x04\x05\x02\x01\x04\x12\x03W\x04\x0c\n\x0c\n\x05\x04\x05\x02\x01\x05\ + \x12\x03W\r\x13\n\x0c\n\x05\x04\x05\x02\x01\x01\x12\x03W\x14\x1c\n\x0c\n\ + \x05\x04\x05\x02\x01\x03\x12\x03W\x1f\x20\n\n\n\x02\x05\x02\x12\x04Z\0^\ + \x01\n\n\n\x03\x05\x02\x01\x12\x03Z\x05\x13\n\x0b\n\x04\x05\x02\x02\0\ + \x12\x03[\x04\x13\n\x0c\n\x05\x05\x02\x02\0\x01\x12\x03[\x04\x0b\n\x0c\n\ + \x05\x05\x02\x02\0\x02\x12\x03[\x11\x12\n\x0b\n\x04\x05\x02\x02\x01\x12\ + \x03\\\x04\x13\n\x0c\n\x05\x05\x02\x02\x01\x01\x12\x03\\\x04\x0e\n\x0c\n\ + \x05\x05\x02\x02\x01\x02\x12\x03\\\x11\x12\n\x0b\n\x04\x05\x02\x02\x02\ + \x12\x03]\x04\x17\n\x0c\n\x05\x05\x02\x02\x02\x01\x12\x03]\x04\x12\n\x0c\ + \n\x05\x05\x02\x02\x02\x02\x12\x03]\x15\x16\n\n\n\x02\x04\x06\x12\x04`\0\ + e\x01\n\n\n\x03\x04\x06\x01\x12\x03`\x08\x12\n\x0b\n\x04\x04\x06\x02\0\ + \x12\x03a\x04\x12\n\r\n\x05\x04\x06\x02\0\x04\x12\x04a\x04`\x14\n\x0c\n\ + \x05\x04\x06\x02\0\x05\x12\x03a\x04\n\n\x0c\n\x05\x04\x06\x02\0\x01\x12\ + \x03a\x0b\r\n\x0c\n\x05\x04\x06\x02\0\x03\x12\x03a\x10\x11\n\x0b\n\x04\ + \x04\x06\x02\x01\x12\x03b\x04#\n\r\n\x05\x04\x06\x02\x01\x04\x12\x04b\ + \x04a\x12\n\x0c\n\x05\x04\x06\x02\x01\x06\x12\x03b\x04\x12\n\x0c\n\x05\ + \x04\x06\x02\x01\x01\x12\x03b\x13\x1e\n\x0c\n\x05\x04\x06\x02\x01\x03\ + \x12\x03b!\"\n\x0b\n\x04\x04\x06\x02\x02\x12\x03c\x04\x17\n\r\n\x05\x04\ + \x06\x02\x02\x04\x12\x04c\x04b#\n\x0c\n\x05\x04\x06\x02\x02\x05\x12\x03c\ + \x04\n\n\x0c\n\x05\x04\x06\x02\x02\x01\x12\x03c\x0b\x12\n\x0c\n\x05\x04\ + \x06\x02\x02\x03\x12\x03c\x15\x16\n\x0b\n\x04\x04\x06\x02\x03\x12\x03d\ + \x04\x16\n\r\n\x05\x04\x06\x02\x03\x04\x12\x04d\x04c\x17\n\x0c\n\x05\x04\ + \x06\x02\x03\x05\x12\x03d\x04\t\n\x0c\n\x05\x04\x06\x02\x03\x01\x12\x03d\ + \n\x11\n\x0c\n\x05\x04\x06\x02\x03\x03\x12\x03d\x14\x15b\x06proto3\ "; static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy { diff --git a/src/errors.rs b/src/errors.rs index 4a7ea6f1f..e6b6ea40d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -63,6 +63,10 @@ quick_error! { NotExists(id: u64, set: &'static str) { display("The node {} is not in the {} set.", id, set) } + /// The request snapshot is dropped. + RequestSnapshotDropped { + description("raft: request snapshot dropped") + } } } @@ -76,6 +80,7 @@ impl cmp::PartialEq for Error { (&Error::Io(ref e1), &Error::Io(ref e2)) => e1.kind() == e2.kind(), (&Error::StepLocalMsg, &Error::StepLocalMsg) => true, (&Error::ConfigInvalid(ref e1), &Error::ConfigInvalid(ref e2)) => e1 == e2, + (&Error::RequestSnapshotDropped, &Error::RequestSnapshotDropped) => true, _ => false, } } @@ -102,7 +107,7 @@ quick_error! { description("snapshot is temporarily unavailable") } /// Some other error occurred. - Other(err: Box) { + Other(err: Box) { from() cause(err.as_ref()) description(err.description()) diff --git a/src/progress.rs b/src/progress.rs index d2a87c2d5..accb59316 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -27,6 +27,7 @@ use errors::Error; use fxhash::FxHashMap; +use raft::INVALID_INDEX; use std::cmp; use std::collections::hash_map::HashMap; @@ -189,6 +190,10 @@ pub struct Progress { /// this Progress will be paused. raft will not resend snapshot until the pending one /// is reported to be failed. pub pending_snapshot: u64, + /// This field is used in request snapshot. + /// If there is a pending request snapshot, this will be set to the request + /// index of the snapshot. + pub pending_request_snapshot: u64, /// This is true if the progress is recently active. Receiving any messages /// from the corresponding follower indicates the progress is active. @@ -248,8 +253,8 @@ impl Progress { self.pending_snapshot = 0; } - /// Unsets pendingSnapshot if Match is equal or higher than - /// the pendingSnapshot + /// Unsets pending_snapshot if matched is equal or higher than + /// the pending_snapshot and the snapshot is not requested. pub fn maybe_snapshot_abort(&self) -> bool { self.state == ProgressState::Snapshot && self.matched >= self.pending_snapshot } @@ -278,25 +283,41 @@ impl Progress { /// Returns false if the given index comes from an out of order message. /// Otherwise it decreases the progress next index to min(rejected, last) /// and returns true. - pub fn maybe_decr_to(&mut self, rejected: u64, last: u64) -> bool { + pub fn maybe_decr_to(&mut self, rejected: u64, last: u64, request_snapshot: u64) -> bool { if self.state == ProgressState::Replicate { // the rejection must be stale if the progress has matched and "rejected" // is smaller than "match". - if rejected <= self.matched { + // Or rejected equals to matched and request_snapshot is the INVALID_INDEX. + if rejected < self.matched + || (rejected == self.matched && request_snapshot == INVALID_INDEX) + { return false; } - self.next_idx = self.matched + 1; + if request_snapshot == INVALID_INDEX { + self.next_idx = self.matched + 1; + } else { + self.pending_request_snapshot = request_snapshot; + } return true; } - // the rejection must be stale if "rejected" does not match next - 1 - if self.next_idx == 0 || self.next_idx - 1 != rejected { + // The rejection must be stale if "rejected" does not match next - 1. + // Do not consider it stale if it is a request snapshot message. + if (self.next_idx == 0 || self.next_idx - 1 != rejected) + && request_snapshot == INVALID_INDEX + { return false; } - self.next_idx = cmp::min(rejected, last + 1); - if self.next_idx < 1 { - self.next_idx = 1; + // Do not decrease next index if it's requesting snapshot. + if request_snapshot == INVALID_INDEX { + self.next_idx = cmp::min(rejected, last + 1); + if self.next_idx < 1 { + self.next_idx = 1; + } + } else if self.pending_request_snapshot == INVALID_INDEX { + // Allow requesting snapshot even if it's not Replicate. + self.pending_request_snapshot = request_snapshot; } self.resume(); true diff --git a/src/raft.rs b/src/raft.rs index 713f9a497..87a8ef7d3 100644 --- a/src/raft.rs +++ b/src/raft.rs @@ -107,6 +107,10 @@ pub struct Raft { /// The maximum length (in bytes) of all the entries. pub max_msg_size: u64, + /// The peer is requesting snapshot, it is the index that the follower + /// needs it to be included in a snapshot. + pub pending_request_snapshot: u64, + prs: Option, /// The current role of this node. @@ -244,6 +248,7 @@ impl Raft { raft_log, max_inflight: c.max_inflight_msgs, max_msg_size: c.max_size_per_msg, + pending_request_snapshot: INVALID_INDEX, prs: Some(ProgressSet::new(peers.len(), learners.len())), state: StateRole::Follower, is_learner: false, @@ -448,7 +453,7 @@ impl Raft { } m.set_msg_type(MessageType::MsgSnapshot); - let snapshot_r = self.raft_log.snapshot(); + let snapshot_r = self.raft_log.snapshot(pr.pending_request_snapshot); if let Err(e) = snapshot_r { if e == Error::Store(StorageError::SnapshotTemporarilyUnavailable) { debug!( @@ -521,17 +526,24 @@ impl Raft { if pr.is_paused() { return; } - let term = self.raft_log.term(pr.next_idx - 1); - let ents = self.raft_log.entries(pr.next_idx, self.max_msg_size); let mut m = Message::new(); m.set_to(to); - if term.is_err() || ents.is_err() { - // send snapshot if we failed to get term or entries + if pr.pending_request_snapshot != INVALID_INDEX { + // Check pending request snapshot first to avoid unnecessary loading entries. if !self.prepare_send_snapshot(&mut m, pr, to) { return; } } else { - self.prepare_send_entries(&mut m, pr, term.unwrap(), ents.unwrap()); + let term = self.raft_log.term(pr.next_idx - 1); + let ents = self.raft_log.entries(pr.next_idx, self.max_msg_size); + if term.is_err() || ents.is_err() { + // send snapshot if we failed to get term or entries. + if !self.prepare_send_snapshot(&mut m, pr, to) { + return; + } + } else { + self.prepare_send_entries(&mut m, pr, term.unwrap(), ents.unwrap()); + } } self.send(m); } @@ -626,6 +638,7 @@ impl Raft { self.pending_conf_index = 0; self.read_only = ReadOnly::new(self.read_only.option); + self.pending_request_snapshot = INVALID_INDEX; let (last_index, max_inflight) = (self.raft_log.last_index(), self.max_inflight); let self_id = self.id; @@ -717,9 +730,11 @@ impl Raft { /// Converts this node to a follower. pub fn become_follower(&mut self, term: u64, leader_id: u64) { + let pending_request_snapshot = self.pending_request_snapshot; self.reset(term); self.leader_id = leader_id; self.state = StateRole::Follower; + self.pending_request_snapshot = pending_request_snapshot; info!("{} became follower at term {}", self.tag, self.term); } @@ -1156,7 +1171,7 @@ impl Raft { m.get_index() ); - if pr.maybe_decr_to(m.get_index(), m.get_reject_hint()) { + if pr.maybe_decr_to(m.get_index(), m.get_reject_hint(), m.get_request_snapshot()) { debug!( "{} decreased progress of {} to [{:?}]", self.tag, @@ -1225,7 +1240,8 @@ impl Raft { if pr.state == ProgressState::Replicate && pr.ins.full() { pr.ins.free_first_one(); } - if pr.matched < self.raft_log.last_index() { + // Does it request snapshot? + if pr.matched < self.raft_log.last_index() || pr.pending_request_snapshot != INVALID_INDEX { *send_append = true; } @@ -1333,6 +1349,7 @@ impl Raft { // out the next msgAppend. // If snapshot failure, wait for a heartbeat interval before next try pr.pause(); + pr.pending_request_snapshot = INVALID_INDEX; } /// Check message's progress to decide which action should be taken. @@ -1710,9 +1727,43 @@ impl Raft { Ok(()) } + /// Request a snapshot from a leader. + pub fn request_snapshot(&mut self, request_index: u64) -> Result<()> { + if self.state == StateRole::Leader { + info!( + "{} can not request snapshot on leader; dropping request snapshot", + self.tag + ); + } else if self.leader_id == INVALID_ID { + info!( + "{} no leader at term {}; dropping request snapshot", + self.tag, self.term + ); + } else if self.get_snap().is_some() { + info!( + "{} there is a pending snapshot; dropping request snapshot", + self.tag + ); + } else if self.pending_request_snapshot != INVALID_INDEX { + info!( + "{} there is a pending snapshot; dropping request snapshot", + self.tag + ); + } else { + self.pending_request_snapshot = request_index; + self.send_request_snapshot(); + return Ok(()); + } + Err(Error::RequestSnapshotDropped) + } + // TODO: revoke pub when there is a better way to test. /// For a given message, append the entries to the log. pub fn handle_append_entries(&mut self, m: &Message) { + if self.pending_request_snapshot != INVALID_INDEX { + self.send_request_snapshot(); + return; + } if m.get_index() < self.raft_log.committed { let mut to_send = Message::new(); to_send.set_to(m.get_from()); @@ -1757,6 +1808,10 @@ impl Raft { /// For a message, commit and send out heartbeat. pub fn handle_heartbeat(&mut self, mut m: Message) { self.raft_log.commit_to(m.get_commit()); + if self.pending_request_snapshot != INVALID_INDEX { + self.send_request_snapshot(); + return; + } let mut to_send = Message::new(); to_send.set_to(m.get_from()); to_send.set_msg_type(MessageType::MsgHeartbeatResponse); @@ -1794,7 +1849,10 @@ impl Raft { fn restore_raft(&mut self, snap: &Snapshot) -> Option { let meta = snap.get_metadata(); - if self.raft_log.match_term(meta.get_index(), meta.get_term()) { + // Do not fast-forward commit if we are requesting snapshot. + if self.pending_request_snapshot == INVALID_INDEX + && self.raft_log.match_term(meta.get_index(), meta.get_term()) + { info!( "{} [commit: {}, lastindex: {}, lastterm: {}] fast-forwarded commit to \ snapshot [index: {}, term: {}]", @@ -1837,6 +1895,7 @@ impl Raft { meta.get_term() ); + self.pending_request_snapshot = INVALID_INDEX; let nodes = meta.get_conf_state().get_nodes(); let learners = meta.get_conf_state().get_learners(); self.prs = Some(ProgressSet::new(nodes.len(), learners.len())); @@ -2058,4 +2117,15 @@ impl Raft { pub fn abort_leader_transfer(&mut self) { self.lead_transferee = None; } + + fn send_request_snapshot(&mut self) { + let mut m = Message::new(); + m.set_msg_type(MessageType::MsgAppendResponse); + m.set_index(self.raft_log.committed); + m.set_reject(true); + m.set_reject_hint(self.raft_log.last_index()); + m.set_to(self.leader_id); + m.set_request_snapshot(self.pending_request_snapshot); + self.send(m); + } } diff --git a/src/raft_log.rs b/src/raft_log.rs index 83d7f1c80..e3aa5b84b 100644 --- a/src/raft_log.rs +++ b/src/raft_log.rs @@ -384,11 +384,13 @@ impl RaftLog { } /// Returns the current snapshot - pub fn snapshot(&self) -> Result { - self.unstable - .snapshot - .clone() - .map_or_else(|| self.store.snapshot(), Ok) + pub fn snapshot(&self, request_index: u64) -> Result { + if let Some(snap) = self.unstable.snapshot.as_ref() { + if snap.get_metadata().get_index() >= request_index { + return Ok(snap.clone()); + } + } + self.store.snapshot(request_index) } fn must_check_outofbounds(&self, low: u64, high: u64) -> Option { diff --git a/src/raw_node.rs b/src/raw_node.rs index 350812b11..4383fba5a 100644 --- a/src/raw_node.rs +++ b/src/raw_node.rs @@ -457,6 +457,12 @@ impl RawNode { let _ = self.raft.step(m); } + /// Request a snapshot from a leader. + /// The snapshot's index must be greater or equal to the request_index. + pub fn request_snapshot(&mut self, request_index: u64) -> Result<()> { + self.raft.request_snapshot(request_index) + } + /// TransferLeader tries to transfer leadership to the given transferee. pub fn transfer_leader(&mut self, transferee: u64) { let mut m = Message::new(); diff --git a/src/storage.rs b/src/storage.rs index d51565c8f..37987e08e 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -78,7 +78,8 @@ pub trait Storage { /// If snapshot is temporarily unavailable, it should return SnapshotTemporarilyUnavailable, /// so raft state machine could know that Storage needs some time to prepare /// snapshot and call snapshot later. - fn snapshot(&self) -> Result; + /// A snapshot's index must not less than the `request_index`. + fn snapshot(&self, request_index: u64) -> Result; } /// The Memory Storage Core instance holds the actual state of the storage struct. To access this @@ -89,6 +90,9 @@ pub struct MemStorageCore { // TODO: maybe vec_deque // entries[i] has raft log position i+snapshot.get_metadata().get_index() entries: Vec, + // If it is true, the next snapshot will return a + // SnapshotTemporarilyUnavailable error. + trigger_snap_unavailable: bool, } impl Default for MemStorageCore { @@ -98,6 +102,7 @@ impl Default for MemStorageCore { entries: vec![Entry::new()], hard_state: HardState::new(), snapshot: Snapshot::new(), + trigger_snap_unavailable: false, } } } @@ -223,6 +228,11 @@ impl MemStorageCore { Ok(()) } + + /// Trigger a SnapshotTemporarilyUnavailable error. + pub fn trigger_snap_unavailable(&mut self) { + self.trigger_snap_unavailable = true; + } } /// `MemStorage` is a thread-safe implementation of Storage trait. @@ -312,9 +322,17 @@ impl Storage for MemStorage { } /// Implements the Storage trait. - fn snapshot(&self) -> Result { - let core = self.rl(); - Ok(core.snapshot.clone()) + fn snapshot(&self, request_index: u64) -> Result { + let mut core = self.wl(); + if core.trigger_snap_unavailable { + core.trigger_snap_unavailable = false; + Err(Error::Store(StorageError::SnapshotTemporarilyUnavailable)) + } else { + if core.snapshot.get_metadata().get_index() < request_index { + core.snapshot.mut_metadata().set_index(request_index); + } + Ok(core.snapshot.clone()) + } } } @@ -529,11 +547,16 @@ mod test { cs.set_nodes(nodes.clone()); let data = b"data".to_vec(); + let unavailable = Err(RaftError::Store( + StorageError::SnapshotTemporarilyUnavailable, + )); let mut tests = vec![ - (4, Ok(new_snapshot(4, 4, nodes.clone(), data.clone()))), - (5, Ok(new_snapshot(5, 5, nodes.clone(), data.clone()))), + (4, Ok(new_snapshot(4, 4, nodes.clone(), data.clone())), 0), + (5, Ok(new_snapshot(5, 5, nodes.clone(), data.clone())), 5), + (5, Ok(new_snapshot(6, 5, nodes.clone(), data.clone())), 6), + (5, unavailable, 6), ]; - for (i, (idx, wresult)) in tests.drain(..).enumerate() { + for (i, (idx, wresult, windex)) in tests.drain(..).enumerate() { let storage = MemStorage::new(); storage.wl().entries = ents.clone(); @@ -541,7 +564,10 @@ mod test { .wl() .create_snapshot(idx, Some(cs.clone()), data.clone()) .expect("create snapshot failed"); - let result = storage.snapshot(); + if wresult.is_err() { + storage.wl().trigger_snap_unavailable(); + } + let result = storage.snapshot(windex); if result != wresult { panic!("#{}: want {:?}, got {:?}", i, wresult, result); } diff --git a/tests/integration_cases/test_raft.rs b/tests/integration_cases/test_raft.rs index f1a80f53a..71ab42d24 100644 --- a/tests/integration_cases/test_raft.rs +++ b/tests/integration_cases/test_raft.rs @@ -32,6 +32,7 @@ use std::panic::{self, AssertUnwindSafe}; use protobuf::{self, RepeatedField}; use raft::eraftpb::{ ConfChange, ConfChangeType, ConfState, Entry, EntryType, HardState, Message, MessageType, + Snapshot, }; use raft::storage::MemStorage; use raft::*; @@ -238,7 +239,7 @@ fn test_progress_maybe_decr() { ]; for (i, &(state, m, n, rejected, last, w, wn)) in tests.iter().enumerate() { let mut p = new_progress(state, m, n, 0, 0); - if p.maybe_decr_to(rejected, last) != w { + if p.maybe_decr_to(rejected, last, 0) != w { panic!("#{}: maybeDecrTo= {}, want {}", i, !w, w); } if p.matched != m { @@ -284,7 +285,7 @@ fn test_progress_resume() { paused: true, ..Default::default() }; - p.maybe_decr_to(1, 1); + p.maybe_decr_to(1, 1, INVALID_INDEX); assert!(!p.paused, "paused= true, want false"); p.paused = true; p.maybe_update(2); @@ -4184,3 +4185,291 @@ fn test_conf_change_check_before_campaign() { } assert_eq!(nt.peers[&1].state, StateRole::Candidate); } + +fn prepare_request_snapshot() -> (Network, Snapshot) { + fn index_term_11(id: u64, ids: Vec) -> Interface { + let store = MemStorage::new(); + store + .wl() + .apply_snapshot(new_snapshot(11, 11, ids.clone())) + .unwrap(); + let mut raft = new_test_raft(id, vec![], 5, 1, store); + raft.reset(11); + raft + } + let mut nt = Network::new(vec![ + Some(index_term_11(1, vec![1, 2, 3])), + Some(index_term_11(2, vec![1, 2, 3])), + Some(index_term_11(3, vec![1, 2, 3])), + ]); + + // elect r1 as leader + nt.send(vec![new_message(1, 1, MessageType::MsgHup, 0)]); + + let mut test_entries = Entry::new(); + test_entries.set_data(b"testdata".to_vec()); + let msg = new_message_with_entries(1, 1, MessageType::MsgPropose, vec![test_entries.clone()]); + nt.send(vec![msg.clone(), msg.clone()]); + assert_eq!(nt.peers[&1].raft_log.committed, 14); + assert_eq!(nt.peers[&2].raft_log.committed, 14); + + let mut cs = ConfState::new(); + cs.set_nodes(nt.peers[&1].prs().nodes()); + let ents = nt + .peers + .get_mut(&1) + .unwrap() + .raft_log + .unstable_entries() + .unwrap_or(&[]) + .to_vec(); + nt.storage[&1].wl().append(&ents).unwrap(); + nt.peers.get_mut(&1).unwrap().raft_log.applied = 14; + let s = nt.storage[&1] + .wl() + .create_snapshot(14, Some(cs), vec![7; 7]) + .unwrap() + .to_owned(); + + // Commit a new raft log. + let mut test_entries = Entry::new(); + test_entries.set_data(b"testdata".to_vec()); + let msg = new_message_with_entries(1, 1, MessageType::MsgPropose, vec![test_entries.clone()]); + nt.send(vec![msg.clone()]); + + (nt, s) +} + +// Test if an up-to-date follower can request a snapshot from leader. +#[test] +fn test_follower_request_snapshot() { + setup_for_test(); + let (mut nt, s) = prepare_request_snapshot(); + + // Request the latest snapshot. + let prev_snapshot_idx = s.get_metadata().get_index(); + let request_idx = nt.peers[&1].raft_log.committed; + assert!(prev_snapshot_idx < request_idx); + nt.peers + .get_mut(&2) + .unwrap() + .request_snapshot(request_idx) + .unwrap(); + + // Send the request snapshot message. + let req_snap = nt.peers.get_mut(&2).unwrap().msgs.pop().unwrap(); + assert!( + req_snap.get_msg_type() == MessageType::MsgAppendResponse + && req_snap.get_reject() + && req_snap.get_request_snapshot() == request_idx, + "{:?}", + req_snap + ); + nt.peers.get_mut(&1).unwrap().step(req_snap).unwrap(); + + // New proposes can not be replicated to peer 2. + let mut test_entries = Entry::new(); + test_entries.set_data(b"testdata".to_vec()); + let msg = new_message_with_entries(1, 1, MessageType::MsgPropose, vec![test_entries.clone()]); + nt.send(vec![msg.clone()]); + assert_eq!(nt.peers[&1].raft_log.committed, 16); + assert_eq!( + nt.peers[&1].prs().voters()[&2].state, + ProgressState::Snapshot + ); + assert_eq!(nt.peers[&2].raft_log.committed, 15); + + // Util snapshot success or fail. + let report_ok = new_message(2, 1, MessageType::MsgSnapStatus, 0); + nt.send(vec![report_ok]); + let hb_resp = new_message(2, 1, MessageType::MsgHeartbeatResponse, 0); + nt.send(vec![hb_resp]); + nt.send(vec![msg]); + assert_eq!(nt.peers[&1].raft_log.committed, 17); + assert_eq!(nt.peers[&2].raft_log.committed, 17); +} + +// Test if request snapshot can make progress when it meets SnapshotTemporarilyUnavailable. +#[test] +fn test_request_snapshot_unavailable() { + setup_for_test(); + let (mut nt, s) = prepare_request_snapshot(); + + // Request the latest snapshot. + let prev_snapshot_idx = s.get_metadata().get_index(); + let request_idx = nt.peers[&1].raft_log.committed; + assert!(prev_snapshot_idx < request_idx); + nt.peers + .get_mut(&2) + .unwrap() + .request_snapshot(request_idx) + .unwrap(); + + // Send the request snapshot message. + let req_snap = nt.peers.get_mut(&2).unwrap().msgs.pop().unwrap(); + assert!( + req_snap.get_msg_type() == MessageType::MsgAppendResponse + && req_snap.get_reject() + && req_snap.get_request_snapshot() == request_idx, + "{:?}", + req_snap + ); + + // Peer 2 is still in probe state due to SnapshotTemporarilyUnavailable. + nt.peers[&1].get_store().wl().trigger_snap_unavailable(); + nt.peers + .get_mut(&1) + .unwrap() + .step(req_snap.clone()) + .unwrap(); + assert_eq!(nt.peers[&1].prs().voters()[&2].state, ProgressState::Probe); + + // Next index is decreased. + nt.peers[&1].get_store().wl().trigger_snap_unavailable(); + nt.peers + .get_mut(&1) + .unwrap() + .step(req_snap.clone()) + .unwrap(); + assert_eq!(nt.peers[&1].prs().voters()[&2].state, ProgressState::Probe); + + // Snapshot will be available if it requests again. This message must not + // be considered stale even if `reject != next - 1` + nt.peers + .get_mut(&1) + .unwrap() + .step(req_snap.clone()) + .unwrap(); + assert_eq!( + nt.peers[&1].prs().voters()[&2].state, + ProgressState::Snapshot, + ); +} + +// Test if request snapshot can make progress when matched is advanced. +#[test] +fn test_request_snapshot_matched_change() { + setup_for_test(); + let (mut nt, _) = prepare_request_snapshot(); + // Let matched be greater than the committed. + nt.peers.get_mut(&2).unwrap().raft_log.committed -= 1; + + // Request the latest snapshot. + let request_idx = nt.peers[&2].raft_log.committed; + nt.peers + .get_mut(&2) + .unwrap() + .request_snapshot(request_idx) + .unwrap(); + let req_snap = nt.peers.get_mut(&2).unwrap().msgs.pop().unwrap(); + // The request snapshot is ignored because it is considered as out of order. + nt.peers.get_mut(&1).unwrap().step(req_snap).unwrap(); + assert_eq!( + nt.peers[&1].prs().voters()[&2].state, + ProgressState::Replicate + ); + + // Heartbeat is responsed with a request snapshot message. + for _ in 0..nt.peers[&1].get_heartbeat_timeout() { + nt.peers.get_mut(&1).unwrap().tick(); + } + let msg_hb = nt.peers.get_mut(&1).unwrap().msgs.pop().unwrap(); + nt.peers.get_mut(&2).unwrap().step(msg_hb).unwrap(); + let req_snap = nt.peers.get_mut(&2).unwrap().msgs.pop().unwrap(); + nt.peers + .get_mut(&1) + .unwrap() + .step(req_snap.clone()) + .unwrap(); + assert_eq!( + nt.peers[&1].prs().voters()[&2].state, + ProgressState::Snapshot + ); +} + +// Test if request snapshot can make progress when the peer is not Replicate. +#[test] +fn test_request_snapshot_none_replicate() { + setup_for_test(); + let (mut nt, _) = prepare_request_snapshot(); + nt.peers + .get_mut(&1) + .unwrap() + .mut_prs() + .get_mut(2) + .unwrap() + .state = ProgressState::Probe; + + // Request the latest snapshot. + let request_idx = nt.peers[&2].raft_log.committed; + nt.peers + .get_mut(&2) + .unwrap() + .request_snapshot(request_idx) + .unwrap(); + let req_snap = nt.peers.get_mut(&2).unwrap().msgs.pop().unwrap(); + nt.peers.get_mut(&1).unwrap().step(req_snap).unwrap(); + assert!(nt.peers[&1].prs().voters()[&2].pending_request_snapshot != 0); +} + +// Test if request snapshot can make progress when leader steps down. +#[test] +fn test_request_snapshot_step_down() { + setup_for_test(); + let (mut nt, _) = prepare_request_snapshot(); + + // Commit a new entry and leader steps down while peer 2 is isolated. + nt.isolate(2); + let mut test_entries = Entry::new(); + test_entries.set_data(b"testdata".to_vec()); + let msg = new_message_with_entries(1, 1, MessageType::MsgPropose, vec![test_entries.clone()]); + nt.send(vec![msg.clone()]); + nt.send(vec![new_message(3, 3, MessageType::MsgHup, 0)]); + assert_eq!(nt.peers[&3].state, StateRole::Leader); + + // Recover and request the latest snapshot. + nt.recover(); + let request_idx = nt.peers[&2].raft_log.committed; + nt.peers + .get_mut(&2) + .unwrap() + .request_snapshot(request_idx) + .unwrap(); + nt.send(vec![new_message(3, 3, MessageType::MsgBeat, 0)]); + assert!( + nt.peers[&2].pending_request_snapshot == INVALID_INDEX, + "{}", + nt.peers[&2].pending_request_snapshot + ); +} + +// Abort request snapshot if it becomes leader or candidate. +#[test] +fn test_request_snapshot_on_role_change() { + setup_for_test(); + let (mut nt, _) = prepare_request_snapshot(); + + let request_idx = nt.peers[&2].raft_log.committed; + nt.peers + .get_mut(&2) + .unwrap() + .request_snapshot(request_idx) + .unwrap(); + + // Becoming follower does not reset pending_request_snapshot. + let (term, id) = (nt.peers[&1].term, nt.peers[&1].id); + nt.peers.get_mut(&2).unwrap().become_follower(term, id); + assert!( + nt.peers[&2].pending_request_snapshot != INVALID_INDEX, + "{}", + nt.peers[&2].pending_request_snapshot + ); + + // Becoming candidate resets pending_request_snapshot. + nt.peers.get_mut(&2).unwrap().become_candidate(); + assert!( + nt.peers[&2].pending_request_snapshot == INVALID_INDEX, + "{}", + nt.peers[&2].pending_request_snapshot + ); +} diff --git a/tests/integration_cases/test_raft_snap.rs b/tests/integration_cases/test_raft_snap.rs index 1dc861841..bcd04bb03 100644 --- a/tests/integration_cases/test_raft_snap.rs +++ b/tests/integration_cases/test_raft_snap.rs @@ -26,6 +26,7 @@ // limitations under the License. use raft::eraftpb::*; +use raft::{Error, ProgressState, INVALID_INDEX}; use test_util::*; fn testing_snap() -> Snapshot { @@ -128,3 +129,83 @@ fn test_snapshot_abort() { assert_eq!(sm.prs().voters()[&2].pending_snapshot, 0); assert_eq!(sm.prs().voters()[&2].next_idx, 12); } + +#[test] +fn test_request_snapshot() { + setup_for_test(); + let mut sm = new_test_raft(1, vec![1, 2], 10, 1, new_storage()); + sm.restore(testing_snap()); + + // Raft can not step request snapshot if there is no leader. + assert_eq!( + sm.raft + .as_mut() + .unwrap() + .request_snapshot(INVALID_INDEX + 1) + .unwrap_err(), + Error::RequestSnapshotDropped + ); + + sm.become_candidate(); + sm.become_leader(); + + // Raft can not step request snapshot if itself is a leader. + assert_eq!( + sm.raft + .as_mut() + .unwrap() + .request_snapshot(INVALID_INDEX + 1) + .unwrap_err(), + Error::RequestSnapshotDropped + ); + + // Advance matched. + let mut m = new_message(2, 1, MessageType::MsgAppendResponse, 0); + m.set_index(11); + sm.step(m).unwrap(); + assert_eq!(sm.prs().voters()[&2].state, ProgressState::Replicate); + + let request_snapshot_idx = sm.raft_log.committed; + let mut m = new_message(2, 1, MessageType::MsgAppendResponse, 0); + m.set_index(11); + m.set_reject(true); + m.set_reject_hint(INVALID_INDEX); + m.set_request_snapshot(request_snapshot_idx); + + // Ignore out of order request snapshot messages. + let mut out_of_order = m.clone(); + out_of_order.set_index(9); + sm.step(out_of_order).unwrap(); + assert_eq!(sm.prs().voters()[&2].state, ProgressState::Replicate); + + // Request snapshot. + sm.step(m.clone()).unwrap(); + assert_eq!(sm.prs().voters()[&2].state, ProgressState::Snapshot); + assert_eq!(sm.prs().voters()[&2].pending_snapshot, 11); + assert_eq!(sm.prs().voters()[&2].next_idx, 12); + assert!(sm.prs().voters()[&2].is_paused()); + let snap = sm.msgs.pop().unwrap(); + assert!( + snap.get_msg_type() == MessageType::MsgSnapshot + && snap.get_snapshot().get_metadata().get_index() == request_snapshot_idx, + "{:?}", + snap + ); + + // Append/heartbeats does not set the state from snapshot to probe. + let mut m = new_message(2, 1, MessageType::MsgAppendResponse, 0); + m.set_index(11); + sm.step(m).unwrap(); + assert_eq!(sm.prs().voters()[&2].state, ProgressState::Snapshot); + assert_eq!(sm.prs().voters()[&2].pending_snapshot, 11); + assert_eq!(sm.prs().voters()[&2].next_idx, 12); + assert!(sm.prs().voters()[&2].is_paused()); + + // However snapshot status report does set the stat to probe. + let m = new_message(2, 1, MessageType::MsgSnapStatus, 0); + sm.step(m).unwrap(); + assert_eq!(sm.prs().voters()[&2].state, ProgressState::Probe); + assert_eq!(sm.prs().voters()[&2].pending_snapshot, 0); + assert_eq!(sm.prs().voters()[&2].next_idx, 12); + assert!(sm.prs().voters()[&2].is_paused()); +} diff --git a/tests/test_util/mod.rs b/tests/test_util/mod.rs index 2c1564d47..7811b1de8 100644 --- a/tests/test_util/mod.rs +++ b/tests/test_util/mod.rs @@ -60,6 +60,7 @@ pub fn new_test_config(id: u64, peers: Vec, election: usize, heartbeat: usi heartbeat_tick: heartbeat, max_size_per_msg: NO_LIMIT, max_inflight_msgs: 256, + tag: format!("{}", id), ..Default::default() } } @@ -266,6 +267,9 @@ impl Network { } Some(mut p) => { p.initial(id, &peer_addrs); + if let Some(raft) = p.raft.as_mut() { + nstorage.insert(id, raft.raft_log.store.clone()); + } npeers.insert(id, p); } }