Skip to main content

dcp/multiplex/
pipeline.rs

1//! Request pipelining for DCP client.
2//!
3//! Provides pipelined request handling for improved throughput.
4
5use bytes::Bytes;
6use std::collections::HashMap;
7use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::Arc;
9use tokio::sync::{mpsc, oneshot, Mutex, RwLock};
10
11use super::connection::{MultiplexError, MultiplexedConnection};
12
13/// Pipelined request
14pub struct PipelinedRequest {
15    /// Request ID
16    pub id: u64,
17    /// Stream ID
18    pub stream_id: u16,
19    /// Request data
20    pub data: Bytes,
21    /// Response channel
22    pub response_tx: oneshot::Sender<Result<Bytes, MultiplexError>>,
23}
24
25/// Pipelined client for request pipelining
26pub struct PipelinedClient {
27    /// Underlying multiplexed connection
28    connection: Arc<MultiplexedConnection>,
29    /// Request ID counter
30    request_id: AtomicU64,
31    /// Pending requests awaiting responses
32    pending: Arc<RwLock<HashMap<u64, PendingRequest>>>,
33    /// Maximum concurrent requests
34    max_in_flight: usize,
35    /// Current in-flight count
36    in_flight: Arc<std::sync::atomic::AtomicUsize>,
37}
38
39/// Pending request state
40struct PendingRequest {
41    stream_id: u16,
42    response_tx: oneshot::Sender<Result<Bytes, MultiplexError>>,
43}
44
45impl PipelinedClient {
46    /// Create a new pipelined client
47    pub fn new(connection: Arc<MultiplexedConnection>, max_in_flight: usize) -> Self {
48        Self {
49            connection,
50            request_id: AtomicU64::new(1),
51            pending: Arc::new(RwLock::new(HashMap::new())),
52            max_in_flight,
53            in_flight: Arc::new(std::sync::atomic::AtomicUsize::new(0)),
54        }
55    }
56
57    /// Get the underlying connection
58    pub fn connection(&self) -> &Arc<MultiplexedConnection> {
59        &self.connection
60    }
61
62    /// Get current in-flight request count
63    pub fn in_flight_count(&self) -> usize {
64        self.in_flight.load(Ordering::Relaxed)
65    }
66
67    /// Check if we can send more requests
68    pub fn can_send(&self) -> bool {
69        self.in_flight_count() < self.max_in_flight
70    }
71
72    /// Send a pipelined request
73    /// Returns a future that resolves when the response is received
74    pub async fn send(
75        &self,
76        data: Bytes,
77    ) -> Result<oneshot::Receiver<Result<Bytes, MultiplexError>>, MultiplexError> {
78        // Check in-flight limit
79        if !self.can_send() {
80            return Err(MultiplexError::SendBufferFull);
81        }
82
83        // Open a stream for this request
84        let stream_id = self.connection.open_stream().await?;
85
86        // Generate request ID
87        let request_id = self.request_id.fetch_add(1, Ordering::SeqCst);
88
89        // Create response channel
90        let (response_tx, response_rx) = oneshot::channel();
91
92        // Register pending request
93        {
94            let mut pending = self.pending.write().await;
95            pending.insert(
96                request_id,
97                PendingRequest {
98                    stream_id,
99                    response_tx,
100                },
101            );
102        }
103
104        // Increment in-flight counter
105        self.in_flight.fetch_add(1, Ordering::AcqRel);
106
107        // Send the request
108        if let Err(error) = self.connection.send(stream_id, data).await {
109            self.pending.write().await.remove(&request_id);
110            self.in_flight.fetch_sub(1, Ordering::AcqRel);
111            self.connection.rollback_stream_open(stream_id).await;
112            return Err(error);
113        }
114
115        Ok(response_rx)
116    }
117
118    /// Send multiple requests in a batch
119    pub async fn send_batch(
120        &self,
121        requests: Vec<Bytes>,
122    ) -> Result<Vec<oneshot::Receiver<Result<Bytes, MultiplexError>>>, MultiplexError> {
123        let mut receivers = Vec::with_capacity(requests.len());
124
125        for data in requests {
126            let rx = self.send(data).await?;
127            receivers.push(rx);
128        }
129
130        Ok(receivers)
131    }
132
133    /// Process a response for a stream
134    pub async fn process_response(
135        &self,
136        stream_id: u16,
137        data: Bytes,
138    ) -> Result<(), MultiplexError> {
139        // Find the pending request for this stream
140        let pending_request = {
141            let mut pending = self.pending.write().await;
142            let mut found_id = None;
143            for (id, req) in pending.iter() {
144                if req.stream_id == stream_id {
145                    found_id = Some(*id);
146                    break;
147                }
148            }
149            found_id.and_then(|id| pending.remove(&id))
150        };
151
152        if let Some(req) = pending_request {
153            // Decrement in-flight counter
154            self.in_flight.fetch_sub(1, Ordering::AcqRel);
155
156            // Send response
157            let _ = req.response_tx.send(Ok(data));
158
159            // Close the stream
160            self.connection.close_stream(stream_id).await?;
161        }
162
163        Ok(())
164    }
165
166    /// Process an error for a stream
167    pub async fn process_error(
168        &self,
169        stream_id: u16,
170        error: MultiplexError,
171    ) -> Result<(), MultiplexError> {
172        // Find the pending request for this stream
173        let pending_request = {
174            let mut pending = self.pending.write().await;
175            let mut found_id = None;
176            for (id, req) in pending.iter() {
177                if req.stream_id == stream_id {
178                    found_id = Some(*id);
179                    break;
180                }
181            }
182            found_id.and_then(|id| pending.remove(&id))
183        };
184
185        if let Some(req) = pending_request {
186            // Decrement in-flight counter
187            self.in_flight.fetch_sub(1, Ordering::AcqRel);
188
189            // Send error
190            let _ = req.response_tx.send(Err(error));
191        }
192
193        Ok(())
194    }
195
196    /// Cancel all pending requests
197    pub async fn cancel_all(&self) {
198        let mut pending = self.pending.write().await;
199        for (_, req) in pending.drain() {
200            let _ = req.response_tx.send(Err(MultiplexError::ConnectionClosed));
201        }
202        self.in_flight.store(0, Ordering::Release);
203    }
204
205    /// Get pending request count
206    pub async fn pending_count(&self) -> usize {
207        self.pending.read().await.len()
208    }
209}
210
211/// Request pipeline for managing multiple concurrent requests
212pub struct RequestPipeline {
213    /// Pipelined client
214    client: Arc<PipelinedClient>,
215    /// Request sender channel
216    request_tx: mpsc::Sender<PipelinedRequest>,
217    /// Request receiver channel
218    request_rx: Mutex<mpsc::Receiver<PipelinedRequest>>,
219}
220
221impl RequestPipeline {
222    /// Create a new request pipeline
223    pub fn new(
224        connection: Arc<MultiplexedConnection>,
225        max_in_flight: usize,
226        buffer_size: usize,
227    ) -> Self {
228        let (request_tx, request_rx) = mpsc::channel(buffer_size);
229        let client = Arc::new(PipelinedClient::new(connection, max_in_flight));
230
231        Self {
232            client,
233            request_tx,
234            request_rx: Mutex::new(request_rx),
235        }
236    }
237
238    /// Get the pipelined client
239    pub fn client(&self) -> &Arc<PipelinedClient> {
240        &self.client
241    }
242
243    /// Submit a request to the pipeline
244    pub async fn submit(
245        &self,
246        data: Bytes,
247    ) -> Result<oneshot::Receiver<Result<Bytes, MultiplexError>>, MultiplexError> {
248        self.client.send(data).await
249    }
250
251    /// Process incoming responses
252    /// This should be called in a loop to handle responses
253    pub async fn process_incoming(
254        &self,
255        stream_id: u16,
256        data: Bytes,
257    ) -> Result<(), MultiplexError> {
258        self.client.process_response(stream_id, data).await
259    }
260
261    /// Get current in-flight count
262    pub fn in_flight_count(&self) -> usize {
263        self.client.in_flight_count()
264    }
265
266    /// Check if pipeline can accept more requests
267    pub fn can_accept(&self) -> bool {
268        self.client.can_send()
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[tokio::test]
277    async fn test_pipelined_client_creation() {
278        let conn = Arc::new(MultiplexedConnection::new());
279        let client = PipelinedClient::new(conn, 10);
280
281        assert_eq!(client.in_flight_count(), 0);
282        assert!(client.can_send());
283    }
284
285    #[tokio::test]
286    async fn test_pipelined_send() {
287        let conn = Arc::new(MultiplexedConnection::new());
288        let client = PipelinedClient::new(conn.clone(), 10);
289
290        let rx = client.send(Bytes::from("request1")).await.unwrap();
291        assert_eq!(client.in_flight_count(), 1);
292
293        // Simulate response
294        let streams = conn.active_streams().await;
295        assert!(!streams.is_empty());
296
297        let stream_id = streams[0];
298        client
299            .process_response(stream_id, Bytes::from("response1"))
300            .await
301            .unwrap();
302
303        // Check response
304        let result = rx.await.unwrap();
305        assert_eq!(result.unwrap(), Bytes::from("response1"));
306        assert_eq!(client.in_flight_count(), 0);
307    }
308
309    #[tokio::test]
310    async fn test_pipelined_batch() {
311        let conn = Arc::new(MultiplexedConnection::new());
312        let client = PipelinedClient::new(conn.clone(), 10);
313
314        let requests = vec![
315            Bytes::from("req1"),
316            Bytes::from("req2"),
317            Bytes::from("req3"),
318        ];
319
320        let receivers = client.send_batch(requests).await.unwrap();
321        assert_eq!(receivers.len(), 3);
322        assert_eq!(client.in_flight_count(), 3);
323    }
324
325    #[tokio::test]
326    async fn test_pipelined_max_in_flight() {
327        let conn = Arc::new(MultiplexedConnection::new());
328        let client = PipelinedClient::new(conn, 2);
329
330        // Send two requests
331        let _rx1 = client.send(Bytes::from("req1")).await.unwrap();
332        let _rx2 = client.send(Bytes::from("req2")).await.unwrap();
333
334        assert_eq!(client.in_flight_count(), 2);
335        assert!(!client.can_send());
336
337        // Third should fail
338        let result = client.send(Bytes::from("req3")).await;
339        assert!(matches!(result, Err(MultiplexError::SendBufferFull)));
340    }
341
342    #[tokio::test]
343    async fn test_pipelined_cancel_all() {
344        let conn = Arc::new(MultiplexedConnection::new());
345        let client = PipelinedClient::new(conn, 10);
346
347        let rx1 = client.send(Bytes::from("req1")).await.unwrap();
348        let rx2 = client.send(Bytes::from("req2")).await.unwrap();
349
350        client.cancel_all().await;
351
352        assert_eq!(client.in_flight_count(), 0);
353
354        // Receivers should get errors
355        let result1 = rx1.await.unwrap();
356        assert!(matches!(result1, Err(MultiplexError::ConnectionClosed)));
357
358        let result2 = rx2.await.unwrap();
359        assert!(matches!(result2, Err(MultiplexError::ConnectionClosed)));
360    }
361
362    #[tokio::test]
363    async fn test_request_pipeline() {
364        let conn = Arc::new(MultiplexedConnection::new());
365        let pipeline = RequestPipeline::new(conn, 10, 100);
366
367        assert!(pipeline.can_accept());
368        assert_eq!(pipeline.in_flight_count(), 0);
369    }
370}