Skip to main content

monocoque/zmq/
push.rs

1//! PUSH socket implementation.
2//!
3//! PUSH sockets are used in pipeline patterns for distributing tasks.
4
5use monocoque_core::monitor::{SocketEventSender, SocketMonitor, create_monitor};
6use monocoque_core::options::SocketOptions;
7use monocoque_core::rt::{TcpListener, TcpStream};
8use monocoque_zmtp::PushSocket as InternalPush;
9use std::io;
10
11/// PUSH socket for distributing tasks in a pipeline.
12///
13/// PUSH sockets send messages in a round-robin fashion to connected PULL sockets.
14pub struct PushSocket<S = TcpStream>
15where
16    S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
17{
18    inner: InternalPush<S>,
19    monitor: Option<SocketEventSender>,
20}
21
22impl PushSocket<TcpStream> {
23    /// Bind to `addr`, accept one connection, and return a ready PUSH socket.
24    ///
25    /// Returns the `TcpListener` so the caller can accept further PULL connections.
26    ///
27    /// # Example
28    ///
29    /// ```rust,no_run
30    /// use monocoque::zmq::PushSocket;
31    /// use bytes::Bytes;
32    ///
33    /// # async fn example() -> std::io::Result<()> {
34    /// let (_listener, mut socket) = PushSocket::bind("127.0.0.1:5555").await?;
35    /// socket.send(vec![Bytes::from("task")]).await?;
36    /// # Ok(())
37    /// # }
38    /// ```
39    pub async fn bind(
40        addr: impl monocoque_core::rt::ToSocketAddrs,
41    ) -> io::Result<(TcpListener, Self)> {
42        let listener = TcpListener::bind(addr).await?;
43        let (stream, _) = listener.accept().await?;
44        let socket = Self::from_tcp(stream).await?;
45        Ok((listener, socket))
46    }
47
48    /// Connect to a PULL socket at `addr`.
49    ///
50    /// # Example
51    ///
52    /// ```rust,no_run
53    /// use monocoque::zmq::PushSocket;
54    /// use bytes::Bytes;
55    ///
56    /// # async fn example() -> std::io::Result<()> {
57    /// let mut socket = PushSocket::connect("127.0.0.1:5555").await?;
58    /// socket.send(vec![Bytes::from("task")]).await?;
59    /// # Ok(())
60    /// # }
61    /// ```
62    pub async fn connect(addr: impl monocoque_core::rt::ToSocketAddrs) -> io::Result<Self> {
63        Ok(Self {
64            inner: InternalPush::connect(addr).await?,
65            monitor: None,
66        })
67    }
68
69    /// Connect with custom options, storing the endpoint for automatic reconnection.
70    pub async fn connect_with_options(
71        addr: impl monocoque_core::rt::ToSocketAddrs,
72        options: SocketOptions,
73    ) -> io::Result<Self> {
74        Ok(Self {
75            inner: InternalPush::connect_with_options(addr, options).await?,
76            monitor: None,
77        })
78    }
79
80    /// Check if the socket is currently connected.
81    #[inline]
82    pub fn is_connected(&self) -> bool {
83        self.inner.is_connected()
84    }
85
86    /// Try to reconnect to the stored endpoint.
87    pub async fn try_reconnect(&mut self) -> io::Result<()> {
88        self.inner.try_reconnect().await
89    }
90
91    /// Send with automatic reconnection on network error.
92    pub async fn send_with_reconnect(&mut self, msg: Vec<bytes::Bytes>) -> io::Result<()> {
93        self.inner.send_with_reconnect(msg).await
94    }
95
96    /// Create a PUSH socket from a TCP stream.
97    pub async fn from_tcp(stream: TcpStream) -> io::Result<Self> {
98        Ok(Self {
99            inner: InternalPush::from_tcp(stream).await?,
100            monitor: None,
101        })
102    }
103
104    /// Create a PUSH socket from a TCP stream with custom options.
105    pub async fn from_tcp_with_options(
106        stream: TcpStream,
107        options: SocketOptions,
108    ) -> io::Result<Self> {
109        Ok(Self {
110            inner: InternalPush::from_tcp_with_options(stream, options).await?,
111            monitor: None,
112        })
113    }
114}
115
116impl<S> PushSocket<S>
117where
118    S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
119{
120    /// Create a PUSH socket from any stream.
121    pub async fn new(stream: S) -> io::Result<Self> {
122        Ok(Self {
123            inner: InternalPush::new(stream).await?,
124            monitor: None,
125        })
126    }
127
128    /// Create a PUSH socket from any stream with custom options.
129    pub async fn with_options(stream: S, options: SocketOptions) -> io::Result<Self> {
130        Ok(Self {
131            inner: InternalPush::with_options(stream, options).await?,
132            monitor: None,
133        })
134    }
135
136    /// Send a message.
137    ///
138    /// By default each call writes to the kernel immediately (eager mode). For
139    /// throughput-bound pipelines, enable write coalescing via
140    /// [`SocketOptions::with_write_coalescing`] and call [`flush`](Self::flush) after
141    /// the last send in each burst. See `docs/performance.md` for measured numbers and
142    /// an explanation of when each mode is appropriate.
143    pub async fn send(&mut self, msg: Vec<bytes::Bytes>) -> io::Result<()> {
144        self.inner.send(msg).await
145    }
146
147    /// Send a single-frame message without allocating a multipart `Vec`.
148    ///
149    /// Equivalent to `send(vec![frame])`, but avoids the per-message container
150    /// allocation in single-frame hot paths.
151    pub async fn send_one(&mut self, frame: bytes::Bytes) -> io::Result<()> {
152        self.inner.send_one(frame).await
153    }
154
155    /// Flush any messages still buffered by write coalescing.
156    ///
157    /// Required after the last `send()` in a burst when `write_coalescing` is enabled.
158    /// In coalesced mode, bytes accumulate in a userspace buffer and are not guaranteed
159    /// to reach the peer until this is called (or the 64 KB threshold fills naturally).
160    /// Has no effect in eager mode (the default).
161    pub async fn flush(&mut self) -> io::Result<()> {
162        self.inner.flush().await
163    }
164
165    /// Send a batch of messages in a single kernel write.
166    ///
167    /// Encodes all messages into the send buffer and flushes once, regardless of
168    /// whether `write_coalescing` is enabled. Use this when you have a collection
169    /// of messages ready to send and want to minimize syscall count without managing
170    /// `flush()` calls manually:
171    ///
172    /// ```rust,no_run
173    /// # async fn example(push: &mut monocoque::zmq::PushSocket) -> std::io::Result<()> {
174    /// use bytes::Bytes;
175    /// let batch: Vec<Vec<Bytes>> = (0..100)
176    ///     .map(|i| vec![Bytes::from(format!("msg-{i}").into_bytes())])
177    ///     .collect();
178    /// push.send_batch(batch).await?;
179    /// # Ok(())
180    /// # }
181    /// ```
182    ///
183    /// Returns the number of messages sent.
184    pub async fn send_batch<I>(&mut self, msgs: I) -> io::Result<usize>
185    where
186        I: IntoIterator<Item = Vec<bytes::Bytes>>,
187    {
188        self.inner.send_batch(msgs).await
189    }
190
191    /// Enable monitoring for this socket.
192    pub fn monitor(&mut self) -> SocketMonitor {
193        let (sender, receiver) = create_monitor();
194        self.monitor = Some(sender);
195        receiver
196    }
197
198    /// Get a mutable reference to this socket's options.
199    #[inline]
200    pub fn options_mut(&mut self) -> &mut SocketOptions {
201        self.inner.options_mut()
202    }
203}
204
205#[cfg(unix)]
206impl PushSocket<monocoque_core::rt::UnixStream> {
207    /// Create a PUSH socket from a Unix domain socket stream (IPC).
208    pub async fn from_unix_stream(stream: monocoque_core::rt::UnixStream) -> io::Result<Self> {
209        Ok(Self {
210            inner: InternalPush::new(stream).await?,
211            monitor: None,
212        })
213    }
214
215    /// Create a PUSH socket from a Unix domain socket stream with custom options.
216    pub async fn from_unix_stream_with_options(
217        stream: monocoque_core::rt::UnixStream,
218        options: SocketOptions,
219    ) -> io::Result<Self> {
220        Ok(Self {
221            inner: InternalPush::with_options(stream, options).await?,
222            monitor: None,
223        })
224    }
225}