1use std::mem;
16use std::sync::Arc;
17use std::sync::atomic::{AtomicU64, Ordering};
18
19use api::v1::{BulkWalEntry, Mutation, OpType, Rows, WalEntry, WriteHint};
20use futures::stream::{FuturesUnordered, StreamExt};
21use snafu::ResultExt;
22use store_api::logstore::LogStore;
23use store_api::logstore::provider::Provider;
24use store_api::storage::{RegionId, SequenceNumber};
25
26use crate::error::{Error, Result, UnexpectedSnafu, WriteGroupSnafu};
27use crate::memtable::KeyValues;
28use crate::memtable::bulk::part::BulkPart;
29use crate::metrics;
30use crate::region::MitoRegionRef;
31use crate::region::version::{VersionControlData, VersionControlRef, VersionRef};
32use crate::request::OptionOutputTx;
33use crate::wal::{EntryId, WalWriter};
34
35struct WriteNotify {
37 err: Option<Arc<Error>>,
39 sender: OptionOutputTx,
41 num_rows: usize,
43}
44
45impl WriteNotify {
46 fn new(sender: OptionOutputTx, num_rows: usize) -> WriteNotify {
48 WriteNotify {
49 err: None,
50 sender,
51 num_rows,
52 }
53 }
54
55 fn notify_result(&mut self) {
57 if let Some(err) = &self.err {
58 self.sender
60 .send_mut(Err(err.clone()).context(WriteGroupSnafu));
61 } else {
62 self.sender.send_mut(Ok(self.num_rows));
64 }
65 }
66}
67
68impl Drop for WriteNotify {
69 fn drop(&mut self) {
70 if self.err.is_none() && std::thread::panicking() {
72 self.err = Some(Arc::new(
73 UnexpectedSnafu {
74 reason: "the write was interrupted by a panic",
75 }
76 .build(),
77 ));
78 }
79 self.notify_result();
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub(crate) enum WriteSource {
86 Request,
87 WalReplay,
88}
89
90pub(crate) struct RegionWriteCtx {
92 source: WriteSource,
94 has_skip_wal_writes: bool,
96 region_id: RegionId,
98 version: VersionRef,
100 version_control: VersionControlRef,
102 next_sequence: SequenceNumber,
104 max_sequence: Option<SequenceNumber>,
106 next_entry_id: EntryId,
108 wal_entry: WalEntry,
113 memtable_mutations: Vec<(Mutation, WriteNotify)>,
115 provider: Provider,
117 wal_notifiers: Vec<WriteNotify>,
121 bulk_notifiers: Vec<WriteNotify>,
123 pub(crate) bulk_parts: Vec<BulkPart>,
125 failed: bool,
127
128 pub(crate) put_num: usize,
131 pub(crate) delete_num: usize,
133 pub(crate) written_bytes: Option<Arc<AtomicU64>>,
135 pub(crate) lease: Option<(MitoRegionRef, u64)>,
138}
139
140impl RegionWriteCtx {
141 pub(crate) fn new(
143 region_id: RegionId,
144 version_control: &VersionControlRef,
145 provider: Provider,
146 written_bytes: Option<Arc<AtomicU64>>,
147 source: WriteSource,
148 ) -> RegionWriteCtx {
149 let VersionControlData {
150 version,
151 last_sequence,
152 last_entry_id,
153 ..
154 } = version_control.current();
155
156 RegionWriteCtx {
157 source,
158 has_skip_wal_writes: false,
159 region_id,
160 version,
161 version_control: version_control.clone(),
162 next_sequence: last_sequence + 1,
163 max_sequence: None,
164 next_entry_id: last_entry_id + 1,
165 wal_entry: WalEntry::default(),
166 memtable_mutations: Vec::new(),
167 provider,
168 wal_notifiers: Vec::new(),
169 bulk_notifiers: vec![],
170 failed: false,
171 put_num: 0,
172 delete_num: 0,
173 bulk_parts: vec![],
174 written_bytes,
175 lease: None,
176 }
177 }
178
179 pub(crate) fn push_mutation(
182 &mut self,
183 op_type: i32,
184 rows: Option<Rows>,
185 write_hint: Option<WriteHint>,
186 tx: OptionOutputTx,
187 sequence: Option<SequenceNumber>,
188 skip_wal: bool,
189 ) {
190 if let Some(sequence) = sequence {
191 self.next_sequence = sequence;
192 }
193 let num_rows = rows.as_ref().map(|rows| rows.rows.len()).unwrap_or(0);
194 self.has_skip_wal_writes |= num_rows > 0 && self.is_skip_wal_write(skip_wal);
195 let mutation = Mutation {
196 op_type,
197 sequence: self.next_sequence,
198 rows,
199 write_hint,
200 };
201
202 let notify = WriteNotify::new(tx, num_rows);
205 if skip_wal {
206 self.memtable_mutations.push((mutation, notify));
207 } else {
208 self.wal_entry.mutations.push(mutation);
209 self.wal_notifiers.push(notify);
210 }
211
212 self.advance_sequence(num_rows);
213
214 match OpType::try_from(op_type) {
216 Ok(OpType::Delete) => self.delete_num += num_rows,
217 Ok(OpType::Put) => self.put_num += num_rows,
218 Err(_) => (),
219 }
220 }
221
222 pub(crate) fn add_wal_entry<S: LogStore>(
224 &mut self,
225 wal_writer: &mut WalWriter<S>,
226 ) -> Result<()> {
227 self.reserve_sequences();
229 wal_writer.add_entry(
230 self.region_id,
231 self.next_entry_id,
232 &self.wal_entry,
233 &self.provider,
234 )?;
235 self.next_entry_id += 1;
236 Ok(())
237 }
238
239 pub(crate) fn version(&self) -> &VersionRef {
240 &self.version
241 }
242
243 #[cfg(test)]
244 pub(crate) fn version_control(&self) -> &VersionControlRef {
245 &self.version_control
246 }
247
248 pub(crate) fn skip_wal(&self) -> bool {
250 self.provider == Provider::Noop
251 || self.version.options.skip_wal
252 || (self.wal_entry.mutations.is_empty() && self.wal_entry.bulk_entries.is_empty())
253 }
254
255 pub(crate) fn set_error(&mut self, err: Arc<Error>) {
257 self.fail_waiters(err);
258
259 self.failed = true;
261 }
262
263 pub(crate) fn fail_waiters(&mut self, err: Arc<Error>) {
265 for notify in self
267 .wal_notifiers
268 .iter_mut()
269 .chain(self.memtable_mutations.iter_mut().map(|(_, notify)| notify))
270 {
271 notify.err = Some(err.clone());
272 }
273 for notify in &mut self.bulk_notifiers {
274 notify.err = Some(err.clone());
275 }
276 }
277
278 pub(crate) fn is_failed(&self) -> bool {
280 self.failed
281 }
282
283 pub(crate) fn set_next_entry_id(&mut self, next_entry_id: EntryId) {
285 self.next_entry_id = next_entry_id
286 }
287
288 #[cfg(test)]
290 pub(crate) fn next_entry_id(&self) -> EntryId {
291 self.next_entry_id
292 }
293
294 pub(crate) async fn write_memtables(&mut self) {
296 if self.failed {
297 return;
298 }
299 if self.source == WriteSource::WalReplay || self.skip_wal() {
301 self.reserve_sequences();
302 }
303 if self.has_skip_wal_writes {
305 self.version.memtables.mutable.mark_skip_wal_writes();
306 }
307 self.write_mutations().await;
308 self.write_bulk().await;
309 self.publish_sequence_and_entry_id();
310 }
311
312 fn is_skip_wal_write(&self, request_skip_wal: bool) -> bool {
313 self.source == WriteSource::Request
314 && (request_skip_wal
315 || self.provider == Provider::Noop
316 || self.version.options.skip_wal)
317 }
318
319 async fn write_mutations(&mut self) {
321 debug_assert_eq!(self.wal_notifiers.len(), self.wal_entry.mutations.len());
322
323 if self.failed {
324 return;
325 }
326
327 let mutable_memtable = self.version.memtables.mutable.clone();
328 let prev_memory_usage = if self.written_bytes.is_some() {
329 Some(mutable_memtable.memory_usage())
330 } else {
331 None
332 };
333
334 let mut mutations = mem::take(&mut self.wal_entry.mutations)
335 .into_iter()
336 .zip(&mut self.wal_notifiers)
337 .chain(
338 self.memtable_mutations
339 .iter_mut()
340 .map(|(mutation, notify)| (mem::take(mutation), notify)),
342 )
343 .filter_map(|(mutation, notify)| {
344 let kvs = KeyValues::new(&self.version.metadata, mutation)?;
345 Some((notify, kvs))
346 })
347 .collect::<Vec<_>>();
348
349 if mutations.len() == 1 {
350 if let Err(err) = mutable_memtable.write(&mutations[0].1) {
351 mutations[0].0.err = Some(Arc::new(err));
352 }
353 } else {
354 let mut tasks = FuturesUnordered::new();
355 for (notify, kvs) in mutations {
356 let mutable = mutable_memtable.clone();
357 let task = common_runtime::spawn_blocking_global(move || mutable.write(&kvs));
359 tasks.push(async move { (notify, task.await) });
360 }
361
362 while let Some((notify, result)) = tasks.next().await {
363 if let Err(err) = result.unwrap() {
365 notify.err = Some(Arc::new(err));
366 }
367 }
368 }
369
370 if let Some(written_bytes) = &self.written_bytes {
371 let new_memory_usage = mutable_memtable.memory_usage();
372 let bytes = new_memory_usage.saturating_sub(prev_memory_usage.unwrap_or_default());
373 written_bytes.fetch_add(bytes as u64, Ordering::Relaxed);
374 }
375 }
376
377 pub(crate) fn push_bulk(
378 &mut self,
379 sender: OptionOutputTx,
380 mut bulk: BulkPart,
381 sequence: Option<SequenceNumber>,
382 skip_wal: bool,
383 ) -> bool {
384 if let Some(sequence) = sequence {
385 self.next_sequence = sequence;
386 }
387 bulk.sequence = self.next_sequence;
388 bulk.min_sequence = self.next_sequence;
389 if !skip_wal {
390 let entry = match BulkWalEntry::try_from(&bulk) {
391 Ok(entry) => entry,
392 Err(e) => {
393 sender.send(Err(e));
394 return false;
395 }
396 };
397 self.wal_entry.bulk_entries.push(entry);
398 }
399
400 self.has_skip_wal_writes |= bulk.num_rows() > 0 && self.is_skip_wal_write(skip_wal);
401 self.bulk_notifiers
403 .push(WriteNotify::new(sender, bulk.num_rows()));
404
405 self.advance_sequence(bulk.num_rows());
406 self.bulk_parts.push(bulk);
407 true
408 }
409
410 async fn write_bulk(&mut self) {
411 if self.failed || self.bulk_parts.is_empty() {
412 return;
413 }
414 #[cfg(test)]
415 test_hooks::pause_before_bulk_install(self.region_id, &self.version_control).await;
416 let _timer = metrics::REGION_WORKER_HANDLE_WRITE_ELAPSED
417 .with_label_values(&["write_bulk"])
418 .start_timer();
419
420 let mutable_memtable = &self.version.memtables.mutable;
421 let prev_memory_usage = if self.written_bytes.is_some() {
422 Some(mutable_memtable.memory_usage())
423 } else {
424 None
425 };
426
427 if self.bulk_parts.len() == 1 {
428 let part = self.bulk_parts.swap_remove(0);
429 let num_rows = part.num_rows();
430 if let Err(e) = self.version.memtables.mutable.write_bulk(part) {
431 self.bulk_notifiers[0].err = Some(Arc::new(e));
432 } else {
433 self.put_num += num_rows;
434 }
435 return;
436 }
437
438 let mut tasks = FuturesUnordered::new();
439 for (i, part) in self.bulk_parts.drain(..).enumerate() {
440 let mutable = mutable_memtable.clone();
441 tasks.push(common_runtime::spawn_blocking_global(move || {
442 let num_rows = part.num_rows();
443 (i, mutable.write_bulk(part), num_rows)
444 }));
445 }
446 while let Some(result) = tasks.next().await {
447 let (i, result, num_rows) = result.unwrap();
449 if let Err(err) = result {
450 self.bulk_notifiers[i].err = Some(Arc::new(err));
451 } else {
452 self.put_num += num_rows;
453 }
454 }
455
456 if let Some(written_bytes) = &self.written_bytes {
457 let new_memory_usage = mutable_memtable.memory_usage();
458 let bytes = new_memory_usage.saturating_sub(prev_memory_usage.unwrap_or_default());
459 written_bytes.fetch_add(bytes as u64, Ordering::Relaxed);
460 }
461 }
462
463 fn advance_sequence(&mut self, num_rows: usize) {
465 self.next_sequence += num_rows as u64;
466 if num_rows > 0 {
467 let sequence = self.next_sequence - 1;
468 self.max_sequence = Some(self.max_sequence.map_or(sequence, |max| max.max(sequence)));
469 }
470 }
471
472 fn reserve_sequences(&self) {
473 if let Some(sequence) = self.max_sequence {
474 self.version_control.update_last_sequence(sequence);
475 }
476 }
477
478 fn publish_sequence_and_entry_id(&self) {
483 if self.failed {
484 return;
485 }
486 if let Some(sequence) = self.max_sequence {
487 self.version_control
488 .set_sequence_and_entry_id(sequence, self.next_entry_id - 1);
489 } else {
490 self.version_control.set_entry_id(self.next_entry_id - 1);
492 }
493 }
494}
495
496#[cfg(test)]
498pub(crate) mod test_hooks {
499 use std::sync::Mutex;
500 use std::sync::atomic::{AtomicU64, Ordering};
501
502 use store_api::storage::RegionId;
503 use tokio::sync::watch;
504
505 use crate::region::version::VersionControlRef;
506
507 struct ActiveBarrier {
510 id: u64,
511 target_region_id: RegionId,
514 target_version_control: VersionControlRef,
515 reached: watch::Sender<bool>,
516 release: watch::Sender<bool>,
517 }
518
519 static ACTIVE_BARRIERS: Mutex<Vec<ActiveBarrier>> = Mutex::new(Vec::new());
520 static NEXT_BARRIER_ID: AtomicU64 = AtomicU64::new(1);
521
522 fn lock_active_barriers() -> std::sync::MutexGuard<'static, Vec<ActiveBarrier>> {
523 ACTIVE_BARRIERS
526 .lock()
527 .unwrap_or_else(|poisoned| poisoned.into_inner())
528 }
529
530 pub(crate) struct BulkInstallBarrier {
533 id: u64,
534 reached_rx: watch::Receiver<bool>,
535 release_tx: watch::Sender<bool>,
536 released: bool,
537 }
538
539 impl BulkInstallBarrier {
540 pub(crate) async fn wait_until_reached(&mut self) {
541 if !*self.reached_rx.borrow() {
542 let _ = self.reached_rx.wait_for(|reached| *reached).await;
543 }
544 }
545
546 pub(crate) fn release(&mut self) {
547 if self.released {
548 return;
549 }
550 self.released = true;
551 let _ = self.release_tx.send(true);
552 disarm_barrier(self.id);
553 }
554 }
555
556 impl Drop for BulkInstallBarrier {
557 fn drop(&mut self) {
558 self.release();
559 }
560 }
561
562 pub(crate) fn arm_bulk_install_barrier(
565 target_region_id: RegionId,
566 target_version_control: VersionControlRef,
567 ) -> BulkInstallBarrier {
568 let (reached_tx, reached_rx) = watch::channel(false);
569 let (release_tx, _release_rx) = watch::channel(false);
570 let id = NEXT_BARRIER_ID.fetch_add(1, Ordering::Relaxed);
571 lock_active_barriers().push(ActiveBarrier {
572 id,
573 target_region_id,
574 target_version_control,
575 reached: reached_tx,
576 release: release_tx.clone(),
577 });
578 BulkInstallBarrier {
579 id,
580 reached_rx,
581 release_tx,
582 released: false,
583 }
584 }
585
586 fn disarm_barrier(id: u64) {
587 lock_active_barriers().retain(|barrier| barrier.id != id);
588 }
589
590 pub(crate) async fn pause_before_bulk_install(
593 region_id: RegionId,
594 version_control: &VersionControlRef,
595 ) {
596 let (reached_tx, release_rx) = {
597 let active = lock_active_barriers();
598 let Some(barrier) = active.iter().find(|barrier| {
599 barrier.target_region_id == region_id
600 && std::sync::Arc::ptr_eq(&barrier.target_version_control, version_control)
601 }) else {
602 return;
603 };
604 (barrier.reached.clone(), barrier.release.subscribe())
605 };
606 let _ = reached_tx.send(true);
607 let mut release_rx = release_rx;
608 if !*release_rx.borrow() {
609 let _ = release_rx.wait_for(|released| *released).await;
612 }
613 }
614}
615
616#[cfg(test)]
617mod tests {
618 use std::sync::Arc;
619
620 use common_recordbatch::DfRecordBatch;
621 use datatypes::arrow::array::{ArrayRef, TimestampMillisecondArray};
622 use datatypes::arrow::datatypes::{DataType, Field, Schema};
623 use prost::Message;
624 use store_api::logstore::provider::Provider;
625 use tokio::sync::oneshot;
626
627 use super::*;
628 use crate::error::UnexpectedSnafu;
629 use crate::memtable::bulk::part::BulkPart;
630 use crate::test_util::version_util::VersionControlBuilder;
631
632 #[test]
633 fn test_write_notify_dropped_by_panic_reports_error() {
634 let (tx, rx) = oneshot::channel();
635 let notify = WriteNotify::new(OptionOutputTx::from(tx), 3);
636 std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
637 let _notify = notify;
638 panic!("interrupt the write");
639 }))
640 .unwrap_err();
641 assert!(rx.blocking_recv().unwrap().is_err());
642 }
643
644 #[test]
645 fn test_request_skip_wal_preserves_sequences_and_other_writes() {
646 check_request_skip_wal_preserves_sequences_and_other_writes(false, false);
648 check_request_skip_wal_preserves_sequences_and_other_writes(false, true);
649 check_request_skip_wal_preserves_sequences_and_other_writes(true, false);
650 check_request_skip_wal_preserves_sequences_and_other_writes(true, true);
651 }
652
653 fn check_request_skip_wal_preserves_sequences_and_other_writes(
654 skip_wal: bool,
655 bulk_skip_wal: bool,
656 ) {
657 let builder = VersionControlBuilder::new();
658 let region_id = builder.region_id();
659 let version_control = Arc::new(builder.build());
660 let mut ctx = RegionWriteCtx::new(
661 region_id,
662 &version_control,
663 Provider::raft_engine_provider(region_id.as_u64()),
664 None,
665 WriteSource::Request,
666 );
667 for (op_type, skip, num_rows) in [
668 (OpType::Put, skip_wal, 2),
669 (OpType::Put, false, 3),
670 (OpType::Delete, false, 1),
671 ] {
672 ctx.push_mutation(
673 op_type as i32,
674 Some(Rows {
675 schema: vec![],
676 rows: vec![api::v1::Row::default(); num_rows],
677 }),
678 None,
679 OptionOutputTx::none(),
680 None,
681 skip,
682 );
683 }
684 for (mutation, notify) in ctx
686 .wal_entry
687 .mutations
688 .iter()
689 .zip(&ctx.wal_notifiers)
690 .chain(
691 ctx.memtable_mutations
692 .iter()
693 .map(|(mutation, notify)| (mutation, notify)),
694 )
695 {
696 assert_eq!(mutation.rows.as_ref().unwrap().rows.len(), notify.num_rows);
697 }
698 assert!(ctx.push_bulk(OptionOutputTx::none(), new_bulk_part(), None, bulk_skip_wal));
699 assert!(!ctx.skip_wal());
700 assert_eq!(ctx.next_sequence, 9);
701 assert_eq!(
702 ctx.wal_entry.bulk_entries.len(),
703 usize::from(!bulk_skip_wal)
704 );
705 assert_eq!(ctx.bulk_parts[0].sequence, 7);
706 assert_eq!(ctx.bulk_parts[0].min_sequence, 7);
707 let sequences: Vec<_> = ctx.wal_entry.mutations.iter().map(|m| m.sequence).collect();
708 assert_eq!(sequences, if skip_wal { vec![3, 6] } else { vec![1, 3, 6] });
709 let encoded = crate::wal::encoder::WalEntryEncoder::new().encode_to_vec(&ctx.wal_entry);
711 let decoded = WalEntry::decode(encoded.as_slice()).unwrap();
712 assert_eq!(
713 decoded
714 .mutations
715 .iter()
716 .map(|m| m.sequence)
717 .collect::<Vec<_>>(),
718 sequences
719 );
720 assert_eq!(decoded.bulk_entries, ctx.wal_entry.bulk_entries);
721 assert_eq!(ctx.wal_entry.mutations.len(), if skip_wal { 2 } else { 3 });
722 assert_eq!(ctx.memtable_mutations.len(), usize::from(skip_wal));
723 if skip_wal {
724 assert_eq!(ctx.memtable_mutations[0].0.sequence, 1);
725 }
726 assert_eq!(
727 ctx.wal_entry.mutations.last().unwrap().op_type,
728 OpType::Delete as i32
729 );
730 }
731
732 #[test]
733 fn test_internal_delete_respects_skip_wal_flag() {
734 check_internal_delete_respects_skip_wal_flag(false);
735 check_internal_delete_respects_skip_wal_flag(true);
736 }
737
738 fn check_internal_delete_respects_skip_wal_flag(skip_wal: bool) {
739 let builder = VersionControlBuilder::new();
740 let region_id = builder.region_id();
741 let version_control = Arc::new(builder.build());
742 let mut ctx = RegionWriteCtx::new(
743 region_id,
744 &version_control,
745 Provider::raft_engine_provider(region_id.as_u64()),
746 None,
747 WriteSource::Request,
748 );
749 ctx.push_mutation(
750 OpType::Delete as i32,
751 Some(Rows {
752 schema: vec![],
753 rows: vec![api::v1::Row::default(); 2],
754 }),
755 None,
756 OptionOutputTx::none(),
757 None,
758 skip_wal,
759 );
760 assert_eq!(ctx.skip_wal(), skip_wal);
761 assert_eq!(ctx.next_sequence, 3);
762 assert_eq!(ctx.delete_num, 2);
763 assert_eq!(ctx.wal_entry.mutations.len(), usize::from(!skip_wal));
764 assert_eq!(ctx.memtable_mutations.len(), usize::from(skip_wal));
765 let encoded = crate::wal::encoder::WalEntryEncoder::new().encode_to_vec(&ctx.wal_entry);
766 let decoded = WalEntry::decode(encoded.as_slice()).unwrap();
767 if skip_wal {
768 assert!(decoded.mutations.is_empty());
769 } else {
770 assert_eq!(decoded, ctx.wal_entry);
771 }
772 }
773
774 #[test]
775 fn test_all_request_skip_wal_keeps_entry_id_and_propagates_errors() {
776 let builder = VersionControlBuilder::new();
777 let region_id = builder.region_id();
778 let version_control = Arc::new(builder.build());
779 let mut ctx = RegionWriteCtx::new(
780 region_id,
781 &version_control,
782 Provider::raft_engine_provider(region_id.as_u64()),
783 None,
784 WriteSource::Request,
785 );
786 let (tx, rx) = oneshot::channel();
787 ctx.push_mutation(
788 OpType::Put as i32,
789 Some(Rows {
790 schema: vec![],
791 rows: vec![api::v1::Row::default(); 2],
792 }),
793 None,
794 OptionOutputTx::from(tx),
795 None,
796 true,
797 );
798 assert!(ctx.skip_wal());
799 assert!(ctx.wal_entry.mutations.is_empty());
800 assert_eq!(ctx.memtable_mutations.len(), 1);
801 assert_eq!(ctx.next_entry_id(), 1);
802 assert_eq!(ctx.next_sequence, 3);
803 ctx.set_error(Arc::new(
804 UnexpectedSnafu {
805 reason: "wal failed".to_string(),
806 }
807 .build(),
808 ));
809 drop(ctx);
810 assert!(rx.blocking_recv().unwrap().is_err());
811 }
812
813 #[test]
814 fn test_bulk_skip_wal_preserves_sequences() {
815 check_bulk_skip_wal_preserves_sequences(false);
816 check_bulk_skip_wal_preserves_sequences(true);
817 }
818
819 fn check_bulk_skip_wal_preserves_sequences(skip_wal: bool) {
820 let builder = VersionControlBuilder::new();
821 let region_id = builder.region_id();
822 let version_control = Arc::new(builder.build());
823 let mut ctx = RegionWriteCtx::new(
824 region_id,
825 &version_control,
826 Provider::raft_engine_provider(region_id.as_u64()),
827 None,
828 WriteSource::Request,
829 );
830 for skip in [skip_wal, false, skip_wal] {
832 assert!(ctx.push_bulk(OptionOutputTx::none(), new_bulk_part(), None, skip));
833 }
834 assert_eq!(ctx.next_sequence, 7);
835 assert_eq!(ctx.bulk_notifiers.len(), 3);
836 assert_eq!(
837 ctx.bulk_parts
838 .iter()
839 .map(|p| p.sequence)
840 .collect::<Vec<_>>(),
841 vec![1, 3, 5]
842 );
843 let encoded = crate::wal::encoder::WalEntryEncoder::new().encode_to_vec(&ctx.wal_entry);
844 let decoded = WalEntry::decode(encoded.as_slice()).unwrap();
845 assert_eq!(
846 decoded
847 .bulk_entries
848 .iter()
849 .map(|e| e.sequence)
850 .collect::<Vec<_>>(),
851 if skip_wal { vec![3] } else { vec![1, 3, 5] }
852 );
853 }
854
855 #[test]
856 fn test_set_error_marks_bulk_notifiers_failed() {
857 check_set_error_marks_bulk_notifiers_failed(false);
858 check_set_error_marks_bulk_notifiers_failed(true);
859 }
860
861 fn check_set_error_marks_bulk_notifiers_failed(skip_wal: bool) {
862 let builder = VersionControlBuilder::new();
863 let region_id = builder.region_id();
864 let version_control = Arc::new(builder.build());
865 let mut ctx = RegionWriteCtx::new(
866 region_id,
867 &version_control,
868 Provider::noop_provider(),
869 None,
870 WriteSource::Request,
871 );
872 let (tx, rx) = oneshot::channel();
873
874 assert!(ctx.push_bulk(OptionOutputTx::from(tx), new_bulk_part(), None, skip_wal));
875 assert_eq!(ctx.wal_entry.bulk_entries.len(), usize::from(!skip_wal));
876 assert_eq!(ctx.bulk_parts.len(), 1);
877 ctx.set_error(Arc::new(
878 UnexpectedSnafu {
879 reason: "wal failed".to_string(),
880 }
881 .build(),
882 ));
883 drop(ctx);
884
885 let result = rx.blocking_recv().unwrap();
886 assert!(result.is_err(), "bulk notifier should report WAL error");
887 }
888
889 #[tokio::test]
890 async fn test_skip_wal_writes_source_and_policy() {
891 use api::v1::helper::{tag_column_schema, time_index_column_schema};
892 use api::v1::value::ValueData;
893 use api::v1::{ColumnDataType, Row, Value};
894
895 for source in [WriteSource::Request, WriteSource::WalReplay] {
896 for skip_wal in [false, true] {
897 for bulk in [false, true] {
898 for fail_before_install in [false, true] {
899 let builder = VersionControlBuilder::new();
900 let region_id = builder.region_id();
901 let version_control = Arc::new(builder.build());
902 let mut ctx = RegionWriteCtx::new(
903 region_id,
904 &version_control,
905 Provider::raft_engine_provider(region_id.as_u64()),
906 None,
907 source,
908 );
909 let (tx, rx) = oneshot::channel();
910 if bulk {
911 let mut part = new_bulk_part();
912 part.fill_missing_columns(&ctx.version.metadata).unwrap();
913 assert!(ctx.push_bulk(OptionOutputTx::from(tx), part, None, skip_wal));
914 } else {
915 ctx.push_mutation(
916 OpType::Put as i32,
917 Some(Rows {
918 schema: vec![
919 time_index_column_schema(
920 "ts",
921 ColumnDataType::TimestampMillisecond,
922 ),
923 tag_column_schema("tag_0", ColumnDataType::String),
924 ],
925 rows: vec![Row {
926 values: vec![
927 crate::test_util::ts_ms_value(0),
928 Value {
929 value_data: Some(ValueData::StringValue(
930 "a".to_string(),
931 )),
932 },
933 ],
934 }],
935 }),
936 None,
937 OptionOutputTx::from(tx),
938 None,
939 skip_wal,
940 );
941 }
942 if fail_before_install {
943 ctx.set_error(Arc::new(
944 UnexpectedSnafu {
945 reason: "WAL append failed".to_string(),
946 }
947 .build(),
948 ));
949 }
950 ctx.write_memtables().await;
951 assert_eq!(
952 version_control
953 .current()
954 .version
955 .memtables
956 .has_skip_wal_writes(),
957 source == WriteSource::Request && skip_wal && !fail_before_install,
958 "source={source:?}, skip_wal={skip_wal}, bulk={bulk}, failed={fail_before_install}",
959 );
960 assert_eq!(
961 version_control.committed_sequence(),
962 if fail_before_install {
963 0
964 } else if bulk {
965 2
966 } else {
967 1
968 }
969 );
970 drop(ctx);
971 assert_eq!(rx.await.unwrap().is_err(), fail_before_install);
972 }
973 }
974 }
975 }
976 }
977
978 #[test]
979 fn test_skip_wal_writes_region_policy_and_empty_requests() {
980 for source in [WriteSource::Request, WriteSource::WalReplay] {
981 for noop in [false, true] {
982 for region_skip_wal in [false, true] {
983 let builder = VersionControlBuilder::new();
984 let region_id = builder.region_id();
985 let mut version = builder.build_version();
986 version.options.skip_wal = region_skip_wal;
987 let version_control =
988 Arc::new(crate::region::version::VersionControl::new(version));
989 let provider = if noop {
990 Provider::Noop
991 } else {
992 Provider::raft_engine_provider(region_id.as_u64())
993 };
994 let mut ctx =
995 RegionWriteCtx::new(region_id, &version_control, provider, None, source);
996 ctx.push_mutation(
997 OpType::Put as i32,
998 None,
999 None,
1000 OptionOutputTx::none(),
1001 None,
1002 true,
1003 );
1004 assert!(!ctx.has_skip_wal_writes);
1005 ctx.push_mutation(
1006 OpType::Put as i32,
1007 Some(Rows {
1008 schema: vec![],
1009 rows: vec![api::v1::Row::default()],
1010 }),
1011 None,
1012 OptionOutputTx::none(),
1013 None,
1014 false,
1015 );
1016 assert_eq!(
1017 ctx.has_skip_wal_writes,
1018 source == WriteSource::Request && (noop || region_skip_wal)
1019 );
1020 }
1021 }
1022 }
1023 }
1024
1025 fn new_bulk_part() -> BulkPart {
1026 let schema = Arc::new(Schema::new(vec![Field::new(
1027 "ts",
1028 DataType::Timestamp(datatypes::arrow::datatypes::TimeUnit::Millisecond, None),
1029 false,
1030 )]));
1031 let arrays = vec![Arc::new(TimestampMillisecondArray::from(vec![1, 2])) as ArrayRef];
1032 let batch = DfRecordBatch::try_new(schema, arrays).unwrap();
1033
1034 BulkPart {
1035 batch,
1036 max_timestamp: 2,
1037 min_timestamp: 1,
1038 sequence: 0,
1039 min_sequence: 0,
1040 timestamp_index: 0,
1041 raw_data: None,
1042 }
1043 }
1044
1045 fn sequence_rows(num_rows: usize) -> Rows {
1046 use api::v1::helper::{tag_column_schema, time_index_column_schema};
1047 Rows {
1048 schema: vec![
1049 time_index_column_schema("ts", api::v1::ColumnDataType::TimestampMillisecond),
1050 tag_column_schema("tag_0", api::v1::ColumnDataType::String),
1051 ],
1052 rows: (0..num_rows)
1053 .map(|i| api::v1::Row {
1054 values: vec![
1055 crate::test_util::ts_ms_value(i as i64),
1056 api::v1::Value {
1057 value_data: Some(api::v1::value::ValueData::StringValue(
1058 "a".to_string(),
1059 )),
1060 },
1061 ],
1062 })
1063 .collect(),
1064 }
1065 }
1066
1067 #[tokio::test]
1068 async fn test_replay_sequence_maximum_below_allocation_floor() {
1069 let builder = VersionControlBuilder::new();
1070 let region_id = builder.region_id();
1071 let vc = Arc::new(builder.build());
1072 vc.update_last_sequence(100);
1073 let provider = Provider::raft_engine_provider(region_id.as_u64());
1074 let mut ctx = RegionWriteCtx::new(
1075 region_id,
1076 &vc,
1077 provider.clone(),
1078 None,
1079 WriteSource::WalReplay,
1080 );
1081 for (sequence, num_rows) in [(40, 2), (3, 1)] {
1082 ctx.push_mutation(
1083 OpType::Put as i32,
1084 Some(sequence_rows(num_rows)),
1085 None,
1086 OptionOutputTx::none(),
1087 Some(sequence),
1088 false,
1089 );
1090 }
1091 for sequence in [10, 2] {
1092 let mut part = new_bulk_part();
1093 part.fill_missing_columns(&ctx.version.metadata).unwrap();
1094 assert!(ctx.push_bulk(OptionOutputTx::none(), part, Some(sequence), false));
1095 }
1096 assert_eq!(ctx.wal_entry.mutations[0].sequence, 40);
1097 assert_eq!(ctx.bulk_parts[0].sequence, 10);
1098 assert_eq!(ctx.bulk_parts[1].sequence, 2);
1099 assert_eq!(vc.committed_sequence(), 0);
1100 ctx.set_next_entry_id(8);
1101 ctx.write_memtables().await;
1102 assert_eq!(vc.committed_sequence(), 41);
1103 assert_eq!(vc.current().last_sequence, 100);
1104 assert_eq!(vc.current().last_entry_id, 7);
1105
1106 let mut ctx = RegionWriteCtx::new(region_id, &vc, provider, None, WriteSource::Request);
1107 ctx.push_mutation(
1108 OpType::Put as i32,
1109 Some(sequence_rows(2)),
1110 None,
1111 OptionOutputTx::none(),
1112 None,
1113 true,
1114 );
1115 assert_eq!(ctx.memtable_mutations[0].0.sequence, 101);
1116 ctx.write_memtables().await;
1117 assert_eq!(vc.committed_sequence(), 102);
1118 assert_eq!(vc.current().last_sequence, 102);
1119 assert_eq!(vc.current().last_entry_id, 7);
1120 }
1121
1122 #[tokio::test]
1123 async fn test_empty_context_does_not_publish_allocation_floor() {
1124 let builder = VersionControlBuilder::new();
1125 let region_id = builder.region_id();
1126 let vc = Arc::new(builder.build());
1127 vc.update_last_sequence(100);
1128 for empty_objects in [false, true] {
1129 let mut ctx =
1130 RegionWriteCtx::new(region_id, &vc, Provider::Noop, None, WriteSource::WalReplay);
1131 if empty_objects {
1132 ctx.push_mutation(
1133 OpType::Put as i32,
1134 Some(sequence_rows(0)),
1135 None,
1136 OptionOutputTx::none(),
1137 Some(500),
1138 false,
1139 );
1140 ctx.push_mutation(
1141 OpType::Put as i32,
1142 None,
1143 None,
1144 OptionOutputTx::none(),
1145 Some(600),
1146 false,
1147 );
1148 let mut part = new_bulk_part();
1149 part.fill_missing_columns(&ctx.version.metadata).unwrap();
1150 part.batch = part.batch.slice(0, 0);
1151 assert!(ctx.push_bulk(OptionOutputTx::none(), part, Some(700), true));
1152 }
1153 ctx.set_next_entry_id(4);
1154 ctx.write_memtables().await;
1155 let current = vc.current();
1156 assert_eq!(current.last_sequence, 100);
1157 assert_eq!(current.committed_sequence, 0);
1158 assert_eq!(current.last_entry_id, 3);
1159 }
1160 }
1161
1162 #[tokio::test]
1163 async fn test_rejected_bulk_does_not_publish_sequence() {
1164 use datatypes::arrow::array::StringDictionaryBuilder;
1165 use datatypes::arrow::datatypes::Int32Type;
1166
1167 let builder = VersionControlBuilder::new();
1168 let vc = Arc::new(builder.build());
1169 vc.update_last_sequence(100);
1170 let mut ctx = RegionWriteCtx::new(
1171 builder.region_id(),
1172 &vc,
1173 Provider::Noop,
1174 None,
1175 WriteSource::Request,
1176 );
1177 let mut part = new_bulk_part();
1178 let mut dictionary = StringDictionaryBuilder::<Int32Type>::new();
1179 dictionary.append("a").unwrap();
1180 dictionary.append("b").unwrap();
1181 let dictionary = Arc::new(dictionary.finish()) as ArrayRef;
1182 let mut fields = part.batch.schema().fields().to_vec();
1183 fields.push(Arc::new(Field::new(
1184 "tag_0",
1185 dictionary.data_type().clone(),
1186 true,
1187 )));
1188 part.batch = DfRecordBatch::try_new(
1189 Arc::new(Schema::new(fields)),
1190 vec![part.batch.column(0).clone(), dictionary],
1191 )
1192 .unwrap();
1193 let (tx, rx) = oneshot::channel();
1194 assert!(!ctx.push_bulk(OptionOutputTx::from(tx), part, Some(500), false));
1195 assert!(rx.await.unwrap().is_err());
1196 ctx.write_memtables().await;
1197 assert_eq!(vc.committed_sequence(), 0);
1198 assert_eq!(vc.current().last_sequence, 100);
1199 }
1200}