operator/write_dispatcher/
progress.rs1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use api::v1::region::{RegionRequest, region_request::Body};
19use backon::{BackoffBuilder, ExponentialBuilder};
20use common_error::ext::{BoxedError, ErrorExt, RetryHint};
21use common_meta::error::{Error, UnexpectedSnafu};
22use common_meta::peer::PeerRef;
23use common_query::WriteProgress;
24use futures::future;
25use prometheus::Histogram;
26use tokio_util::sync::CancellationToken;
27
28use crate::write_dispatcher::{
29 RoutedWrite, WriteAttempt, WriteAttribution, WriteDispatcher, get_peer, write_region_id,
30};
31
32pub struct TrackedWrite {
35 pub request: RegionRequest,
36 pub owners: Vec<usize>,
37}
38
39struct TrackedBatch {
41 request: RegionRequest,
42 owners: Vec<Vec<usize>>,
43}
44
45impl WriteDispatcher {
46 pub async fn dispatch_tracked(
49 &self,
50 requests: Vec<TrackedWrite>,
51 inputs: usize,
52 histogram: Option<&Histogram>,
53 ) -> Vec<WriteProgress> {
54 let (writes, progress) = self.prepare_tracked(requests, inputs).await;
55 self.execute_tracked(writes, progress, histogram, None)
56 .await
57 }
58
59 pub(crate) async fn prepare_tracked(
61 &self,
62 requests: Vec<TrackedWrite>,
63 inputs: usize,
64 ) -> (Vec<RoutedWrite>, Vec<WriteProgress>) {
65 let mut progress = vec![WriteProgress::default(); inputs];
66 if requests
67 .iter()
68 .any(|request| request.owners.iter().any(|owner| *owner >= inputs))
69 {
70 let error = UnexpectedSnafu {
71 err_msg: "Write owner exceeds original input count",
72 }
73 .build();
74 return (
75 Vec::new(),
76 vec![WriteProgress::failed(BoxedError::new(error)); inputs],
77 );
78 }
79 let regions = match requests
80 .iter()
81 .map(|write| write_region_id(&write.request))
82 .collect::<common_meta::error::Result<Vec<_>>>()
83 {
84 Ok(regions) => regions,
85 Err(error) => {
86 return (
87 Vec::new(),
88 vec![WriteProgress::failed(BoxedError::new(error)); inputs],
89 );
90 }
91 };
92 let peers = match self.resolve_peers(®ions).await {
93 Ok(peers) => peers,
94 Err(error) => {
95 return (
96 Vec::new(),
97 vec![WriteProgress::failed(BoxedError::new(error)); inputs],
98 );
99 }
100 };
101 let mut pending = Vec::with_capacity(requests.len());
102 for (write, region) in requests.into_iter().zip(regions) {
103 match get_peer(&peers, region) {
104 Ok(peer) => pending.push(RoutedWrite {
105 peer,
106 request: Arc::new(write.request),
107 attribution: WriteAttribution::default(),
108 owners: Some(write.owners),
109 }),
110 Err(error) => record_failure(&mut progress, &write.owners, error),
111 }
112 }
113 (pending, progress)
114 }
115
116 pub(crate) async fn execute_tracked(
118 &self,
119 mut pending: Vec<RoutedWrite>,
120 mut progress: Vec<WriteProgress>,
121 histogram: Option<&Histogram>,
122 cancellation: Option<&CancellationToken>,
123 ) -> Vec<WriteProgress> {
124 let mut backoff = self.retry.as_ref().map(|retry| {
125 ExponentialBuilder::default()
126 .with_min_delay(retry.options.initial_backoff)
127 .with_max_delay(retry.options.max_backoff)
128 .with_factor(retry.options.backoff_multiplier)
129 .with_max_times(retry.options.max_retries)
130 .with_jitter()
131 .build()
132 });
133 let mut failures = Vec::new();
134 loop {
135 failures.clear();
136 let attempts = if progress.len() == 1
138 && self.retry.is_some()
139 && pending.len() > 1
140 && pending
141 .iter()
142 .any(|write| matches!(write.request.body, Some(Body::Inserts(_))))
143 {
144 self.send_tracked_batches(pending, histogram).await
145 } else {
146 future::join_all(
147 pending
148 .into_iter()
149 .map(|write| self.send_tracked_write(write, histogram)),
150 )
151 .await
152 };
153 pending = Vec::new();
154 let mut refresh = Vec::new();
155 let mut terminal = false;
156 for (owners, attempt) in attempts {
157 match attempt {
158 WriteAttempt::Success(response) if response.affected_rows == owners.len() => {
159 for run in owners.chunk_by(|a, b| a == b) {
160 progress[run[0]].affected_rows += run.len();
161 }
162 }
163 WriteAttempt::Success(response) => {
164 terminal = true;
165 record_failure(&mut progress, &owners, UnexpectedSnafu {
166 err_msg: format!("Write acknowledged {} rows, expected {}; cannot attribute this acknowledgement", response.affected_rows, owners.len()),
167 }.build());
168 }
169 WriteAttempt::Failed {
170 error,
171 request: Some(write),
172 } => {
173 if error.retry_hint() == RetryHint::RetryAfterRouteRefresh {
174 refresh.push(write);
175 } else {
176 pending.push(write);
177 }
178 failures.push((owners, error));
179 }
180 WriteAttempt::Failed {
181 error,
182 request: None,
183 } => {
184 terminal = true;
185 record_failure(&mut progress, &owners, error);
186 }
187 }
188 }
189 if failures.is_empty() {
190 break;
191 }
192 let delay = (!terminal)
193 .then(|| backoff.as_mut().and_then(Iterator::next))
194 .flatten();
195 let (Some(delay), Some(retry)) = (delay, &self.retry) else {
196 break;
197 };
198 if let Some(cancellation) = cancellation {
199 tokio::select! {
200 biased;
201 _ = cancellation.cancelled() => break,
202 _ = tokio::time::sleep(delay) => {}
203 }
204 } else {
205 tokio::time::sleep(delay).await;
206 }
207 if cancellation.is_some_and(CancellationToken::is_cancelled) {
208 break;
209 }
210 if !refresh.is_empty() {
211 let owners = refresh
212 .iter()
213 .flat_map(|write| write.owners.iter().flatten().copied())
214 .collect::<Vec<_>>();
215 match retry.reroute_failed_writes(refresh).await {
216 Ok(writes) => pending.extend(writes),
217 Err(error) => record_failure(&mut progress, &owners, error),
218 }
219 }
220 if pending.is_empty() || cancellation.is_some_and(CancellationToken::is_cancelled) {
221 break;
222 }
223 }
224 for (owners, error) in failures {
225 record_failure(&mut progress, &owners, error);
226 }
227 progress
228 }
229
230 async fn send_tracked_write(
231 &self,
232 mut write: RoutedWrite,
233 histogram: Option<&Histogram>,
234 ) -> (Vec<usize>, WriteAttempt) {
235 let owners = write.owners.take().unwrap_or_default();
236 let mut attempt = if let Some(retry) = &self.retry {
237 retry.send(write, histogram).await
238 } else {
239 let _timer = histogram.map(Histogram::start_timer);
240 let datanode = self.node_manager.datanode(&write.peer).await;
241 match datanode.handle(write.request).await {
242 Ok(response) => WriteAttempt::Success(response),
243 Err(error) => WriteAttempt::Failed {
244 error,
245 request: None,
246 },
247 }
248 };
249 if let WriteAttempt::Failed {
250 request: Some(write),
251 ..
252 } = &mut attempt
253 {
254 write.owners = Some(owners.clone());
255 }
256 (owners, attempt)
257 }
258
259 async fn send_tracked_batches(
260 &self,
261 writes: Vec<RoutedWrite>,
262 histogram: Option<&Histogram>,
263 ) -> Vec<(Vec<usize>, WriteAttempt)> {
264 let mut batches: HashMap<PeerRef, Vec<TrackedBatch>> = HashMap::new();
265 let mut singles = Vec::new();
266 let mut attempts = Vec::new();
267 for write in writes {
268 if !matches!(write.request.body, Some(Body::Inserts(_))) {
269 singles.push(write);
270 continue;
271 }
272 let owners = write.owners.unwrap_or_default();
273 let mut request = match Arc::try_unwrap(write.request) {
274 Ok(request) => request,
275 Err(_) => {
276 attempts.push((
277 owners,
278 WriteAttempt::Failed {
279 error: UnexpectedSnafu {
280 err_msg: "Region request is still shared before batching",
281 }
282 .build(),
283 request: None,
284 },
285 ));
286 continue;
287 }
288 };
289 let batches = batches.entry(write.peer).or_default();
290 if let Some(batch) = batches
291 .iter_mut()
292 .find(|batch| batch.request.header == request.header)
293 && let (Some(Body::Inserts(existing)), Some(Body::Inserts(next))) =
294 (&mut batch.request.body, &mut request.body)
295 {
296 existing.requests.append(&mut next.requests);
297 batch.owners.push(owners);
298 } else {
299 batches.push(TrackedBatch {
300 request,
301 owners: vec![owners],
302 });
303 }
304 }
305 let batches = future::join_all(batches.into_iter().flat_map(|(peer, batches)| {
306 batches.into_iter().map(move |batch| {
307 self.send_batch(
308 Arc::clone(&peer),
309 batch.request,
310 batch.owners,
311 histogram,
312 None,
313 )
314 })
315 }));
316 let singles = future::join_all(
317 singles
318 .into_iter()
319 .map(|write| self.send_tracked_write(write, histogram)),
320 );
321 let (batches, singles) = future::join(batches, singles).await;
322 attempts.extend(batches.into_iter().flatten());
323 attempts.extend(singles);
324 attempts
325 }
326}
327
328fn record_failure(progress: &mut [WriteProgress], owners: &[usize], error: Error) {
329 let error = Arc::new(BoxedError::new(error));
330 for owner in owners.iter().copied().collect::<HashSet<_>>() {
331 progress[owner].error.get_or_insert_with(|| error.clone());
332 }
333}