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