1mod attribution;
16mod options;
17mod progress;
18#[cfg(test)]
19mod tests;
20
21use std::collections::{HashMap, HashSet, hash_map::Entry};
22use std::sync::Arc;
23
24use api::region::RegionResponse;
25use api::v1::region::{DeleteRequests, InsertRequests, RegionRequest, region_request::Body};
26use backon::{BackoffBuilder, ExponentialBuilder};
27use catalog::CatalogManagerRef;
28use common_error::ext::{BoxedError, ErrorExt, RetryHint};
29use common_meta::cache_invalidator::{CacheInvalidatorRef, Context};
30use common_meta::error::{
31 BatchRequestSnafu, Error, ExternalSnafu, Result, TableInfoNotFoundSnafu, UnexpectedSnafu,
32};
33use common_meta::instruction::CacheIdent;
34use common_meta::node_manager::NodeManagerRef;
35use common_meta::peer::{Peer, PeerRef};
36use common_meta::rpc::router;
37use futures::future;
38use partition::manager::{PartitionRuleManager, PartitionRuleManagerRef};
39use prometheus::Histogram;
40use session::write_progress::WriteProgress;
41use snafu::{IntoError, OptionExt, ResultExt, ensure};
42use store_api::storage::RegionId;
43use table::table_name::TableName;
44use tokio_util::sync::CancellationToken;
45
46pub use self::attribution::WriteAttribution;
47pub use self::options::WriteRetryOptions;
48pub use self::progress::TrackedWrite;
49use crate::error::{self, FindRegionLeaderSnafu, RequestRegionSnafu};
50use crate::req_convert::TablePartitioner;
51
52pub struct WriteDispatcher {
54 node_manager: NodeManagerRef,
55 partition_manager: PartitionRuleManagerRef,
56 retry: Option<WriteRetry>,
57}
58
59pub type WriteDispatcherRef = Arc<WriteDispatcher>;
61
62pub(crate) enum WriteAttempt {
64 Success(RegionResponse),
65 Failed {
66 error: Error,
67 request: Option<RoutedWrite>,
70 },
71}
72
73impl WriteDispatcher {
74 pub fn new(node_manager: NodeManagerRef, partition_manager: PartitionRuleManagerRef) -> Self {
76 Self {
77 node_manager,
78 partition_manager,
79 retry: None,
80 }
81 }
82
83 pub fn with_retry(
85 mut self,
86 options: WriteRetryOptions,
87 catalog_manager: CatalogManagerRef,
88 cache_invalidator: CacheInvalidatorRef,
89 ) -> Self {
90 if options.max_retries > 0 {
91 self.retry = Some(WriteRetry {
92 node_manager: self.node_manager.clone(),
93 partition_manager: self.partition_manager.clone(),
94 options,
95 catalog_manager,
96 cache_invalidator,
97 });
98 } else {
99 self.retry = None;
100 }
101 self
102 }
103
104 pub async fn dispatch(&self, requests: Vec<RegionRequest>) -> Result<RegionResponse> {
106 self.dispatch_inner(requests, None).await
107 }
108
109 pub async fn dispatch_with_metrics(
111 &self,
112 requests: Vec<RegionRequest>,
113 histogram: Histogram,
114 ) -> Result<RegionResponse> {
115 self.dispatch_inner(requests, Some(histogram)).await
116 }
117
118 pub async fn dispatch_attributed(
120 &self,
121 requests: Vec<(RegionRequest, WriteAttribution)>,
122 histogram: Option<Histogram>,
123 ) -> Result<RegionResponse> {
124 let regions = requests
125 .iter()
126 .map(|(request, _)| write_region_id(request))
127 .collect::<Result<Vec<_>>>()?;
128 let peers = self
129 .resolve_peers(®ions)
130 .await
131 .map_err(BoxedError::new)
132 .context(ExternalSnafu)?;
133 let writes = requests
134 .into_iter()
135 .zip(regions)
136 .map(|((request, attribution), region)| {
137 Ok((get_peer(&peers, region)?, request, attribution))
138 })
139 .collect::<Result<Vec<_>>>()?;
140 let attempts = future::join_all(writes.into_iter().map(|(peer, request, attribution)| {
141 self.send_attributed(peer, request, attribution, histogram.as_ref())
142 }))
143 .await;
144 self.finish(attempts, histogram.as_ref(), None).await
145 }
146
147 pub(crate) async fn prepare(
149 &self,
150 requests: Vec<RegionRequest>,
151 ) -> error::Result<Vec<(PeerRef, RegionRequest)>> {
152 let requests = requests
153 .into_iter()
154 .map(|mut request| {
155 let header = request.header.take();
156 (header, split_request(request))
157 })
158 .collect::<Vec<_>>();
159 let regions = requests
160 .iter()
161 .flat_map(|(_, requests)| requests)
162 .map(write_region_id)
163 .collect::<Result<Vec<_>>>()
164 .context(RequestRegionSnafu)?;
165 let peers = self
166 .resolve_peers(®ions)
167 .await
168 .context(FindRegionLeaderSnafu)?;
169 let mut writes = Vec::new();
170 for (header, requests) in requests {
172 let mut by_peer: HashMap<PeerRef, RegionRequest> = HashMap::new();
173 for request in requests {
174 let peer = get_peer(
175 &peers,
176 write_region_id(&request).context(RequestRegionSnafu)?,
177 )
178 .context(RequestRegionSnafu)?;
179 match by_peer.entry(peer) {
180 Entry::Vacant(entry) => {
181 entry.insert(request);
182 }
183 Entry::Occupied(mut entry) => match (&mut entry.get_mut().body, request.body) {
184 (Some(Body::Inserts(existing)), Some(Body::Inserts(next))) => {
185 existing.requests.extend(next.requests)
186 }
187 (Some(Body::Deletes(existing)), Some(Body::Deletes(next))) => {
188 existing.requests.extend(next.requests)
189 }
190 _ => {
191 return UnexpectedSnafu {
192 err_msg: "Cannot combine different write kinds",
193 }
194 .fail()
195 .context(RequestRegionSnafu);
196 }
197 },
198 }
199 }
200 for (peer, mut request) in by_peer {
201 request.header = header.clone();
202 writes.push((peer, request));
203 }
204 }
205 Ok(writes)
206 }
207
208 pub(crate) async fn execute(
211 &self,
212 writes: Vec<(PeerRef, RegionRequest)>,
213 histogram: Option<Histogram>,
214 cancellation: Option<&CancellationToken>,
215 ) -> Result<RegionResponse> {
216 self.execute_with_progress(writes, histogram, cancellation, None)
217 .await
218 }
219
220 pub(crate) async fn execute_with_progress(
221 &self,
222 writes: Vec<(PeerRef, RegionRequest)>,
223 histogram: Option<Histogram>,
224 cancellation: Option<&CancellationToken>,
225 progress: Option<Arc<WriteProgress>>,
226 ) -> Result<RegionResponse> {
227 let histogram = histogram.as_ref();
228 let progress = progress.as_ref();
229 let attempts = future::join_all(writes.into_iter().map(|(peer, request)| async move {
230 if self.retry.is_some() || progress.is_some() {
231 self.send_batch(peer, request, Vec::new(), histogram, progress)
232 .await
233 } else {
234 vec![(Vec::new(), self.send(peer, request, histogram).await)]
235 }
236 }))
237 .await
238 .into_iter()
239 .flatten()
240 .map(|(_, attempt)| attempt)
241 .collect();
242 self.finish(attempts, histogram, cancellation).await
243 }
244
245 pub(crate) async fn send(
247 &self,
248 peer: PeerRef,
249 request: RegionRequest,
250 histogram: Option<&Histogram>,
251 ) -> WriteAttempt {
252 self.send_attributed(peer, request, WriteAttribution::default(), histogram)
253 .await
254 }
255
256 async fn send_attributed(
257 &self,
258 peer: PeerRef,
259 mut request: RegionRequest,
260 attribution: WriteAttribution,
261 histogram: Option<&Histogram>,
262 ) -> WriteAttempt {
263 if let Some(retry) = &self.retry {
264 if matches!(&request.body, Some(Body::Inserts(_) | Body::Deletes(_))) {
265 api::region::set_accept_partial_result(request.header.get_or_insert_default());
266 }
267 return retry
268 .send(
269 RoutedWrite {
270 peer,
271 request: Arc::new(request),
272 attribution,
273 owners: None,
274 },
275 histogram,
276 )
277 .await;
278 }
279 let _timer = histogram.map(Histogram::start_timer);
280 let datanode = self.node_manager.datanode(&peer).await;
281 match datanode.handle(request).await {
282 Ok(response) => acknowledged(response, &attribution),
283 Err(error) => WriteAttempt::Failed {
284 error,
285 request: None,
286 },
287 }
288 }
289
290 pub(crate) async fn finish(
292 &self,
293 attempts: Vec<WriteAttempt>,
294 histogram: Option<&Histogram>,
295 cancellation: Option<&CancellationToken>,
296 ) -> Result<RegionResponse> {
297 if let Some(retry) = &self.retry {
298 if retry.should_retry(&attempts, 0) {
299 return retry.retry(attempts, histogram, cancellation).await;
300 }
301 let mut response = RegionResponse::new(0);
302 let failures = merge_attempts(attempts, &mut response)?;
303 return match failures.into_iter().next() {
304 Some((_, error)) => Err(error),
305 None => Ok(response),
306 };
307 }
308 let mut response = RegionResponse::new(0);
309 for attempt in attempts {
310 match attempt {
311 WriteAttempt::Success(result) => merge_response(&mut response, result),
312 WriteAttempt::Failed { error, .. } => return Err(error),
313 }
314 }
315 Ok(response)
316 }
317
318 pub(crate) async fn resolve_peers(
320 &self,
321 regions: &[RegionId],
322 ) -> partition::error::Result<HashMap<RegionId, PeerRef>> {
323 resolve_peers(&self.partition_manager, regions).await
324 }
325
326 async fn send_batch(
328 &self,
329 peer: PeerRef,
330 mut request: RegionRequest,
331 owners: Vec<Vec<usize>>,
332 histogram: Option<&Histogram>,
333 progress: Option<&Arc<WriteProgress>>,
334 ) -> Vec<(Vec<usize>, WriteAttempt)> {
335 if !matches!(&request.body, Some(Body::Inserts(_) | Body::Deletes(_))) {
336 return vec![(
337 owners.into_iter().flatten().collect(),
338 self.send(peer, request, histogram).await,
339 )];
340 }
341 api::region::set_accept_partial_result(request.header.get_or_insert_default());
344 if self.retry.is_none() {
345 let requester = self.node_manager.datanode(&peer).await;
347 let _timer = histogram.map(Histogram::start_timer);
348 return match requester.handle(request).await {
349 Ok(response) => {
350 if let Some(progress) = progress {
351 progress.add_written(response.affected_rows);
352 }
353 vec![WriteAttempt::Success(response)]
354 }
355 Err(Error::PartialRegion { response }) => {
356 if let Some(progress) = progress {
357 progress.add_written(response.affected_rows);
358 }
359 let mut attempts = vec![WriteAttempt::Success(RegionResponse {
360 affected_rows: response.affected_rows,
361 extensions: response.extensions,
362 metadata: Vec::new(),
363 })];
364 attempts.extend(response.failures.into_iter().map(|(_, error)| {
365 WriteAttempt::Failed {
366 error,
367 request: None,
368 }
369 }));
370 attempts
371 }
372 Err(error) => vec![WriteAttempt::Failed {
373 error,
374 request: None,
375 }],
376 }
377 .into_iter()
378 .map(|attempt| (Vec::new(), attempt))
379 .collect();
380 }
381 let request = Arc::new(request);
382 let requester = self.node_manager.datanode(&peer).await;
383 let result = {
384 let _timer = histogram.map(Histogram::start_timer);
385 requester.handle(Arc::clone(&request)).await
386 };
387 let error = match result {
388 Ok(response) => {
389 if let Some(progress) = progress {
390 progress.add_written(response.affected_rows);
391 }
392 return vec![(
393 owners.into_iter().flatten().collect(),
394 WriteAttempt::Success(response),
395 )];
396 }
397 Err(error) => error,
398 };
399 let request = match Arc::try_unwrap(request) {
400 Ok(request) => request,
401 Err(_) => {
402 return vec![(
403 owners.into_iter().flatten().collect(),
404 WriteAttempt::Failed {
405 error: UnexpectedSnafu {
406 err_msg: "Region request is still shared after its RPC completed",
407 }
408 .build(),
409 request: None,
410 },
411 )];
412 }
413 };
414 let requests = split_request(request);
415 let mut owners = owners.into_iter();
416 match error {
417 Error::PartialRegion { response } => {
418 if let Some(progress) = progress {
419 progress.add_written(response.affected_rows);
420 }
421 let mut failures: HashMap<_, _> = response.failures.into_iter().collect();
422 let mut attempts = Vec::new();
423 let mut success_owners = Vec::new();
424 for (index, request) in requests.into_iter().enumerate() {
425 let owners = owners.next();
426 if let Some(error) = failures.remove(&index) {
427 let failed_owners = owners.clone().unwrap_or_default();
428 let request = error.retry_hint().is_retryable().then(|| RoutedWrite {
429 peer: Arc::clone(&peer),
430 attribution: direct_attribution(&request, progress.cloned()),
431 request: Arc::new(request),
432 owners,
433 });
434 attempts.push((failed_owners, WriteAttempt::Failed { error, request }));
435 } else {
436 success_owners.extend(owners.unwrap_or_default());
437 }
438 }
439 attempts.insert(
440 0,
441 (
442 success_owners,
443 WriteAttempt::Success(RegionResponse {
444 affected_rows: response.affected_rows,
445 extensions: response.extensions,
446 metadata: Vec::new(),
447 }),
448 ),
449 );
450 attempts
451 }
452 error => {
453 let error = Arc::new(error);
455 requests
456 .into_iter()
457 .map(|request| {
458 let owners = owners.next();
459 let failed_owners = owners.clone().unwrap_or_default();
460 let request = error.retry_hint().is_retryable().then(|| RoutedWrite {
461 peer: Arc::clone(&peer),
462 attribution: direct_attribution(&request, progress.cloned()),
463 request: Arc::new(request),
464 owners,
465 });
466 (
467 failed_owners,
468 WriteAttempt::Failed {
469 error: BatchRequestSnafu.into_error(Arc::clone(&error)),
470 request,
471 },
472 )
473 })
474 .collect()
475 }
476 }
477 }
478
479 async fn dispatch_inner(
480 &self,
481 requests: Vec<RegionRequest>,
482 histogram: Option<Histogram>,
483 ) -> Result<RegionResponse> {
484 let writes = self
485 .prepare(requests)
486 .await
487 .map_err(BoxedError::new)
488 .context(ExternalSnafu)?;
489 self.execute(writes, histogram, None).await
490 }
491}
492
493struct WriteRetry {
495 node_manager: NodeManagerRef,
496 partition_manager: PartitionRuleManagerRef,
497 options: WriteRetryOptions,
498 catalog_manager: CatalogManagerRef,
499 cache_invalidator: CacheInvalidatorRef,
500}
501
502pub(crate) struct RoutedWrite {
504 peer: PeerRef,
505 request: Arc<RegionRequest>,
506 attribution: WriteAttribution,
507 owners: Option<Vec<usize>>,
508}
509
510impl WriteRetry {
511 async fn send(&self, write: RoutedWrite, histogram: Option<&Histogram>) -> WriteAttempt {
512 let _timer = histogram.map(Histogram::start_timer);
513 let requester = self.node_manager.datanode(&write.peer).await;
514 match requester.handle(Arc::clone(&write.request)).await {
515 Ok(response) => acknowledged(response, &write.attribution),
516 Err(error) => {
517 let request = error.retry_hint().is_retryable().then_some(write);
518 WriteAttempt::Failed { error, request }
519 }
520 }
521 }
522
523 fn should_retry(&self, attempts: &[WriteAttempt], retries: usize) -> bool {
524 if retries >= self.options.max_retries {
525 return false;
526 }
527 let mut retryable = false;
528 for attempt in attempts {
529 match attempt {
530 WriteAttempt::Success(_) => {}
531 WriteAttempt::Failed {
532 request: Some(_), ..
533 } => retryable = true,
534 WriteAttempt::Failed { request: None, .. } => return false,
535 }
536 }
537 retryable
538 }
539
540 async fn retry(
541 &self,
542 mut attempts: Vec<WriteAttempt>,
543 histogram: Option<&Histogram>,
544 cancellation: Option<&CancellationToken>,
545 ) -> Result<RegionResponse> {
546 let mut response = RegionResponse::new(0);
547 let mut backoff = ExponentialBuilder::default()
548 .with_min_delay(self.options.initial_backoff)
549 .with_max_delay(self.options.max_backoff)
550 .with_factor(self.options.backoff_multiplier)
551 .with_max_times(self.options.max_retries)
552 .with_jitter()
553 .build();
554 let mut retries = 0;
555 while self.should_retry(&attempts, retries) {
556 let Some(delay) = backoff.next() else {
557 break;
558 };
559 if let Some(cancellation) = cancellation {
560 tokio::select! {
561 biased;
562 _ = cancellation.cancelled() => break,
563 _ = tokio::time::sleep(delay) => {}
564 }
565 } else {
566 tokio::time::sleep(delay).await;
567 }
568 if cancellation.is_some_and(CancellationToken::is_cancelled) {
569 break;
570 }
571 let failures = merge_attempts(attempts, &mut response)?;
572 let mut first_error = None;
573 let mut refresh = Vec::new();
574 let mut pending = Vec::new();
575 for (write, error) in failures {
576 if error.retry_hint() == RetryHint::RetryAfterRouteRefresh {
577 refresh.push(write);
578 } else {
579 pending.push(write);
580 }
581 first_error.get_or_insert(error);
582 }
583 if !refresh.is_empty() {
584 pending.extend(self.reroute_failed_writes(refresh).await?);
585 }
586 if let Some(error) =
587 first_error.filter(|_| cancellation.is_some_and(CancellationToken::is_cancelled))
588 {
589 return Err(error);
590 }
591 attempts =
592 future::join_all(pending.into_iter().map(|write| self.send(write, histogram)))
593 .await;
594 retries += 1;
595 }
596 match merge_attempts(attempts, &mut response)?.into_iter().next() {
597 Some((_, error)) => Err(error),
598 None => Ok(response),
599 }
600 }
601
602 async fn reroute_failed_writes(&self, writes: Vec<RoutedWrite>) -> Result<Vec<RoutedWrite>> {
603 let regions = writes
604 .iter()
605 .map(|write| write_region_id(&write.request))
606 .collect::<Result<Vec<_>>>()?;
607 self.invalidate_routes(®ions).await?;
608 let mut table_names = HashMap::new();
609 for table_id in regions
610 .iter()
611 .map(|region| region.table_id())
612 .collect::<HashSet<_>>()
613 {
614 let table_info = self
616 .catalog_manager
617 .table_info_by_id(table_id)
618 .await
619 .map_err(BoxedError::new)
620 .context(ExternalSnafu)?
621 .with_context(|| TableInfoNotFoundSnafu {
622 table: table_id.to_string(),
623 })?;
624 table_names.insert(
625 table_id,
626 TableName::new(
627 &table_info.catalog_name,
628 &table_info.schema_name,
629 &table_info.name,
630 ),
631 );
632 }
633 let caches = table_names
636 .values()
637 .cloned()
638 .map(CacheIdent::TableName)
639 .collect::<Vec<_>>();
640 self.cache_invalidator
641 .invalidate(&Context::default(), &caches)
642 .await?;
643 let mut partitioners = HashMap::new();
644 for (table_id, name) in table_names {
645 let table = self
646 .catalog_manager
647 .table(
648 &name.catalog_name,
649 &name.schema_name,
650 &name.table_name,
651 None,
652 )
653 .await
654 .map_err(BoxedError::new)
655 .context(ExternalSnafu)?
656 .with_context(|| TableInfoNotFoundSnafu {
657 table: name.to_string(),
658 })?;
659 let table_info = table.table_info();
660 ensure!(
661 table_info.table_id() == table_id,
662 UnexpectedSnafu {
663 err_msg: format!(
664 "Table {name} changed ID from {table_id} to {} while retrying writes",
665 table_info.table_id()
666 ),
667 }
668 );
669 let partitioner = TablePartitioner::try_new(&self.partition_manager, &table_info)
670 .await
671 .map_err(BoxedError::new)
672 .context(ExternalSnafu)?;
673 partitioners.insert(table_id, (table_info, partitioner));
674 }
675 let mut rebuilt_requests = Vec::new();
676 for write in writes {
677 let request = Arc::try_unwrap(write.request).map_err(|_| {
678 UnexpectedSnafu {
679 err_msg: "Write request is still shared after its RPC completed",
680 }
681 .build()
682 })?;
683 let table_id = write_region_id(&request)?.table_id();
684 let (table_info, partitioner) =
685 partitioners.get(&table_id).context(UnexpectedSnafu {
686 err_msg: "Refreshed write has no partition snapshot",
687 })?;
688 if let Some(owners) = write.owners {
689 let requests = partitioner
690 .rebuild_tracked_write_requests(request, table_info, Some(owners))
691 .map_err(BoxedError::new)
692 .context(ExternalSnafu)?;
693 rebuilt_requests.extend(
694 requests
695 .into_iter()
696 .map(|(request, owners)| ((request, WriteAttribution::default()), owners)),
697 );
698 } else {
699 let requests = partitioner
700 .rebuild_attributed_write(request, table_info, write.attribution)
701 .map_err(BoxedError::new)
702 .context(ExternalSnafu)?;
703 rebuilt_requests.extend(requests.into_iter().map(|request| (request, None)));
704 }
705 }
706 let (requests, owners): (Vec<_>, Vec<_>) = rebuilt_requests.into_iter().unzip();
707 let mut writes = self.prepare(requests).await?;
708 for (write, owners) in writes.iter_mut().zip(owners) {
709 write.owners = owners;
710 }
711 Ok(writes)
712 }
713
714 async fn prepare(
715 &self,
716 requests: Vec<(RegionRequest, WriteAttribution)>,
717 ) -> Result<Vec<RoutedWrite>> {
718 let regions = requests
719 .iter()
720 .map(|(request, _)| write_region_id(request))
721 .collect::<Result<Vec<_>>>()?;
722 let peers = resolve_peers(&self.partition_manager, ®ions)
723 .await
724 .map_err(BoxedError::new)
725 .context(ExternalSnafu)?;
726 requests
727 .into_iter()
728 .zip(regions)
729 .map(|((request, attribution), region)| {
730 Ok(RoutedWrite {
731 peer: get_peer(&peers, region)?,
732 request: Arc::new(request),
733 attribution,
734 owners: None,
735 })
736 })
737 .collect()
738 }
739
740 async fn invalidate_routes(&self, regions: &[RegionId]) -> Result<()> {
741 let mut table_ids = regions
742 .iter()
743 .map(|region| region.table_id())
744 .collect::<HashSet<_>>();
745 let mut physical_ids = HashSet::new();
746 for table_id in &table_ids {
747 let (physical_id, _) = self
748 .partition_manager
749 .find_physical_table_route_with_id(*table_id)
750 .await
751 .map_err(BoxedError::new)
752 .context(ExternalSnafu)?;
753 physical_ids.insert(physical_id);
754 }
755 table_ids.extend(physical_ids);
756 let caches = table_ids
757 .into_iter()
758 .map(CacheIdent::TableId)
759 .collect::<Vec<_>>();
760 self.cache_invalidator
761 .invalidate(&Context::default(), &caches)
762 .await
763 }
764}
765
766fn acknowledged(response: RegionResponse, attribution: &WriteAttribution) -> WriteAttempt {
767 match attribution.record(response.affected_rows) {
768 Ok(()) => WriteAttempt::Success(response),
769 Err(error) => WriteAttempt::Failed {
770 error,
771 request: None,
772 },
773 }
774}
775
776pub(crate) fn write_row_count(request: &RegionRequest) -> usize {
777 match &request.body {
778 Some(Body::Inserts(inserts)) => inserts
779 .requests
780 .iter()
781 .filter_map(|request| request.rows.as_ref())
782 .map(|rows| rows.rows.len())
783 .sum(),
784 Some(Body::Deletes(deletes)) => deletes
785 .requests
786 .iter()
787 .filter_map(|request| request.rows.as_ref())
788 .map(|rows| rows.rows.len())
789 .sum(),
790 _ => 0,
791 }
792}
793
794fn direct_attribution(
795 request: &RegionRequest,
796 progress: Option<Arc<WriteProgress>>,
797) -> WriteAttribution {
798 WriteAttribution::new(write_row_count(request), progress)
799}
800
801async fn resolve_peers(
802 partition_manager: &PartitionRuleManager,
803 regions: &[RegionId],
804) -> partition::error::Result<HashMap<RegionId, PeerRef>> {
805 let table_ids = regions
806 .iter()
807 .map(|region| region.table_id())
808 .collect::<HashSet<_>>();
809 let mut physical_tables = HashMap::new();
810 let mut routes = HashMap::new();
811 for table_id in table_ids {
812 let (physical_id, route) = partition_manager
813 .find_physical_table_route_with_id(table_id)
814 .await?;
815 physical_tables.insert(table_id, physical_id);
816 routes.entry(physical_id).or_insert(route);
817 }
818 let mut shared_peers: HashMap<&Peer, PeerRef> = HashMap::new();
819 let mut peers = HashMap::with_capacity(regions.len());
820 for region in regions {
821 if peers.contains_key(region) {
822 continue;
823 }
824 let peer = physical_tables
825 .get(®ion.table_id())
826 .and_then(|physical_id| routes.get(physical_id))
827 .and_then(|route| {
828 router::find_region_leader_ref(&route.region_routes, region.region_number())
829 })
830 .context(partition::error::FindLeaderSnafu {
831 region_id: *region,
832 table_id: region.table_id(),
833 })?;
834 let peer = shared_peers
835 .entry(peer)
836 .or_insert_with(|| Arc::new(peer.clone()));
837 peers.insert(*region, Arc::clone(peer));
838 }
839 Ok(peers)
840}
841
842fn get_peer(peers: &HashMap<RegionId, PeerRef>, region: RegionId) -> Result<PeerRef> {
843 peers.get(®ion).cloned().context(UnexpectedSnafu {
844 err_msg: "Planned write has no resolved peer",
845 })
846}
847
848fn merge_attempts(
849 attempts: Vec<WriteAttempt>,
850 response: &mut RegionResponse,
851) -> Result<Vec<(RoutedWrite, Error)>> {
852 let mut failures = Vec::new();
853 let mut terminal_error = None;
854 for attempt in attempts {
855 match attempt {
856 WriteAttempt::Success(result) => merge_response(response, result),
857 WriteAttempt::Failed {
858 error,
859 request: Some(write),
860 } => failures.push((write, error)),
861 WriteAttempt::Failed {
862 error,
863 request: None,
864 } => {
865 terminal_error.get_or_insert(error);
866 }
867 }
868 }
869 match terminal_error {
871 Some(error) => Err(error),
872 None => Ok(failures),
873 }
874}
875
876fn merge_response(response: &mut RegionResponse, result: RegionResponse) {
877 response.affected_rows += result.affected_rows;
878 response.extensions.extend(result.extensions);
879 response.metadata = result.metadata;
880}
881
882fn split_request(request: RegionRequest) -> Vec<RegionRequest> {
883 let RegionRequest { header, body } = request;
884 let bodies: Vec<Body> = match body {
885 Some(Body::Inserts(inserts)) => inserts
886 .requests
887 .into_iter()
888 .map(|request| {
889 Body::Inserts(InsertRequests {
890 requests: vec![request],
891 })
892 })
893 .collect(),
894 Some(Body::Deletes(deletes)) => deletes
895 .requests
896 .into_iter()
897 .map(|request| {
898 Body::Deletes(DeleteRequests {
899 requests: vec![request],
900 })
901 })
902 .collect(),
903 body => return vec![RegionRequest { header, body }],
904 };
905 bodies
906 .into_iter()
907 .map(|body| RegionRequest {
908 header: header.clone(),
909 body: Some(body),
910 })
911 .collect()
912}
913
914fn write_region_id(request: &RegionRequest) -> Result<RegionId> {
915 let region_id = match &request.body {
916 Some(Body::Inserts(inserts)) => match inserts.requests.as_slice() {
917 [request] => Some(request.region_id),
918 _ => None,
919 },
920 Some(Body::Deletes(deletes)) => match deletes.requests.as_slice() {
921 [request] => Some(request.region_id),
922 _ => None,
923 },
924 Some(Body::BulkInsert(bulk)) => Some(bulk.region_id),
925 _ => None,
926 };
927 region_id.map(RegionId::from_u64).ok_or_else(|| {
928 UnexpectedSnafu {
929 err_msg: "Expected a single-region write request",
930 }
931 .build()
932 })
933}