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