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