1use std::collections::{HashMap, HashSet, hash_map};
18use std::sync::Arc;
19
20use api::v1::OpType;
21use common_telemetry::{debug, error};
22use snafu::ensure;
23use store_api::codec::PrimaryKeyEncoding;
24use store_api::logstore::LogStore;
25use store_api::storage::RegionId;
26
27use crate::error::{
28 InvalidRequestSnafu, PartitionExprVersionMismatchSnafu, RegionLeaseExpiredSnafu,
29 RegionNotFoundSnafu, RegionStateSnafu, RejectWriteInStagingSnafu, RejectWriteSnafu, Result,
30 UnacknowledgedWriteSnafu,
31};
32use crate::metrics;
33use crate::metrics::{
34 WRITE_REJECT_TOTAL, WRITE_ROWS_TOTAL, WRITE_STAGE_ELAPSED, WRITE_STALL_TOTAL,
35};
36use crate::region::{RegionLeaderState, RegionRoleState};
37use crate::region_write_ctx::{RegionWriteCtx, WriteSource};
38use crate::request::{
39 SenderBulkRequest, SenderWriteRequest, WriteRequest, check_bulk_insert_batch,
40};
41use crate::wal::Wal;
42use crate::worker::RegionWorkerLoop;
43
44impl<S: LogStore> RegionWorkerLoop<S> {
45 pub(crate) async fn handle_write_requests(
47 &mut self,
48 write_requests: &mut Vec<SenderWriteRequest>,
49 bulk_requests: &mut Vec<SenderBulkRequest>,
50 allow_stall: bool,
51 ) {
52 if write_requests.is_empty() && bulk_requests.is_empty() {
53 return;
54 }
55
56 let write_region_ids = write_region_ids(write_requests, bulk_requests);
57
58 self.maybe_flush_worker();
60 let pressure = self.maybe_flush_write_regions(write_region_ids);
61
62 if self.should_reject_write() {
63 reject_write_requests(write_requests, bulk_requests);
65 self.reject_stalled_requests();
67 return;
68 }
69
70 if !pressure.rejected_region_ids.is_empty() {
71 reject_region_write_requests(
72 &pressure.rejected_region_ids,
73 write_requests,
74 bulk_requests,
75 );
76 for region_id in &pressure.rejected_region_ids {
77 self.reject_region_stalled_requests(region_id);
78 }
79 if write_requests.is_empty() && bulk_requests.is_empty() {
80 return;
81 }
82 }
83
84 if self.write_buffer_manager.should_stall() && allow_stall {
85 let stalled_count = (write_requests.len() + bulk_requests.len()) as i64;
86 self.stalling_count.add(stalled_count);
87 WRITE_STALL_TOTAL.inc_by(stalled_count as u64);
88 self.stalled_requests.append(write_requests, bulk_requests);
89 self.listener.on_write_stall();
90 return;
91 }
92
93 if allow_stall {
94 self.stall_region_write_requests(
95 &pressure.stalled_region_ids,
96 write_requests,
97 bulk_requests,
98 );
99 if write_requests.is_empty() && bulk_requests.is_empty() {
100 return;
101 }
102 }
103
104 let mut region_ctxs = {
106 let _timer = WRITE_STAGE_ELAPSED
107 .with_label_values(&["prepare_ctx"])
108 .start_timer();
109 self.prepare_region_write_ctx(write_requests, bulk_requests)
110 };
111
112 {
114 let _timer = WRITE_STAGE_ELAPSED
115 .with_label_values(&["write_wal"])
116 .start_timer();
117 if !write_wal(&self.wal, &mut region_ctxs).await {
118 return;
120 }
121 }
122 self.listener.on_write_wal_end().await;
123 self.fail_writes_after_lease_expiry(&mut region_ctxs);
124
125 let (mut put_rows, mut delete_rows) = (0, 0);
126 {
128 let _timer = WRITE_STAGE_ELAPSED
129 .with_label_values(&["write_memtable"])
130 .start_timer();
131 if region_ctxs.len() == 1 {
132 let mut region_ctx = region_ctxs.into_values().next().unwrap();
134 region_ctx.write_memtables().await;
135 put_rows += region_ctx.put_num;
136 delete_rows += region_ctx.delete_num;
137 } else {
138 let region_write_task = region_ctxs
139 .into_values()
140 .map(|mut region_ctx| {
141 common_runtime::spawn_global(async move {
143 region_ctx.write_memtables().await;
144 (region_ctx.put_num, region_ctx.delete_num)
145 })
146 })
147 .collect::<Vec<_>>();
148
149 for result in futures::future::join_all(region_write_task).await {
150 match result {
151 Ok((put, delete)) => {
152 put_rows += put;
153 delete_rows += delete;
154 }
155 Err(e) => {
156 error!(e; "unexpected error when joining region write tasks");
157 }
158 }
159 }
160 }
161 }
162 WRITE_ROWS_TOTAL
163 .with_label_values(&["put"])
164 .inc_by(put_rows as u64);
165 WRITE_ROWS_TOTAL
166 .with_label_values(&["delete"])
167 .inc_by(delete_rows as u64);
168 }
169
170 pub(crate) async fn handle_stalled_requests(&mut self) {
172 let region_ids = self
173 .stalled_requests
174 .requests
175 .keys()
176 .copied()
177 .collect::<HashSet<_>>();
178 let pressure = self.maybe_flush_write_regions(region_ids);
179 for region_id in &pressure.rejected_region_ids {
180 self.reject_region_stalled_requests(region_id);
181 }
182 let ready_region_ids = self
183 .stalled_requests
184 .requests
185 .keys()
186 .filter(|region_id| !pressure.stalled_region_ids.contains(region_id))
187 .copied()
188 .collect::<Vec<_>>();
189
190 for region_id in ready_region_ids {
194 self.handle_region_stalled_requests(®ion_id, false).await;
195 }
196 }
197
198 pub(crate) fn reject_stalled_requests(&mut self) {
200 let stalled = std::mem::take(&mut self.stalled_requests);
201 self.stalling_count.sub(stalled.stalled_count() as i64);
202 for (_, (_, mut requests, mut bulk)) in stalled.requests {
203 reject_write_requests(&mut requests, &mut bulk);
204 }
205 }
206
207 pub(crate) fn reject_region_stalled_requests(&mut self, region_id: &RegionId) {
209 debug!("Rejects stalled requests for region {}", region_id);
210 let (mut requests, mut bulk) = self.stalled_requests.remove(region_id);
211 self.stalling_count
212 .sub((requests.len() + bulk.len()) as i64);
213 reject_write_requests(&mut requests, &mut bulk);
214 }
215
216 pub(crate) fn fail_region_stalled_requests_as_not_found(&mut self, region_id: &RegionId) {
218 debug!(
219 "Fails stalled requests for region {} as region not found",
220 region_id
221 );
222 let (requests, bulk) = self.stalled_requests.remove(region_id);
223 self.stalling_count
224 .sub((requests.len() + bulk.len()) as i64);
225
226 for req in requests {
227 req.sender.send(
228 RegionNotFoundSnafu {
229 region_id: req.request.region_id,
230 }
231 .fail(),
232 );
233 }
234 for req in bulk {
235 req.sender.send(
236 RegionNotFoundSnafu {
237 region_id: req.region_id,
238 }
239 .fail(),
240 );
241 }
242 }
243
244 pub(crate) async fn handle_region_stalled_requests(
251 &mut self,
252 region_id: &RegionId,
253 allow_stall: bool,
254 ) {
255 debug!("Handles stalled requests for region {}", region_id);
256 let (mut requests, mut bulk) = self.stalled_requests.remove(region_id);
257 self.stalling_count
258 .sub((requests.len() + bulk.len()) as i64);
259 self.handle_write_requests(&mut requests, &mut bulk, allow_stall)
260 .await;
261 }
262
263 pub(crate) async fn handle_buffered_region_write_requests(
269 &mut self,
270 region_id: &RegionId,
271 write_requests: &mut Vec<SenderWriteRequest>,
272 bulk_requests: &mut Vec<SenderBulkRequest>,
273 ) {
274 let mut current_region_write_requests = write_requests
275 .extract_if(.., |r| r.request.region_id == *region_id)
276 .collect::<Vec<_>>();
277
278 let mut current_region_bulk_requests = bulk_requests
279 .extract_if(.., |r| r.region_id == *region_id)
280 .collect::<Vec<_>>();
281
282 self.handle_write_requests(
283 &mut current_region_write_requests,
284 &mut current_region_bulk_requests,
285 true,
286 )
287 .await;
288 }
289}
290
291impl<S> RegionWorkerLoop<S> {
292 fn prepare_region_write_ctx(
294 &mut self,
295 write_requests: &mut Vec<SenderWriteRequest>,
296 bulk_requests: &mut Vec<SenderBulkRequest>,
297 ) -> HashMap<RegionId, RegionWriteCtx> {
298 let mut region_ctxs = HashMap::new();
300 let lease_now = self.node_lease.is_enabled().then(|| self.node_lease.now());
302 self.process_write_requests(&mut region_ctxs, write_requests, lease_now);
303 self.process_bulk_requests(&mut region_ctxs, bulk_requests, lease_now);
304 region_ctxs
305 }
306
307 fn process_write_requests(
308 &mut self,
309 region_ctxs: &mut HashMap<RegionId, RegionWriteCtx>,
310 write_requests: &mut Vec<SenderWriteRequest>,
311 lease_now: Option<u64>,
312 ) {
313 for mut sender_req in write_requests.drain(..) {
314 let region_id = sender_req.request.region_id;
315
316 if self.flush_scheduler.has_pending_ddls(region_id) {
318 self.flush_scheduler
321 .add_write_request_to_pending(sender_req);
322 continue;
323 }
324
325 if let hash_map::Entry::Vacant(e) = region_ctxs.entry(region_id) {
327 let Some(region) = self
328 .regions
329 .get_region_or(region_id, &mut sender_req.sender)
330 else {
331 continue;
333 };
334 #[cfg(test)]
335 debug!(
336 "Handling write request for region {}, state: {:?}",
337 region_id,
338 region.state()
339 );
340 match region.state() {
341 RegionRoleState::Leader(RegionLeaderState::Writable)
342 | RegionRoleState::Leader(RegionLeaderState::Staging) => {
343 let leader_deadline = region.leader_deadline.get();
344 if lease_now.is_some_and(|now| now >= leader_deadline) {
345 sender_req
346 .sender
347 .send(RegionLeaseExpiredSnafu { region_id }.fail());
348 continue;
349 }
350 if region.reject_all_writes_in_staging() {
351 sender_req
352 .sender
353 .send(RejectWriteInStagingSnafu { region_id }.fail());
354 continue;
355 }
356
357 let mut region_ctx = RegionWriteCtx::new(
358 region.region_id,
359 ®ion.version_control,
360 region.provider.clone(),
361 Some(region.region_stats.written_bytes.clone()),
362 WriteSource::Request,
363 );
364 region_ctx.lease = lease_now.map(|_| (region, leader_deadline));
365
366 e.insert(region_ctx);
367 }
368 RegionRoleState::Leader(RegionLeaderState::Altering)
369 | RegionRoleState::Leader(RegionLeaderState::Editing) => {
370 debug!(
373 "Region {} is {:?}, add request to pending writes",
374 region.region_id,
375 region.state()
376 );
377 self.stalling_count.add(1);
378 WRITE_STALL_TOTAL.inc();
379 self.stalled_requests.push(sender_req);
380 continue;
381 }
382 RegionRoleState::Leader(RegionLeaderState::EnteringStaging) => {
383 debug!(
384 "Region {} is entering staging, add request to pending writes",
385 region.region_id
386 );
387 self.stalling_count.add(1);
388 WRITE_STALL_TOTAL.inc();
389 self.stalled_requests.push(sender_req);
390 continue;
391 }
392 state => {
393 sender_req.sender.send(
395 RegionStateSnafu {
396 region_id,
397 state,
398 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
399 }
400 .fail(),
401 );
402 continue;
403 }
404 }
405 }
406
407 let region_ctx = region_ctxs.get_mut(®ion_id).unwrap();
409 let Some(region) = self
410 .regions
411 .get_region_or(region_id, &mut sender_req.sender)
412 else {
413 continue;
414 };
415 if region.reject_all_writes_in_staging() {
416 sender_req
417 .sender
418 .send(RejectWriteInStagingSnafu { region_id }.fail());
419 continue;
420 }
421 let expected_version = region.expected_partition_expr_version();
422 if let Err(e) = check_partition_expr_version(
423 region_id,
424 expected_version,
425 sender_req.request.partition_expr_version,
426 ) {
427 sender_req.sender.send(Err(e));
428 continue;
429 }
430
431 if let Err(e) = check_op_type(
432 region_ctx.version().options.append_mode,
433 &sender_req.request,
434 ) {
435 sender_req.sender.send(Err(e));
437
438 continue;
439 }
440
441 let need_fill_missing_columns =
443 if let Some(ref region_metadata) = sender_req.request.region_metadata {
444 region_ctx.version().metadata.schema_version != region_metadata.schema_version
445 } else {
446 true
447 };
448 if need_fill_missing_columns
450 && sender_req.request.primary_key_encoding() == PrimaryKeyEncoding::Dense
451 && let Err(e) = sender_req
452 .request
453 .maybe_fill_missing_columns(®ion_ctx.version().metadata)
454 {
455 sender_req.sender.send(Err(e));
456
457 continue;
458 }
459
460 region_ctx.push_mutation(
462 sender_req.request.op_type as i32,
463 Some(sender_req.request.rows),
464 sender_req.request.hint,
465 sender_req.sender,
466 None,
467 sender_req.request.skip_wal,
468 );
469 }
470 }
471
472 fn process_bulk_requests(
474 &mut self,
475 region_ctxs: &mut HashMap<RegionId, RegionWriteCtx>,
476 requests: &mut Vec<SenderBulkRequest>,
477 lease_now: Option<u64>,
478 ) {
479 let _timer = metrics::REGION_WORKER_HANDLE_WRITE_ELAPSED
480 .with_label_values(&["prepare_bulk_request"])
481 .start_timer();
482 for mut bulk_req in requests.drain(..) {
483 let region_id = bulk_req.region_id;
484 if self.flush_scheduler.has_pending_ddls(region_id) {
486 self.flush_scheduler.add_bulk_request_to_pending(bulk_req);
488 continue;
489 }
490
491 if let hash_map::Entry::Vacant(e) = region_ctxs.entry(region_id) {
493 let Some(region) = self.regions.get_region_or(region_id, &mut bulk_req.sender)
494 else {
495 continue;
496 };
497 match region.state() {
498 RegionRoleState::Leader(RegionLeaderState::Writable)
499 | RegionRoleState::Leader(RegionLeaderState::Staging) => {
500 let leader_deadline = region.leader_deadline.get();
501 if lease_now.is_some_and(|now| now >= leader_deadline) {
502 bulk_req
503 .sender
504 .send(RegionLeaseExpiredSnafu { region_id }.fail());
505 continue;
506 }
507 if region.reject_all_writes_in_staging() {
508 bulk_req
509 .sender
510 .send(RejectWriteInStagingSnafu { region_id }.fail());
511 continue;
512 }
513 let mut region_ctx = RegionWriteCtx::new(
514 region.region_id,
515 ®ion.version_control,
516 region.provider.clone(),
517 Some(region.region_stats.written_bytes.clone()),
518 WriteSource::Request,
519 );
520 region_ctx.lease = lease_now.map(|_| (region, leader_deadline));
521
522 e.insert(region_ctx);
523 }
524 RegionRoleState::Leader(RegionLeaderState::Altering)
525 | RegionRoleState::Leader(RegionLeaderState::Editing) => {
526 debug!(
529 "Region {} is {:?}, add request to pending writes",
530 region.region_id,
531 region.state()
532 );
533 self.stalling_count.add(1);
534 WRITE_STALL_TOTAL.inc();
535 self.stalled_requests.push_bulk(bulk_req);
536 continue;
537 }
538 state => {
539 bulk_req.sender.send(
541 RegionStateSnafu {
542 region_id,
543 state,
544 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
545 }
546 .fail(),
547 );
548 continue;
549 }
550 }
551 }
552
553 let region_ctx = region_ctxs.get_mut(®ion_id).unwrap();
555 let Some(region) = self.regions.get_region_or(region_id, &mut bulk_req.sender) else {
556 continue;
557 };
558 if region.reject_all_writes_in_staging() {
559 bulk_req
560 .sender
561 .send(RejectWriteInStagingSnafu { region_id }.fail());
562 continue;
563 }
564 let expected_version = region.expected_partition_expr_version();
565 if let Err(e) = check_partition_expr_version(
566 region_id,
567 expected_version,
568 bulk_req.partition_expr_version,
569 ) {
570 bulk_req.sender.send(Err(e));
571 continue;
572 }
573
574 let need_fill_missing_columns =
576 !bulk_req.region_metadata.is_some_and(|aligned_schema| {
577 aligned_schema.schema_version == region_ctx.version().metadata.schema_version
578 });
579
580 if need_fill_missing_columns {
583 let metadata = ®ion_ctx.version().metadata;
584 if let Err(e) = check_bulk_insert_batch(metadata, &bulk_req.request.batch)
585 .and_then(|_| bulk_req.request.fill_missing_columns(metadata))
586 {
587 bulk_req.sender.send(Err(e));
588 continue;
589 }
590 }
591
592 if !region_ctx.push_bulk(bulk_req.sender, bulk_req.request, None, bulk_req.skip_wal) {
594 continue;
595 }
596 }
597 }
598
599 fn fail_writes_after_lease_expiry(&self, region_ctxs: &mut HashMap<RegionId, RegionWriteCtx>) {
602 if !self.node_lease.is_enabled() {
603 return;
604 }
605 let now = self.node_lease.now();
606 for (region_id, region_ctx) in region_ctxs.iter_mut() {
607 let expired = region_ctx.lease.as_ref().is_some_and(|(region, deadline)| {
609 now >= *deadline && now >= region.leader_deadline.get()
610 });
611 if !expired || region_ctx.is_failed() {
612 continue;
613 }
614 region_ctx.fail_waiters(Arc::new(
615 UnacknowledgedWriteSnafu {
616 region_id: *region_id,
617 }
618 .build(),
619 ));
620 }
621 }
622
623 pub(crate) fn should_reject_write(&self) -> bool {
625 self.write_buffer_manager.memory_usage() + self.stalled_requests.estimated_size
627 >= self.config.global_write_buffer_reject_size.as_bytes() as usize
628 }
629
630 fn stall_region_write_requests(
631 &mut self,
632 stalled_region_ids: &HashSet<RegionId>,
633 write_requests: &mut Vec<SenderWriteRequest>,
634 bulk_requests: &mut Vec<SenderBulkRequest>,
635 ) {
636 let mut stalled_count = 0;
637 let mut stalled_write_requests = write_requests
638 .extract_if(.., |req| {
639 stalled_region_ids.contains(&req.request.region_id)
640 })
641 .collect::<Vec<_>>();
642 let mut stalled_bulk_requests = bulk_requests
643 .extract_if(.., |req| stalled_region_ids.contains(&req.region_id))
644 .collect::<Vec<_>>();
645
646 stalled_count += stalled_write_requests.len() + stalled_bulk_requests.len();
647 self.stalled_requests
648 .append(&mut stalled_write_requests, &mut stalled_bulk_requests);
649
650 if stalled_count > 0 {
651 let stalled_count = stalled_count as i64;
652 self.stalling_count.add(stalled_count);
653 WRITE_STALL_TOTAL.inc_by(stalled_count as u64);
654 self.listener.on_write_stall();
655 }
656 }
657}
658
659async fn write_wal<S: LogStore>(
666 wal: &Wal<S>,
667 region_ctxs: &mut HashMap<RegionId, RegionWriteCtx>,
668) -> bool {
669 let mut wal_writer = wal.writer();
670 let mut has_wal_entries = false;
671 for region_ctx in region_ctxs.values_mut() {
672 if region_ctx.skip_wal() {
673 continue;
674 }
675 if let Err(e) = region_ctx.add_wal_entry(&mut wal_writer).map_err(Arc::new) {
676 region_ctx.set_error(e);
677 } else {
678 has_wal_entries = true;
679 }
680 }
681 if !has_wal_entries {
683 return true;
684 }
685 match wal_writer.write_to_wal().await.map_err(Arc::new) {
686 Ok(response) => {
687 for (region_id, region_ctx) in region_ctxs.iter_mut() {
688 if region_ctx.skip_wal() {
689 continue;
690 }
691 if region_ctx.is_failed() {
695 continue;
696 }
697
698 let last_entry_id = response.last_entry_ids.get(region_id).unwrap();
701 region_ctx.set_next_entry_id(last_entry_id + 1);
702 }
703 true
704 }
705 Err(e) => {
706 for (_, mut region_ctx) in region_ctxs.drain() {
708 region_ctx.set_error(e.clone());
709 }
710 false
711 }
712 }
713}
714
715fn reject_write_requests(
717 write_requests: &mut Vec<SenderWriteRequest>,
718 bulk_requests: &mut Vec<SenderBulkRequest>,
719) {
720 WRITE_REJECT_TOTAL.inc_by(write_requests.len() as u64);
721
722 for req in write_requests.drain(..) {
723 req.sender.send(
724 RejectWriteSnafu {
725 region_id: req.request.region_id,
726 }
727 .fail(),
728 );
729 }
730 for req in bulk_requests.drain(..) {
731 let region_id = req.region_id;
732 req.sender.send(RejectWriteSnafu { region_id }.fail());
733 }
734}
735
736fn reject_region_write_requests(
737 rejected_region_ids: &HashSet<RegionId>,
738 write_requests: &mut Vec<SenderWriteRequest>,
739 bulk_requests: &mut Vec<SenderBulkRequest>,
740) {
741 let mut rejected_write_requests = write_requests
742 .extract_if(.., |req| {
743 rejected_region_ids.contains(&req.request.region_id)
744 })
745 .collect::<Vec<_>>();
746 let mut rejected_bulk_requests = bulk_requests
747 .extract_if(.., |req| rejected_region_ids.contains(&req.region_id))
748 .collect::<Vec<_>>();
749 reject_write_requests(&mut rejected_write_requests, &mut rejected_bulk_requests);
750}
751
752fn write_region_ids(
753 write_requests: &[SenderWriteRequest],
754 bulk_requests: &[SenderBulkRequest],
755) -> HashSet<RegionId> {
756 write_requests
757 .iter()
758 .map(|req| req.request.region_id)
759 .chain(bulk_requests.iter().map(|req| req.region_id))
760 .collect()
761}
762
763fn check_op_type(append_mode: bool, request: &WriteRequest) -> Result<()> {
765 if append_mode {
766 ensure!(
767 request.op_type == OpType::Put,
768 InvalidRequestSnafu {
769 region_id: request.region_id,
770 reason: "DELETE is not allowed under append mode",
771 }
772 );
773 }
774
775 Ok(())
776}
777
778fn check_partition_expr_version(
779 region_id: RegionId,
780 expected_version: u64,
781 request_version: Option<u64>,
782) -> Result<()> {
783 let request_version = match request_version {
784 None => return Ok(()),
785 Some(value) => value,
786 };
787 if request_version != expected_version {
788 return PartitionExprVersionMismatchSnafu {
789 region_id,
790 request_version,
791 expected_version,
792 }
793 .fail();
794 }
795 Ok(())
796}
797
798#[cfg(test)]
799mod tests {
800 use api::v1::helper::{tag_column_schema, time_index_column_schema};
801 use api::v1::value::ValueData;
802 use api::v1::{ColumnDataType, Row, Rows};
803 use common_recordbatch::DfRecordBatch;
804 use datatypes::arrow::array::{ArrayRef, StringArray, TimestampMillisecondArray};
805 use datatypes::arrow::datatypes::{DataType, Field, Schema};
806 use futures::stream;
807 use log_store::error::{
808 Error as LogStoreError, IllegalStateSnafu, InvalidProviderSnafu, Result as LogStoreResult,
809 };
810 use store_api::logstore::entry::{Entry, NaiveEntry};
811 use store_api::logstore::provider::Provider;
812 use store_api::logstore::{AppendBatchResponse, EntryId, SendableEntryStream};
813 use store_api::region_request::AffectedRows;
814 use tokio::sync::oneshot;
815
816 use super::*;
817 use crate::memtable::bulk::part::BulkPart;
818 use crate::request::OptionOutputTx;
819 use crate::test_util::ts_ms_value;
820 use crate::test_util::version_util::VersionControlBuilder;
821
822 fn new_bulk_part(num_rows: i64) -> BulkPart {
823 let schema = Arc::new(Schema::new(vec![
824 Field::new("tag_0", DataType::Utf8, true),
825 Field::new(
826 "ts",
827 DataType::Timestamp(datatypes::arrow::datatypes::TimeUnit::Millisecond, None),
828 false,
829 ),
830 ]));
831 let tag = Arc::new(StringArray::from_iter_values(
832 (0..num_rows).map(|value| value.to_string()),
833 )) as ArrayRef;
834 let ts = Arc::new(TimestampMillisecondArray::from(
835 (0..num_rows).collect::<Vec<_>>(),
836 )) as ArrayRef;
837 let batch = DfRecordBatch::try_new(schema, vec![tag, ts]).unwrap();
838
839 BulkPart {
840 batch,
841 max_timestamp: num_rows - 1,
842 min_timestamp: 0,
843 sequence: 0,
844 min_sequence: 0,
845 timestamp_index: 1,
846 raw_data: None,
847 }
848 }
849
850 #[derive(Debug, Default)]
853 struct MockLogStore {
854 failing_region: Option<RegionId>,
855 fail_append: bool,
856 }
857
858 #[async_trait::async_trait]
859 impl LogStore for MockLogStore {
860 type Error = LogStoreError;
861
862 async fn stop(&self) -> LogStoreResult<()> {
863 Ok(())
864 }
865
866 async fn append_batch(&self, entries: Vec<Entry>) -> LogStoreResult<AppendBatchResponse> {
867 if self.fail_append {
868 return IllegalStateSnafu {}.fail();
869 }
870 let mut last_entry_ids = HashMap::new();
871 for entry in &entries {
872 let last_entry_id = last_entry_ids.entry(entry.region_id()).or_insert(0);
873 *last_entry_id = entry.entry_id().max(*last_entry_id);
874 }
875 Ok(AppendBatchResponse { last_entry_ids })
876 }
877
878 async fn read(
879 &self,
880 _provider: &Provider,
881 _entry_id: EntryId,
882 ) -> LogStoreResult<SendableEntryStream<'static, Entry, Self::Error>> {
883 Ok(Box::pin(stream::empty()))
884 }
885
886 async fn create_namespace(&self, _ns: &Provider) -> LogStoreResult<()> {
887 Ok(())
888 }
889
890 async fn delete_namespace(&self, _ns: &Provider) -> LogStoreResult<()> {
891 Ok(())
892 }
893
894 async fn list_namespaces(&self) -> LogStoreResult<Vec<Provider>> {
895 Ok(vec![])
896 }
897
898 async fn obsolete(
899 &self,
900 _provider: &Provider,
901 _region_id: RegionId,
902 _entry_id: EntryId,
903 ) -> LogStoreResult<()> {
904 Ok(())
905 }
906
907 async fn obsolete_all(
908 &self,
909 _provider: &Provider,
910 _region_id: RegionId,
911 ) -> LogStoreResult<()> {
912 Ok(())
913 }
914
915 fn entry(
916 &self,
917 data: Vec<u8>,
918 entry_id: EntryId,
919 region_id: RegionId,
920 provider: &Provider,
921 ) -> LogStoreResult<Entry> {
922 if self.failing_region == Some(region_id) {
923 return InvalidProviderSnafu {
924 expected: "raft_engine",
925 actual: "mock",
926 }
927 .fail();
928 }
929 Ok(Entry::Naive(NaiveEntry {
930 provider: provider.clone(),
931 region_id,
932 entry_id,
933 data,
934 }))
935 }
936
937 fn latest_entry_id(&self, _provider: &Provider) -> LogStoreResult<EntryId> {
938 Ok(0)
939 }
940 }
941
942 fn new_region_ctx(
943 region_id: RegionId,
944 skip_wal: bool,
945 ) -> (RegionWriteCtx, oneshot::Receiver<Result<AffectedRows>>) {
946 let version_control = Arc::new(VersionControlBuilder::new().build());
947 let mut ctx = RegionWriteCtx::new(
948 region_id,
949 &version_control,
950 Provider::raft_engine_provider(region_id.as_u64()),
951 None,
952 WriteSource::Request,
953 );
954 let (tx, rx) = oneshot::channel();
955 ctx.push_mutation(
956 OpType::Put as i32,
957 Some(Rows {
958 schema: vec![
959 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
960 tag_column_schema("tag_0", ColumnDataType::String),
961 ],
962 rows: vec![Row {
963 values: vec![
964 ts_ms_value(0),
965 api::v1::Value {
966 value_data: Some(ValueData::StringValue("a".to_string())),
967 },
968 ],
969 }],
970 }),
971 None,
972 OptionOutputTx::from(tx),
973 None,
974 skip_wal,
975 );
976 (ctx, rx)
977 }
978
979 #[tokio::test]
980 async fn test_request_skip_wal_does_not_append_empty_batch() {
981 check_request_skip_wal_does_not_append_empty_batch(false, false).await;
984 check_request_skip_wal_does_not_append_empty_batch(false, true).await;
985 check_request_skip_wal_does_not_append_empty_batch(true, false).await;
986 check_request_skip_wal_does_not_append_empty_batch(true, true).await;
987 }
988
989 async fn check_request_skip_wal_does_not_append_empty_batch(skip_wal: bool, bulk: bool) {
990 let region_id = RegionId::new(1, 1);
991 let wal = Wal::new(Arc::new(MockLogStore {
992 fail_append: true,
993 ..Default::default()
994 }));
995 let (ctx, rx) = if bulk {
996 let version_control = Arc::new(VersionControlBuilder::new().build());
997 let mut ctx = RegionWriteCtx::new(
998 region_id,
999 &version_control,
1000 Provider::raft_engine_provider(region_id.as_u64()),
1001 None,
1002 WriteSource::Request,
1003 );
1004 let (tx, rx) = oneshot::channel();
1005 assert!(ctx.push_bulk(OptionOutputTx::from(tx), new_bulk_part(1), None, skip_wal));
1006 (ctx, rx)
1007 } else {
1008 new_region_ctx(region_id, skip_wal)
1009 };
1010 let version_control = ctx.version_control().clone();
1011 let mut contexts = HashMap::from([(region_id, ctx)]);
1012 assert_eq!(write_wal(&wal, &mut contexts).await, skip_wal);
1013 if skip_wal {
1014 let ctx = contexts.get_mut(®ion_id).unwrap();
1015 assert_eq!(ctx.next_entry_id(), 1);
1016 ctx.write_memtables().await;
1017 assert_eq!(version_control.committed_sequence(), 1);
1018 assert_eq!(version_control.current().last_entry_id, 0);
1019 }
1020 drop(contexts);
1021 assert_eq!(rx.await.unwrap().is_ok(), skip_wal);
1022 }
1023
1024 #[tokio::test]
1025 async fn test_write_wal_skips_region_failed_to_build_entry() {
1026 let failing_region = RegionId::new(1, 1);
1027 let ok_region = RegionId::new(1, 2);
1028 let wal = Wal::new(Arc::new(MockLogStore {
1029 failing_region: Some(failing_region),
1030 ..Default::default()
1031 }));
1032
1033 let mut region_ctxs = HashMap::new();
1034 let (ctx, failing_rx) = new_region_ctx(failing_region, false);
1035 let failing_committed_sequence = ctx.version_control().committed_sequence();
1036 region_ctxs.insert(failing_region, ctx);
1037 let (ctx, ok_rx) = new_region_ctx(ok_region, false);
1038 let ok_committed_sequence = ctx.version_control().committed_sequence();
1039 region_ctxs.insert(ok_region, ctx);
1040 let entry_id = region_ctxs[&ok_region].next_entry_id();
1041
1042 assert!(write_wal(&wal, &mut region_ctxs).await);
1044
1045 assert!(region_ctxs[&failing_region].is_failed());
1046 assert!(!region_ctxs[&ok_region].is_failed());
1047 assert_eq!(entry_id + 1, region_ctxs[&ok_region].next_entry_id());
1048
1049 for region_ctx in region_ctxs.values_mut() {
1050 region_ctx.write_memtables().await;
1051 }
1052
1053 assert_eq!(
1054 failing_committed_sequence,
1055 region_ctxs[&failing_region]
1056 .version_control()
1057 .committed_sequence()
1058 );
1059 assert_eq!(
1060 ok_committed_sequence + 1,
1061 region_ctxs[&ok_region]
1062 .version_control()
1063 .committed_sequence()
1064 );
1065
1066 drop(region_ctxs);
1067 assert!(failing_rx.await.unwrap().is_err());
1068 assert_eq!(1, ok_rx.await.unwrap().unwrap());
1069 }
1070
1071 #[tokio::test]
1072 async fn test_bulk_write_sequence_not_committed_before_install_worker_level() {
1073 check_bulk_write_sequence_not_committed_before_install_worker_level(false).await;
1074 check_bulk_write_sequence_not_committed_before_install_worker_level(true).await;
1075 }
1076
1077 async fn check_bulk_write_sequence_not_committed_before_install_worker_level(skip_wal: bool) {
1078 let region_id = RegionId::new(1, 1);
1079 let version_control = Arc::new(VersionControlBuilder::new().build());
1080
1081 let mut region_ctxs = HashMap::new();
1082 let mut ctx = RegionWriteCtx::new(
1083 region_id,
1084 &version_control,
1085 Provider::raft_engine_provider(region_id.as_u64()),
1086 None,
1087 WriteSource::Request,
1088 );
1089 let (tx, rx) = oneshot::channel();
1090 assert!(ctx.push_bulk(OptionOutputTx::from(tx), new_bulk_part(3), None, skip_wal));
1091 region_ctxs.insert(region_id, ctx);
1092
1093 let wal = Wal::new(Arc::new(MockLogStore::default()));
1094 assert!(write_wal(&wal, &mut region_ctxs).await);
1095 assert!(!region_ctxs[®ion_id].is_failed());
1096
1097 let mut barrier = crate::region_write_ctx::test_hooks::arm_bulk_install_barrier(
1098 region_id,
1099 version_control.clone(),
1100 );
1101
1102 let write_handle = tokio::spawn(async move {
1103 let mut region_ctx = region_ctxs.remove(®ion_id).unwrap();
1104 region_ctx.write_memtables().await;
1105 });
1106
1107 tokio::time::timeout(
1108 std::time::Duration::from_secs(10),
1109 barrier.wait_until_reached(),
1110 )
1111 .await
1112 .expect("bulk write never reached the install barrier");
1113
1114 assert_eq!(
1115 0,
1116 version_control.committed_sequence(),
1117 "committed sequence leaked before the bulk part was installed"
1118 );
1119
1120 barrier.release();
1121 write_handle.await.expect("bulk write should complete");
1122 assert_eq!(
1123 3,
1124 version_control.committed_sequence(),
1125 "committed sequence must cover the installed bulk rows"
1126 );
1127
1128 assert_eq!(3, rx.await.unwrap().unwrap());
1129 }
1130
1131 #[tokio::test]
1132 async fn test_write_wal_all_regions_failed_to_build_entries() {
1133 let failing_region = RegionId::new(1, 1);
1134 let wal = Wal::new(Arc::new(MockLogStore {
1135 failing_region: Some(failing_region),
1136 ..Default::default()
1137 }));
1138
1139 let mut region_ctxs = HashMap::new();
1140 let (ctx, rx) = new_region_ctx(failing_region, false);
1141 region_ctxs.insert(failing_region, ctx);
1142
1143 assert!(write_wal(&wal, &mut region_ctxs).await);
1146
1147 assert!(region_ctxs[&failing_region].is_failed());
1148 drop(region_ctxs);
1149 assert!(rx.await.unwrap().is_err());
1150 }
1151
1152 #[tokio::test]
1153 async fn test_write_wal_append_batch_failure() {
1154 let region_id = RegionId::new(1, 1);
1155 let wal = Wal::new(Arc::new(MockLogStore {
1156 fail_append: true,
1157 ..Default::default()
1158 }));
1159
1160 let mut region_ctxs = HashMap::new();
1161 let (ctx, rx) = new_region_ctx(region_id, false);
1162 region_ctxs.insert(region_id, ctx);
1163
1164 assert!(!write_wal(&wal, &mut region_ctxs).await);
1165
1166 assert!(region_ctxs.is_empty());
1168 assert!(rx.await.unwrap().is_err());
1169 }
1170}