nodedb_cluster/rpc_codec/
surrogate.rs1use super::discriminants::*;
17use super::execute::TypedClusterError;
18use super::header::write_frame;
19use super::raft_rpc::RaftRpc;
20use crate::error::{ClusterError, Result};
21
22#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
34pub struct AssignSurrogateRequest {
35 pub vshard_id: u32,
37 pub database_id: u64,
38 pub tenant_id: u64,
39 pub collection: String,
41 pub pk: Vec<u8>,
43 pub deadline_remaining_ms: u64,
44 pub trace_id: [u8; 16],
45}
46
47#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
55pub struct AssignSurrogateResponse {
56 pub surrogate: u32,
58 pub error: Option<TypedClusterError>,
59}
60
61macro_rules! to_bytes {
64 ($msg:expr) => {
65 rkyv::to_bytes::<rkyv::rancor::Error>($msg)
66 .map(|b| b.to_vec())
67 .map_err(|e| ClusterError::Codec {
68 detail: format!("rkyv serialize: {e}"),
69 })
70 };
71}
72
73macro_rules! from_bytes {
74 ($payload:expr, $T:ty, $name:expr) => {{
75 let mut aligned = rkyv::util::AlignedVec::<16>::with_capacity($payload.len());
76 aligned.extend_from_slice($payload);
77 rkyv::from_bytes::<$T, rkyv::rancor::Error>(&aligned).map_err(|e| ClusterError::Codec {
78 detail: format!("rkyv deserialize {}: {e}", $name),
79 })
80 }};
81}
82
83pub(super) fn encode_assign_surrogate_req(
84 msg: &AssignSurrogateRequest,
85 out: &mut Vec<u8>,
86) -> Result<()> {
87 write_frame(RPC_ASSIGN_SURROGATE_REQ, &to_bytes!(msg)?, out)
88}
89pub(super) fn encode_assign_surrogate_resp(
90 msg: &AssignSurrogateResponse,
91 out: &mut Vec<u8>,
92) -> Result<()> {
93 write_frame(RPC_ASSIGN_SURROGATE_RESP, &to_bytes!(msg)?, out)
94}
95
96pub(super) fn decode_assign_surrogate_req(payload: &[u8]) -> Result<RaftRpc> {
97 Ok(RaftRpc::AssignSurrogateRequest(from_bytes!(
98 payload,
99 AssignSurrogateRequest,
100 "AssignSurrogateRequest"
101 )?))
102}
103pub(super) fn decode_assign_surrogate_resp(payload: &[u8]) -> Result<RaftRpc> {
104 Ok(RaftRpc::AssignSurrogateResponse(from_bytes!(
105 payload,
106 AssignSurrogateResponse,
107 "AssignSurrogateResponse"
108 )?))
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 fn roundtrip_req(req: AssignSurrogateRequest) -> AssignSurrogateRequest {
116 let rpc = RaftRpc::AssignSurrogateRequest(req);
117 let encoded = super::super::encode(&rpc).unwrap();
118 match super::super::decode(&encoded).unwrap() {
119 RaftRpc::AssignSurrogateRequest(r) => r,
120 other => panic!("expected AssignSurrogateRequest, got {other:?}"),
121 }
122 }
123
124 fn roundtrip_resp(resp: AssignSurrogateResponse) -> AssignSurrogateResponse {
125 let rpc = RaftRpc::AssignSurrogateResponse(resp);
126 let encoded = super::super::encode(&rpc).unwrap();
127 match super::super::decode(&encoded).unwrap() {
128 RaftRpc::AssignSurrogateResponse(r) => r,
129 other => panic!("expected AssignSurrogateResponse, got {other:?}"),
130 }
131 }
132
133 #[test]
134 fn roundtrip_assign_surrogate_request() {
135 let req = AssignSurrogateRequest {
136 vshard_id: 512,
137 database_id: 7,
138 tenant_id: 42,
139 collection: "people".into(),
140 pk: vec![0x61, 0x6C, 0x69, 0x63, 0x65],
141 deadline_remaining_ms: 5000,
142 trace_id: [9u8; 16],
143 };
144 let decoded = roundtrip_req(req.clone());
145 assert_eq!(decoded.vshard_id, 512);
146 assert_eq!(decoded.database_id, 7);
147 assert_eq!(decoded.tenant_id, 42);
148 assert_eq!(decoded.collection, "people");
149 assert_eq!(decoded.pk, vec![0x61, 0x6C, 0x69, 0x63, 0x65]);
150 assert_eq!(decoded.deadline_remaining_ms, 5000);
151 assert_eq!(decoded.trace_id, [9u8; 16]);
152 }
153
154 #[test]
155 fn roundtrip_assign_surrogate_request_empty_pk() {
156 let req = AssignSurrogateRequest {
157 vshard_id: 0,
158 database_id: 0,
159 tenant_id: 0,
160 collection: String::new(),
161 pk: vec![],
162 deadline_remaining_ms: 1000,
163 trace_id: [0u8; 16],
164 };
165 let decoded = roundtrip_req(req);
166 assert!(decoded.pk.is_empty());
167 assert!(decoded.collection.is_empty());
168 assert_eq!(decoded.deadline_remaining_ms, 1000);
169 }
170
171 #[test]
172 fn roundtrip_assign_surrogate_response_ok() {
173 let decoded = roundtrip_resp(AssignSurrogateResponse {
174 surrogate: 12345,
175 error: None,
176 });
177 assert_eq!(decoded.surrogate, 12345);
178 assert!(decoded.error.is_none());
179 }
180
181 #[test]
182 fn roundtrip_assign_surrogate_response_error() {
183 let decoded = roundtrip_resp(AssignSurrogateResponse {
184 surrogate: 0,
185 error: Some(TypedClusterError::Internal {
186 code: 0x7,
187 message: "assign-remote-surrogate not configured".into(),
188 }),
189 });
190 assert_eq!(decoded.surrogate, 0);
191 match decoded.error {
192 Some(TypedClusterError::Internal { code, message }) => {
193 assert_eq!(code, 0x7);
194 assert!(message.contains("assign-remote-surrogate"));
195 }
196 other => panic!("expected Internal, got {other:?}"),
197 }
198 }
199}