hydro_lang/networking/mod.rs
1//! Types for configuring network channels with serialization formats, transports, etc.
2
3use std::marker::PhantomData;
4
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7
8use crate::live_collections::stream::networking::{deserialize_bincode, serialize_bincode};
9use crate::live_collections::stream::{NoOrder, Ordering, TotalOrder};
10use crate::location::cluster::{Consistency, EventualConsistency, NoConsistency};
11use crate::nondet::NonDet;
12
13#[sealed::sealed]
14trait SerKind<T: ?Sized> {
15 fn serialize_thunk(is_demux: bool) -> syn::Expr;
16
17 fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr;
18
19 /// Whether this serialization backend leaves serialization to code outside of Hydro (see
20 /// [`Embedded`]). When `true`, [`Self::serialize_thunk`] and [`Self::deserialize_thunk`] are
21 /// never called; the raw element type flows across the channel unserialized.
22 fn is_embedded() -> bool {
23 false
24 }
25}
26
27/// Serialize items using the [`bincode`] crate.
28pub enum Bincode {}
29
30#[sealed::sealed]
31impl<T: Serialize + DeserializeOwned> SerKind<T> for Bincode {
32 fn serialize_thunk(is_demux: bool) -> syn::Expr {
33 serialize_bincode::<T>(is_demux)
34 }
35
36 fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr {
37 deserialize_bincode::<T>(tagged)
38 }
39}
40
41/// Leaves serialization of items to code outside of Hydro. Every [`EMBEDDED`] channel uses it.
42///
43/// This serialization backend is only supported by the embedded deployment backend (it will panic
44/// on all other backends). The generated network channel exposes the raw element type `T` to the
45/// developer (rather than serialized bytes), so they can perform custom serialization logic outside
46/// of the Hydro program for that channel.
47pub enum Embedded {}
48
49#[sealed::sealed]
50impl<T> SerKind<T> for Embedded {
51 fn serialize_thunk(_is_demux: bool) -> syn::Expr {
52 unreachable!("embedded serialization does not use a serialize thunk")
53 }
54
55 fn deserialize_thunk(_tagged: Option<&syn::Type>) -> syn::Expr {
56 unreachable!("embedded serialization does not use a deserialize thunk")
57 }
58
59 fn is_embedded() -> bool {
60 true
61 }
62}
63
64/// An unconfigured serialization backend.
65pub enum NoSer {}
66
67/// A transport backend for network channels.
68#[sealed::sealed]
69#[diagnostic::on_unimplemented(
70 message = "this network config requires a failure policy",
71 note = "for example, `EMBEDDED.fail_stop()` delivers, per sender, an in-order prefix of the messages it sent"
72)]
73pub trait TransportKind {
74 /// The ordering guarantee provided by this transport.
75 type OrderingGuarantee: Ordering;
76
77 /// The consistency guarantee this transport can preserve for replicated outputs
78 /// (see [`NetworkFor::ConsistencyGuarantee`]).
79 type ConsistencyGuarantee: Consistency;
80
81 /// Returns the [`NetworkingInfo`] describing this transport's configuration.
82 fn networking_info() -> NetworkingInfo;
83}
84
85#[sealed::sealed]
86#[diagnostic::on_unimplemented(
87 message = "the TCP transport requires a failure policy",
88 note = "for example, `TCP.fail_stop()` stops delivering messages after a failed connection"
89)]
90/// A failure policy for TCP connections, determining how the transport handles
91/// connection failures and what ordering guarantees the output stream provides.
92pub trait TcpFailPolicy {
93 /// The ordering guarantee provided by this failure policy.
94 type OrderingGuarantee: Ordering;
95
96 /// The consistency guarantee this failure policy can preserve for replicated outputs
97 /// (see [`NetworkFor::ConsistencyGuarantee`]).
98 type ConsistencyGuarantee: Consistency;
99
100 /// Returns the [`TcpFault`] variant for this failure policy.
101 fn tcp_fault() -> TcpFault;
102}
103
104/// A TCP failure policy that stops sending messages after a failed connection.
105pub enum FailStop {}
106#[sealed::sealed]
107impl TcpFailPolicy for FailStop {
108 type OrderingGuarantee = TotalOrder;
109
110 // A failed connection stops *all* future deliveries to that recipient, which models the
111 // recipient as having failed. Consistency guarantees only apply to live members, so
112 // eventual consistency of replicated outputs is preserved.
113 type ConsistencyGuarantee = EventualConsistency;
114
115 fn tcp_fault() -> TcpFault {
116 TcpFault::FailStop
117 }
118}
119
120/// A failure policy that allows messages to be lost.
121pub enum Lossy {}
122#[sealed::sealed]
123impl TcpFailPolicy for Lossy {
124 type OrderingGuarantee = TotalOrder;
125
126 // A lossy channel can drop an arbitrary message for one recipient while continuing to
127 // deliver later messages, so replicated outputs can diverge across members forever.
128 type ConsistencyGuarantee = NoConsistency;
129
130 fn tcp_fault() -> TcpFault {
131 TcpFault::Lossy
132 }
133}
134
135/// A failure policy that treats dropped messages as indefinitely delayed.
136///
137/// Unlike [`Lossy`], this does not require a [`NonDet`] annotation because the output
138/// stream is always lower in the partial order than the ideal stream (dropped messages
139/// are modeled as infinite delays). The tradeoff is that the output has [`NoOrder`]
140/// guarantees, imposing stricter conditions on downstream consumers.
141///
142/// When using this mode in the Hydro simulator, you must call
143/// [`.test_safety_only()`](crate::sim::flow::SimFlow::test_safety_only): the simulator
144/// will not actually drop packets—it delays "dropped" messages until the end of the
145/// execution, which catches safety bugs but cannot test liveness.
146pub enum LossyDelayedForever {}
147#[sealed::sealed]
148impl TcpFailPolicy for LossyDelayedForever {
149 type OrderingGuarantee = NoOrder;
150
151 // Dropped messages are modeled as indefinitely delayed, so the output on each member is
152 // always a lower bound of the ideal stream that is eventually delivered in full; replicated
153 // outputs therefore remain eventually consistent.
154 type ConsistencyGuarantee = EventualConsistency;
155
156 fn tcp_fault() -> TcpFault {
157 TcpFault::LossyDelayedForever
158 }
159}
160
161#[sealed::sealed]
162#[diagnostic::on_unimplemented(
163 message = "the UDP transport requires a failure policy",
164 note = "for example, `UDP.lossy_delayed_forever()` treats dropped messages as indefinitely delayed"
165)]
166/// A failure policy for UDP channels, determining how the transport handles
167/// message loss. Because UDP provides no ordering guarantees, all policies
168/// produce [`NoOrder`] output streams, and there is no `fail_stop` option
169/// (UDP is connectionless, so there is no connection to fail).
170pub trait UdpFailPolicy {
171 /// The consistency guarantee this failure policy can preserve for replicated outputs
172 /// (see [`NetworkFor::ConsistencyGuarantee`]).
173 type ConsistencyGuarantee: Consistency;
174
175 /// Returns the [`UdpFault`] variant for this failure policy.
176 fn udp_fault() -> UdpFault;
177}
178
179#[sealed::sealed]
180impl UdpFailPolicy for Lossy {
181 // A lossy channel can drop an arbitrary message for one recipient while continuing to
182 // deliver later messages, so replicated outputs can diverge across members forever.
183 type ConsistencyGuarantee = NoConsistency;
184
185 fn udp_fault() -> UdpFault {
186 UdpFault::Lossy
187 }
188}
189
190#[sealed::sealed]
191impl UdpFailPolicy for LossyDelayedForever {
192 // Dropped messages are modeled as indefinitely delayed, so the output on each member is
193 // always a lower bound of the ideal stream that is eventually delivered in full; replicated
194 // outputs therefore remain eventually consistent.
195 type ConsistencyGuarantee = EventualConsistency;
196
197 fn udp_fault() -> UdpFault {
198 UdpFault::LossyDelayedForever
199 }
200}
201
202/// Send items across a length-delimited TCP channel.
203pub struct Tcp<F> {
204 _phantom: PhantomData<F>,
205}
206
207#[sealed::sealed]
208impl<F: TcpFailPolicy> TransportKind for Tcp<F> {
209 type OrderingGuarantee = F::OrderingGuarantee;
210
211 type ConsistencyGuarantee = F::ConsistencyGuarantee;
212
213 fn networking_info() -> NetworkingInfo {
214 NetworkingInfo::Tcp {
215 fault: F::tcp_fault(),
216 }
217 }
218}
219
220/// Items cross a channel that the host of an embedded program implements however it likes (a
221/// network connection, an in-memory queue, ...), keeping the guarantees of the policy `F`.
222pub struct EmbeddedTransport<F> {
223 _phantom: PhantomData<F>,
224}
225
226/// An [`EMBEDDED`] channel names no transport, so its [`NetworkingInfo`] is just the guarantees
227/// its received stream is declared with, which the host's channel must keep.
228fn embedded_info<K: TransportKind>() -> NetworkingInfo {
229 NetworkingInfo::Embedded {
230 ordering: K::OrderingGuarantee::ORDERING_KIND,
231 consistency: K::ConsistencyGuarantee::consistency(),
232 }
233}
234
235/// For each sender, an in-order prefix of the messages it sent.
236#[sealed::sealed]
237impl TransportKind for EmbeddedTransport<FailStop> {
238 type OrderingGuarantee = TotalOrder;
239
240 type ConsistencyGuarantee = EventualConsistency;
241
242 fn networking_info() -> NetworkingInfo {
243 embedded_info::<Self>()
244 }
245}
246
247/// Each sender's messages in order, any of which may be dropped.
248#[sealed::sealed]
249impl TransportKind for EmbeddedTransport<Lossy> {
250 type OrderingGuarantee = TotalOrder;
251
252 type ConsistencyGuarantee = NoConsistency;
253
254 fn networking_info() -> NetworkingInfo {
255 embedded_info::<Self>()
256 }
257}
258
259/// Messages in any order; a message never delivered counts as delayed forever.
260#[sealed::sealed]
261impl TransportKind for EmbeddedTransport<LossyDelayedForever> {
262 type OrderingGuarantee = NoOrder;
263
264 type ConsistencyGuarantee = EventualConsistency;
265
266 fn networking_info() -> NetworkingInfo {
267 embedded_info::<Self>()
268 }
269}
270
271/// Send items across a UDP channel, which does not guarantee delivery or ordering.
272pub struct Udp<F> {
273 _phantom: PhantomData<F>,
274}
275
276#[sealed::sealed]
277impl<F: UdpFailPolicy> TransportKind for Udp<F> {
278 type OrderingGuarantee = NoOrder;
279
280 type ConsistencyGuarantee = F::ConsistencyGuarantee;
281
282 fn networking_info() -> NetworkingInfo {
283 NetworkingInfo::Udp {
284 fault: F::udp_fault(),
285 }
286 }
287}
288
289/// A networking backend implementation that supports items of type `T`.
290#[sealed::sealed]
291pub trait NetworkFor<T: ?Sized> {
292 /// The ordering guarantee provided by this network configuration.
293 /// When combined with an input stream's ordering `O`, the output ordering
294 /// will be `<O as MinOrder<Self::OrderingGuarantee>>::Min`.
295 type OrderingGuarantee: Ordering;
296
297 /// The consistency guarantee this network configuration can preserve when the same data is
298 /// replicated to several recipients (e.g. via
299 /// [`Stream::broadcast_closed`](crate::live_collections::stream::Stream::broadcast_closed)).
300 ///
301 /// Failure policies that guarantee each recipient eventually observes the full stream of
302 /// sent messages, or that model failures as the recipient stopping entirely (such as
303 /// `fail_stop` or `lossy_delayed_forever`), preserve
304 /// [`EventualConsistency`]. Plain `lossy`
305 /// channels can silently drop individual messages for some recipients while others receive
306 /// them, so replicated outputs can permanently diverge and only
307 /// [`NoConsistency`] is guaranteed.
308 type ConsistencyGuarantee: Consistency;
309
310 /// Generates serialization logic for sending `T`.
311 fn serialize_thunk(is_demux: bool) -> syn::Expr;
312
313 /// Generates deserialization logic for receiving `T`.
314 fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr;
315
316 /// Whether this network channel leaves serialization to code outside of Hydro (an
317 /// [`EMBEDDED`] channel). When `true`, [`Self::serialize_thunk`] and
318 /// [`Self::deserialize_thunk`] are never called; the raw element type flows across the
319 /// channel unserialized.
320 fn is_embedded() -> bool {
321 false
322 }
323
324 /// Returns the optional name of the network channel.
325 fn name(&self) -> Option<&str>;
326
327 /// Returns the [`NetworkingInfo`] describing this network channel's transport and fault model.
328 fn networking_info() -> NetworkingInfo;
329}
330
331/// The fault model for a TCP connection.
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
333pub enum TcpFault {
334 /// Stops sending messages after a failed connection.
335 FailStop,
336 /// Messages may be lost (e.g. due to network partitions).
337 Lossy,
338 /// Dropped messages are treated as indefinitely delayed with no ordering guarantee.
339 LossyDelayedForever,
340}
341
342/// The fault model for a UDP channel.
343///
344/// UDP is connectionless and never guarantees delivery, so there is no
345/// `FailStop` variant — messages can always be dropped.
346#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
347pub enum UdpFault {
348 /// Messages may be lost (e.g. due to network partitions or congestion).
349 Lossy,
350 /// Dropped messages are treated as indefinitely delayed.
351 LossyDelayedForever,
352}
353
354/// Describes the networking configuration for a network channel at the IR level.
355#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
356pub enum NetworkingInfo {
357 /// A TCP-based network channel with a specific fault model.
358 Tcp {
359 /// The fault model for this TCP connection.
360 fault: TcpFault,
361 },
362 /// A UDP-based network channel with a specific fault model.
363 Udp {
364 /// The fault model for this UDP channel.
365 fault: UdpFault,
366 },
367 /// A channel implemented by the host of an embedded program ([`EMBEDDED`]), described by
368 /// the guarantees its received stream is declared with, which the host's channel must keep.
369 Embedded {
370 /// The order the channel must deliver each sender's messages in: `TotalOrder` (the order
371 /// they were sent) or `NoOrder` (any).
372 ordering: crate::compile::ir::StreamOrder,
373 /// Whether the channel may lose a message and deliver later ones as if nothing were
374 /// missing (`NoConsistency`), or must not (`EventualConsistency`).
375 consistency: crate::location::dynamic::ClusterConsistency,
376 },
377}
378
379/// A network channel configuration with `T` as transport backend and `S` as the serialization
380/// backend.
381pub struct NetworkingConfig<Tr: ?Sized, S: ?Sized, Name = ()> {
382 name: Option<Name>,
383 _phantom: (PhantomData<Tr>, PhantomData<S>),
384}
385
386impl<Tr: ?Sized, S: ?Sized> NetworkingConfig<Tr, S> {
387 /// Names the network channel and enables stable communication across multiple service versions.
388 pub fn name(self, name: impl Into<String>) -> NetworkingConfig<Tr, S, String> {
389 NetworkingConfig {
390 name: Some(name.into()),
391 _phantom: (PhantomData, PhantomData),
392 }
393 }
394}
395
396impl<Tr: ?Sized, N> NetworkingConfig<Tr, NoSer, N> {
397 /// Configures the network channel to use [`bincode`] to serialize items.
398 pub const fn bincode(mut self) -> NetworkingConfig<Tr, Bincode, N> {
399 let taken_name = self.name.take();
400 std::mem::forget(self); // nothing else is stored
401 NetworkingConfig {
402 name: taken_name,
403 _phantom: (PhantomData, PhantomData),
404 }
405 }
406}
407
408impl<S: ?Sized> NetworkingConfig<Tcp<()>, S> {
409 /// Configures the TCP transport to stop sending messages after a failed connection.
410 ///
411 /// Note that the Hydro simulator will not simulate connection failures that impact the
412 /// *liveness* of a program. If an output assertion depends on a `fail_stop` channel
413 /// making progress, that channel will not experience a failure that would cause the test to
414 /// block indefinitely. However, any *safety* issues caused by connection failures will still
415 /// be caught, such as a race condition between a failed connection and some other message.
416 #[must_use]
417 pub const fn fail_stop(self) -> NetworkingConfig<Tcp<FailStop>, S> {
418 NetworkingConfig {
419 name: self.name,
420 _phantom: (PhantomData, PhantomData),
421 }
422 }
423
424 /// Configures the TCP transport to allow messages to be lost.
425 ///
426 /// This is appropriate for networks where messages may be dropped, such as when
427 /// running under a Maelstrom partition nemesis. Unlike `fail_stop`, which guarantees
428 /// a prefix of messages is delivered, `lossy` makes no such guarantee.
429 ///
430 /// # Non-Determinism
431 /// A lossy TCP channel will non-deterministically drop messages during execution.
432 #[must_use]
433 pub const fn lossy(self, nondet: NonDet) -> NetworkingConfig<Tcp<Lossy>, S> {
434 let _ = nondet;
435 NetworkingConfig {
436 name: self.name,
437 _phantom: (PhantomData, PhantomData),
438 }
439 }
440
441 /// Configures the TCP transport to treat dropped messages as indefinitely delayed.
442 ///
443 /// This is appropriate for networks where messages may be dropped, such as when
444 /// running under a Maelstrom partition nemesis. Unlike [`Self::lossy`], this does
445 /// *not* require a [`NonDet`] annotation because the output is always lower in the
446 /// partial order than the ideal stream. However, the output stream will have
447 /// [`NoOrder`] guarantees, imposing stricter conditions on downstream consumers.
448 ///
449 /// Unlike [`Self::lossy`], this mode can easily be simulated in exhaustive mode
450 /// without running into fairness issues.
451 ///
452 /// When using this mode in the Hydro simulator, you must call
453 /// [`.test_safety_only()`](crate::sim::flow::SimFlow::test_safety_only) to opt in:
454 /// the simulator will not actually drop packets—it delays "dropped" messages until
455 /// the end of the execution, which catches safety bugs but cannot test liveness.
456 #[must_use]
457 pub const fn lossy_delayed_forever(self) -> NetworkingConfig<Tcp<LossyDelayedForever>, S> {
458 NetworkingConfig {
459 name: self.name,
460 _phantom: (PhantomData, PhantomData),
461 }
462 }
463}
464
465impl<S: ?Sized> NetworkingConfig<EmbeddedTransport<()>, S> {
466 /// Requires the host's channel to stop delivering messages after a failure, like
467 /// [`TCP.fail_stop()`](NetworkingConfig::fail_stop): for each sender, an in-order prefix of the
468 /// messages it sent.
469 #[must_use]
470 pub const fn fail_stop(self) -> NetworkingConfig<EmbeddedTransport<FailStop>, S> {
471 NetworkingConfig {
472 name: self.name,
473 _phantom: (PhantomData, PhantomData),
474 }
475 }
476
477 /// Allows the host's channel to lose messages while delivering each sender's messages in
478 /// order, like [`TCP.lossy()`](NetworkingConfig::lossy).
479 ///
480 /// # Non-Determinism
481 /// A lossy channel will non-deterministically drop messages during execution.
482 #[must_use]
483 pub const fn lossy(self, nondet: NonDet) -> NetworkingConfig<EmbeddedTransport<Lossy>, S> {
484 let _ = nondet;
485 NetworkingConfig {
486 name: self.name,
487 _phantom: (PhantomData, PhantomData),
488 }
489 }
490
491 /// Allows the host's channel to deliver messages in any order and treats a message that is
492 /// never delivered as indefinitely delayed, like
493 /// [`TCP.lossy_delayed_forever()`](NetworkingConfig::lossy_delayed_forever).
494 #[must_use]
495 pub const fn lossy_delayed_forever(
496 self,
497 ) -> NetworkingConfig<EmbeddedTransport<LossyDelayedForever>, S> {
498 NetworkingConfig {
499 name: self.name,
500 _phantom: (PhantomData, PhantomData),
501 }
502 }
503}
504
505impl<S: ?Sized> NetworkingConfig<Udp<()>, S> {
506 /// Configures the UDP transport to allow messages to be lost.
507 ///
508 /// UDP never guarantees delivery or ordering, so unlike TCP there is no `fail_stop`
509 /// policy — messages may always be dropped and the output stream always has
510 /// [`NoOrder`] guarantees.
511 ///
512 /// # Non-Determinism
513 /// A lossy UDP channel will non-deterministically drop messages during execution.
514 #[must_use]
515 pub const fn lossy(self, nondet: NonDet) -> NetworkingConfig<Udp<Lossy>, S> {
516 let _ = nondet;
517 NetworkingConfig {
518 name: self.name,
519 _phantom: (PhantomData, PhantomData),
520 }
521 }
522
523 /// Configures the UDP transport to treat dropped messages as indefinitely delayed.
524 ///
525 /// UDP never guarantees delivery or ordering, so unlike TCP there is no `fail_stop`
526 /// policy. Unlike [`Self::lossy`], this does *not* require a [`NonDet`] annotation
527 /// because the output is always lower in the partial order than the ideal stream
528 /// (dropped messages are modeled as infinite delays). The output stream has
529 /// [`NoOrder`] guarantees, imposing stricter conditions on downstream consumers.
530 ///
531 /// Unlike [`Self::lossy`], this mode can easily be simulated in exhaustive mode
532 /// without running into fairness issues.
533 ///
534 /// When using this mode in the Hydro simulator, you must call
535 /// [`.test_safety_only()`](crate::sim::flow::SimFlow::test_safety_only) to opt in:
536 /// the simulator will not actually drop packets—it delays "dropped" messages until
537 /// the end of the execution, which catches safety bugs but cannot test liveness.
538 #[must_use]
539 pub const fn lossy_delayed_forever(self) -> NetworkingConfig<Udp<LossyDelayedForever>, S> {
540 NetworkingConfig {
541 name: self.name,
542 _phantom: (PhantomData, PhantomData),
543 }
544 }
545}
546
547#[sealed::sealed]
548impl<Tr, S, T: ?Sized> NetworkFor<T> for NetworkingConfig<Tr, S>
549where
550 Tr: ?Sized + TransportKind,
551 S: ?Sized + SerKind<T>,
552{
553 type OrderingGuarantee = Tr::OrderingGuarantee;
554
555 type ConsistencyGuarantee = Tr::ConsistencyGuarantee;
556
557 fn serialize_thunk(is_demux: bool) -> syn::Expr {
558 S::serialize_thunk(is_demux)
559 }
560
561 fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr {
562 S::deserialize_thunk(tagged)
563 }
564
565 fn is_embedded() -> bool {
566 S::is_embedded()
567 }
568
569 fn name(&self) -> Option<&str> {
570 None
571 }
572
573 fn networking_info() -> NetworkingInfo {
574 Tr::networking_info()
575 }
576}
577
578#[sealed::sealed]
579impl<Tr, S, T: ?Sized> NetworkFor<T> for NetworkingConfig<Tr, S, String>
580where
581 Tr: ?Sized + TransportKind,
582 S: ?Sized + SerKind<T>,
583{
584 type OrderingGuarantee = Tr::OrderingGuarantee;
585
586 type ConsistencyGuarantee = Tr::ConsistencyGuarantee;
587
588 fn serialize_thunk(is_demux: bool) -> syn::Expr {
589 S::serialize_thunk(is_demux)
590 }
591
592 fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr {
593 S::deserialize_thunk(tagged)
594 }
595
596 fn is_embedded() -> bool {
597 S::is_embedded()
598 }
599
600 fn name(&self) -> Option<&str> {
601 self.name.as_deref()
602 }
603
604 fn networking_info() -> NetworkingInfo {
605 Tr::networking_info()
606 }
607}
608
609/// A network channel that uses length-delimited TCP for transport.
610pub const TCP: NetworkingConfig<Tcp<()>, NoSer> = NetworkingConfig {
611 name: None,
612 _phantom: (PhantomData, PhantomData),
613};
614
615/// A network channel implemented by the host of an embedded program, which chooses how messages
616/// travel: a network connection, an in-memory queue, or anything else that delivers them with the
617/// guarantees of the chosen failure policy (for example `EMBEDDED.fail_stop().name("messages")`).
618///
619/// Items cross the channel unserialized: the generated channel ends carry the raw element type,
620/// and the host encodes it if its transport needs to. The program names only the guarantees, not
621/// a transport, so it is only supported by the embedded deployment backend and the Hydro
622/// simulator (which carries the raw values in memory); other backends use [`TCP`] or [`UDP`].
623pub const EMBEDDED: NetworkingConfig<EmbeddedTransport<()>, Embedded> = NetworkingConfig {
624 name: None,
625 _phantom: (PhantomData, PhantomData),
626};
627
628/// A network channel that uses UDP for transport.
629///
630/// Unlike [`TCP`], UDP does not guarantee delivery or ordering, so output streams
631/// always have [`NoOrder`] guarantees. Because UDP is connectionless, there is no
632/// `fail_stop` policy; only [`lossy`](NetworkingConfig::lossy) and
633/// [`lossy_delayed_forever`](NetworkingConfig::lossy_delayed_forever) are available.
634///
635/// # Availability
636/// UDP is **not yet available** in "deploy" deployment mode (via Hydro Deploy,
637/// including Docker and ECS deployments); attempting to deploy a UDP channel there
638/// will panic at compile time. Both UDP modes are available for embedded
639/// deployments (the only production deployment option) and Maelstrom testing. In
640/// the Hydro simulator, only `lossy_delayed_forever` is supported, and it requires
641/// [`.test_safety_only()`](crate::sim::flow::SimFlow::test_safety_only): the
642/// simulator will not actually drop packets—it delays "dropped" messages until the
643/// end of the execution, which catches safety bugs but cannot test liveness.
644pub const UDP: NetworkingConfig<Udp<()>, NoSer> = NetworkingConfig {
645 name: None,
646 _phantom: (PhantomData, PhantomData),
647};