1pub mod actor_mesh;
14pub mod host_mesh;
15pub mod proc_mesh;
16pub mod testactor;
17pub mod testing;
18pub mod value_mesh;
19
20use std::str::FromStr;
21
22pub use actor_mesh::ActorMesh;
23pub use actor_mesh::ActorMeshRef;
24pub use host_mesh::HostMeshRef;
25use hyperactor::ActorId;
26use hyperactor::ActorRef;
27use hyperactor::mailbox::MailboxSenderError;
28use ndslice::view;
29pub use proc_mesh::ProcMesh;
30pub use proc_mesh::ProcMeshRef;
31use serde::Deserialize;
32use serde::Serialize;
33pub use value_mesh::ValueMesh;
34
35use crate::resource;
36use crate::shortuuid::ShortUuid;
37use crate::v1::host_mesh::HostMeshAgent;
38use crate::v1::host_mesh::HostMeshRefParseError;
39
40#[derive(Debug, thiserror::Error)]
42pub enum Error {
43 #[error("invalid mesh ref: expected {expected} ranks, but contains {actual} ranks")]
44 InvalidRankCardinality { expected: usize, actual: usize },
45
46 #[error(transparent)]
47 NameParseError(#[from] NameParseError),
48
49 #[error(transparent)]
50 HostMeshRefParseError(#[from] HostMeshRefParseError),
51
52 #[error(transparent)]
53 AllocatorError(#[from] Box<crate::alloc::AllocatorError>),
54
55 #[error(transparent)]
56 ChannelError(#[from] Box<hyperactor::channel::ChannelError>),
57
58 #[error(transparent)]
59 MailboxError(#[from] Box<hyperactor::mailbox::MailboxError>),
60
61 #[error(transparent)]
62 CodecError(#[from] CodecError),
63
64 #[error("error during mesh configuration: {0}")]
65 ConfigurationError(anyhow::Error),
66
67 #[error("configuration error: mesh is unroutable")]
70 UnroutableMesh(),
71
72 #[error("error while calling actor {0}: {1}")]
73 CallError(ActorId, anyhow::Error),
74
75 #[error("actor not registered for type {0}")]
76 ActorTypeNotRegistered(String),
77
78 #[error("error while spawning actor {0}: {1}")]
80 GspawnError(Name, String),
81
82 #[error("error while sending message to actor {0}: {1}")]
83 SendingError(ActorId, Box<MailboxSenderError>),
84
85 #[error("error while casting message to {0}: {1}")]
86 CastingError(Name, anyhow::Error),
87
88 #[error("error configuring host mesh agent {0}: {1}")]
89 HostMeshAgentConfigurationError(ActorId, String),
90
91 #[error("error creating {proc_name} (host rank {host_rank}) on host mesh agent {mesh_agent}")]
92 ProcCreationError {
93 proc_name: Name,
94 mesh_agent: ActorRef<HostMeshAgent>,
95 host_rank: usize,
96 status: resource::Status,
97 },
98
99 #[error("error: {0} does not exist")]
100 NotExist(Name),
101}
102
103#[derive(Debug, thiserror::Error)]
105pub enum CodecError {
106 #[error(transparent)]
107 BincodeError(#[from] Box<bincode::Error>),
108 #[error(transparent)]
109 JsonError(#[from] Box<serde_json::Error>),
110 #[error(transparent)]
111 Base64Error(#[from] Box<base64::DecodeError>),
112 #[error(transparent)]
113 Utf8Error(#[from] Box<std::str::Utf8Error>),
114}
115
116impl From<bincode::Error> for Error {
117 fn from(e: bincode::Error) -> Self {
118 Error::CodecError(Box::new(e).into())
119 }
120}
121
122impl From<serde_json::Error> for Error {
123 fn from(e: serde_json::Error) -> Self {
124 Error::CodecError(Box::new(e).into())
125 }
126}
127
128impl From<base64::DecodeError> for Error {
129 fn from(e: base64::DecodeError) -> Self {
130 Error::CodecError(Box::new(e).into())
131 }
132}
133
134impl From<std::str::Utf8Error> for Error {
135 fn from(e: std::str::Utf8Error) -> Self {
136 Error::CodecError(Box::new(e).into())
137 }
138}
139
140impl From<crate::alloc::AllocatorError> for Error {
141 fn from(e: crate::alloc::AllocatorError) -> Self {
142 Error::AllocatorError(Box::new(e))
143 }
144}
145
146impl From<hyperactor::channel::ChannelError> for Error {
147 fn from(e: hyperactor::channel::ChannelError) -> Self {
148 Error::ChannelError(Box::new(e))
149 }
150}
151
152impl From<hyperactor::mailbox::MailboxError> for Error {
153 fn from(e: hyperactor::mailbox::MailboxError) -> Self {
154 Error::MailboxError(Box::new(e))
155 }
156}
157
158impl From<view::InvalidCardinality> for crate::v1::Error {
159 fn from(e: view::InvalidCardinality) -> Self {
160 crate::v1::Error::InvalidRankCardinality {
161 expected: e.expected,
162 actual: e.actual,
163 }
164 }
165}
166
167pub type Result<T> = std::result::Result<T, Error>;
169
170#[derive(
175 Debug,
176 Clone,
177 PartialEq,
178 Eq,
179 PartialOrd,
180 Ord,
181 Hash,
182 Serialize,
183 Deserialize
184)]
185pub enum Name {
186 Suffixed(String, ShortUuid),
188 Reserved(String),
190}
191
192static NAME_SUFFIX_DELIMITER: &str = "_";
196
197impl Name {
198 pub fn new(name: impl Into<String>) -> Self {
200 Self::new_with_uuid(name, Some(ShortUuid::generate()))
201 }
202
203 pub(crate) fn new_reserved(name: impl Into<String>) -> Self {
205 Self::new_with_uuid(name, None)
206 }
207
208 fn new_with_uuid(name: impl Into<String>, uuid: Option<ShortUuid>) -> Self {
209 let mut name = name.into();
210 if name.is_empty() {
211 name = "unnamed".to_string();
212 }
213 if let Some(uuid) = uuid {
214 Self::Suffixed(name, uuid)
215 } else {
216 Self::Reserved(name)
217 }
218 }
219
220 pub fn name(&self) -> &str {
222 match self {
223 Self::Suffixed(n, _) => n,
224 Self::Reserved(n) => n,
225 }
226 }
227
228 pub fn uuid(&self) -> Option<&ShortUuid> {
231 match self {
232 Self::Suffixed(_, uuid) => Some(uuid),
233 Self::Reserved(_) => None,
234 }
235 }
236}
237
238#[derive(thiserror::Error, Debug)]
240pub enum NameParseError {
241 #[error("invalid name: missing name")]
242 MissingName,
243
244 #[error("invalid name: missing uuid")]
245 MissingUuid,
246
247 #[error(transparent)]
248 InvalidUuid(#[from] <ShortUuid as FromStr>::Err),
249
250 #[error("invalid name: missing separator")]
251 MissingSeparator,
252}
253
254impl FromStr for Name {
255 type Err = NameParseError;
256
257 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
258 if let Some((name, uuid)) = s.split_once(NAME_SUFFIX_DELIMITER) {
259 if name.is_empty() {
260 return Err(NameParseError::MissingName);
261 }
262 if uuid.is_empty() {
263 return Err(NameParseError::MissingName);
264 }
265
266 Ok(Name::new_with_uuid(name.to_string(), Some(uuid.parse()?)))
267 } else {
268 if s.is_empty() {
269 return Err(NameParseError::MissingName);
270 }
271 Ok(Name::new_reserved(s))
272 }
273 }
274}
275
276impl std::fmt::Display for Name {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 match self {
279 Self::Suffixed(n, uuid) => {
280 write!(f, "{}{}", n, NAME_SUFFIX_DELIMITER)?;
281 uuid.format(f, true )
282 }
283 Self::Reserved(n) => write!(f, "{}", n),
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_name_unique() {
294 assert_ne!(Name::new("foo"), Name::new("foo"));
295 let name = Name::new("foo");
296 assert_eq!(name, name);
297 }
298
299 #[test]
300 fn test_name_roundtrip() {
301 let uuid = "111111111111".parse::<ShortUuid>().unwrap();
302 let name = Name::new_with_uuid("foo", Some(uuid));
303 let str = name.to_string();
304 assert_eq!(str, "foo_111111111111");
305 assert_eq!(name, Name::from_str(&str).unwrap());
306 }
307
308 #[test]
309 fn test_name_roundtrip_with_underscore() {
310 let uuid = "_1a2b3c4d5e6f".parse::<ShortUuid>().unwrap();
313 let name = Name::new_with_uuid("foo", Some(uuid));
314 let str = name.to_string();
315 assert_eq!(str, "foo_1a2b3c4d5e6f");
317 assert_eq!(name, Name::from_str(&str).unwrap());
318 }
319
320 #[test]
321 fn test_name_roundtrip_random() {
322 let name = Name::new("foo");
323 assert_eq!(name, Name::from_str(&name.to_string()).unwrap());
324 }
325
326 #[test]
327 fn test_name_roundtrip_reserved() {
328 let name = Name::new_reserved("foo");
329 let str = name.to_string();
330 assert_eq!(str, "foo");
331 assert_eq!(name, Name::from_str(&str).unwrap());
332 }
333
334 #[test]
335 fn test_name_parse() {
336 let name = Name::from_str("foo__1a2b3c4_d5e6f").unwrap();
339 assert_eq!(format!("{}", name), "foo_1a2b3c4d5e6f");
340 }
341}