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
402 .push(WriteNotify::new(sender, bulk.num_rows()));
403
404 self.advance_sequence(bulk.num_rows());
405 self.bulk_parts.push(bulk);
406 true
407 }
408
409 async fn write_bulk(&mut self) {
410 if self.failed || self.bulk_parts.is_empty() {
411 return;
412 }
413 #[cfg(test)]
414 test_hooks::pause_before_bulk_install(self.region_id, &self.version_control).await;
415 let _timer = metrics::REGION_WORKER_HANDLE_WRITE_ELAPSED
416 .with_label_values(&["write_bulk"])
417 .start_timer();
418
419 let mutable_memtable = &self.version.memtables.mutable;
420 let prev_memory_usage = if self.written_bytes.is_some() {
421 Some(mutable_memtable.memory_usage())
422 } else {
423 None
424 };
425
426 if self.bulk_parts.len() == 1 {
427 let part = self.bulk_parts.swap_remove(0);
428 let num_rows = part.num_rows();
429 if let Err(e) = self.version.memtables.mutable.write_bulk(part) {
430 self.bulk_notifiers[0].err = Some(Arc::new(e));
431 } else {
432 self.put_num += num_rows;
433 }
434 return;
435 }
436
437 let mut tasks = FuturesUnordered::new();
438 for (i, part) in self.bulk_parts.drain(..).enumerate() {
439 let mutable = mutable_memtable.clone();
440 tasks.push(common_runtime::spawn_blocking_global(move || {
441 let num_rows = part.num_rows();
442 (i, mutable.write_bulk(part), num_rows)
443 }));
444 }
445 while let Some(result) = tasks.next().await {
446 let (i, result, num_rows) = result.unwrap();
448 if let Err(err) = result {
449 self.bulk_notifiers[i].err = Some(Arc::new(err));
450 } else {
451 self.put_num += num_rows;
452 }
453 }
454
455 if let Some(written_bytes) = &self.written_bytes {
456 let new_memory_usage = mutable_memtable.memory_usage();
457 let bytes = new_memory_usage.saturating_sub(prev_memory_usage.unwrap_or_default());
458 written_bytes.fetch_add(bytes as u64, Ordering::Relaxed);
459 }
460 }
461
462 fn advance_sequence(&mut self, num_rows: usize) {
464 self.next_sequence += num_rows as u64;
465 if num_rows > 0 {
466 let sequence = self.next_sequence - 1;
467 self.max_sequence = Some(self.max_sequence.map_or(sequence, |max| max.max(sequence)));
468 }
469 }
470
471 fn reserve_sequences(&self) {
472 if let Some(sequence) = self.max_sequence {
473 self.version_control.update_last_sequence(sequence);
474 }
475 }
476
477 fn publish_sequence_and_entry_id(&self) {
482 if self.failed {
483 return;
484 }
485 if let Some(sequence) = self.max_sequence {
486 self.version_control
487 .set_sequence_and_entry_id(sequence, self.next_entry_id - 1);
488 } else {
489 self.version_control.set_entry_id(self.next_entry_id - 1);
491 }
492 }
493}
494
495#[cfg(test)]
497pub(crate) mod test_hooks {
498 use std::sync::Mutex;
499 use std::sync::atomic::{AtomicU64, Ordering};
500
501 use store_api::storage::RegionId;
502 use tokio::sync::watch;
503
504 use crate::region::version::VersionControlRef;
505
506 struct ActiveBarrier {
509 id: u64,
510 target_region_id: RegionId,
513 target_version_control: VersionControlRef,
514 reached: watch::Sender<bool>,
515 release: watch::Sender<bool>,
516 }
517
518 static ACTIVE_BARRIERS: Mutex<Vec<ActiveBarrier>> = Mutex::new(Vec::new());
519 static NEXT_BARRIER_ID: AtomicU64 = AtomicU64::new(1);
520
521 fn lock_active_barriers() -> std::sync::MutexGuard<'static, Vec<ActiveBarrier>> {
522 ACTIVE_BARRIERS
525 .lock()
526 .unwrap_or_else(|poisoned| poisoned.into_inner())
527 }
528
529 pub(crate) struct BulkInstallBarrier {
532 id: u64,
533 reached_rx: watch::Receiver<bool>,
534 release_tx: watch::Sender<bool>,
535 released: bool,
536 }
537
538 impl BulkInstallBarrier {
539 pub(crate) async fn wait_until_reached(&mut self) {
540 if !*self.reached_rx.borrow() {
541 let _ = self.reached_rx.wait_for(|reached| *reached).await;
542 }
543 }
544
545 pub(crate) fn release(&mut self) {
546 if self.released {
547 return;
548 }
549 self.released = true;
550 let _ = self.release_tx.send(true);
551 disarm_barrier(self.id);
552 }
553 }
554
555 impl Drop for BulkInstallBarrier {
556 fn drop(&mut self) {
557 self.release();
558 }
559 }
560
561 pub(crate) fn arm_bulk_install_barrier(
564 target_region_id: RegionId,
565 target_version_control: VersionControlRef,
566 ) -> BulkInstallBarrier {
567 let (reached_tx, reached_rx) = watch::channel(false);
568 let (release_tx, _release_rx) = watch::channel(false);
569 let id = NEXT_BARRIER_ID.fetch_add(1, Ordering::Relaxed);
570 lock_active_barriers().push(ActiveBarrier {
571 id,
572 target_region_id,
573 target_version_control,
574 reached: reached_tx,
575 release: release_tx.clone(),
576 });
577 BulkInstallBarrier {
578 id,
579 reached_rx,
580 release_tx,
581 released: false,
582 }
583 }
584
585 fn disarm_barrier(id: u64) {
586 lock_active_barriers().retain(|barrier| barrier.id != id);
587 }
588
589 pub(crate) async fn pause_before_bulk_install(
592 region_id: RegionId,
593 version_control: &VersionControlRef,
594 ) {
595 let (reached_tx, release_rx) = {
596 let active = lock_active_barriers();
597 let Some(barrier) = active.iter().find(|barrier| {
598 barrier.target_region_id == region_id
599 && std::sync::Arc::ptr_eq(&barrier.target_version_control, version_control)
600 }) else {
601 return;
602 };
603 (barrier.reached.clone(), barrier.release.subscribe())
604 };
605 let _ = reached_tx.send(true);
606 let mut release_rx = release_rx;
607 if !*release_rx.borrow() {
608 let _ = release_rx.wait_for(|released| *released).await;
611 }
612 }
613}
614
615#[cfg(test)]
616mod tests {
617 use std::sync::Arc;
618
619 use common_recordbatch::DfRecordBatch;
620 use datatypes::arrow::array::{ArrayRef, TimestampMillisecondArray};
621 use datatypes::arrow::datatypes::{DataType, Field, Schema};
622 use prost::Message;
623 use store_api::logstore::provider::Provider;
624 use tokio::sync::oneshot;
625
626 use super::*;
627 use crate::error::UnexpectedSnafu;
628 use crate::memtable::bulk::part::BulkPart;
629 use crate::test_util::version_util::VersionControlBuilder;
630
631 #[test]
632 fn test_write_notify_dropped_by_panic_reports_error() {
633 let (tx, rx) = oneshot::channel();
634 let notify = WriteNotify::new(OptionOutputTx::from(tx), 3);
635 std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
636 let _notify = notify;
637 panic!("interrupt the write");
638 }))
639 .unwrap_err();
640 assert!(rx.blocking_recv().unwrap().is_err());
641 }
642
643 #[test]
644 fn test_request_skip_wal_preserves_sequences_and_other_writes() {
645 check_request_skip_wal_preserves_sequences_and_other_writes(false, false);
647 check_request_skip_wal_preserves_sequences_and_other_writes(false, true);
648 check_request_skip_wal_preserves_sequences_and_other_writes(true, false);
649 check_request_skip_wal_preserves_sequences_and_other_writes(true, true);
650 }
651
652 fn check_request_skip_wal_preserves_sequences_and_other_writes(
653 skip_wal: bool,
654 bulk_skip_wal: bool,
655 ) {
656 let builder = VersionControlBuilder::new();
657 let region_id = builder.region_id();
658 let version_control = Arc::new(builder.build());
659 let mut ctx = RegionWriteCtx::new(
660 region_id,
661 &version_control,
662 Provider::raft_engine_provider(region_id.as_u64()),
663 None,
664 WriteSource::Request,
665 );
666 for (op_type, skip, num_rows) in [
667 (OpType::Put, skip_wal, 2),
668 (OpType::Put, false, 3),
669 (OpType::Delete, false, 1),
670 ] {
671 ctx.push_mutation(
672 op_type as i32,
673 Some(Rows {
674 schema: vec![],
675 rows: vec![api::v1::Row::default(); num_rows],
676 }),
677 None,
678 OptionOutputTx::none(),
679 None,
680 skip,
681 );
682 }
683 for (mutation, notify) in ctx
685 .wal_entry
686 .mutations
687 .iter()
688 .zip(&ctx.wal_notifiers)
689 .chain(
690 ctx.memtable_mutations
691 .iter()
692 .map(|(mutation, notify)| (mutation, notify)),
693 )
694 {
695 assert_eq!(mutation.rows.as_ref().unwrap().rows.len(), notify.num_rows);
696 }
697 assert!(ctx.push_bulk(OptionOutputTx::none(), new_bulk_part(), None, bulk_skip_wal));
698 assert!(!ctx.skip_wal());
699 assert_eq!(ctx.next_sequence, 9);
700 assert_eq!(
701 ctx.wal_entry.bulk_entries.len(),
702 usize::from(!bulk_skip_wal)
703 );
704 assert_eq!(ctx.bulk_parts[0].sequence, 7);
705 assert_eq!(ctx.bulk_parts[0].min_sequence, 7);
706 let sequences: Vec<_> = ctx.wal_entry.mutations.iter().map(|m| m.sequence).collect();
707 assert_eq!(sequences, if skip_wal { vec![3, 6] } else { vec![1, 3, 6] });
708 let encoded = crate::wal::encoder::WalEntryEncoder::new().encode_to_vec(&ctx.wal_entry);
710 let decoded = WalEntry::decode(encoded.as_slice()).unwrap();
711 assert_eq!(
712 decoded
713 .mutations
714 .iter()
715 .map(|m| m.sequence)
716 .collect::<Vec<_>>(),
717 sequences
718 );
719 assert_eq!(decoded.bulk_entries, ctx.wal_entry.bulk_entries);
720 assert_eq!(ctx.wal_entry.mutations.len(), if skip_wal { 2 } else { 3 });
721 assert_eq!(ctx.memtable_mutations.len(), usize::from(skip_wal));
722 if skip_wal {
723 assert_eq!(ctx.memtable_mutations[0].0.sequence, 1);
724 }
725 assert_eq!(
726 ctx.wal_entry.mutations.last().unwrap().op_type,
727 OpType::Delete as i32
728 );
729 }
730
731 #[test]
732 fn test_internal_delete_respects_skip_wal_flag() {
733 check_internal_delete_respects_skip_wal_flag(false);
734 check_internal_delete_respects_skip_wal_flag(true);
735 }
736
737 fn check_internal_delete_respects_skip_wal_flag(skip_wal: bool) {
738 let builder = VersionControlBuilder::new();
739 let region_id = builder.region_id();
740 let version_control = Arc::new(builder.build());
741 let mut ctx = RegionWriteCtx::new(
742 region_id,
743 &version_control,
744 Provider::raft_engine_provider(region_id.as_u64()),
745 None,
746 WriteSource::Request,
747 );
748 ctx.push_mutation(
749 OpType::Delete as i32,
750 Some(Rows {
751 schema: vec![],
752 rows: vec![api::v1::Row::default(); 2],
753 }),
754 None,
755 OptionOutputTx::none(),
756 None,
757 skip_wal,
758 );
759 assert_eq!(ctx.skip_wal(), skip_wal);
760 assert_eq!(ctx.next_sequence, 3);
761 assert_eq!(ctx.delete_num, 2);
762 assert_eq!(ctx.wal_entry.mutations.len(), usize::from(!skip_wal));
763 assert_eq!(ctx.memtable_mutations.len(), usize::from(skip_wal));
764 let encoded = crate::wal::encoder::WalEntryEncoder::new().encode_to_vec(&ctx.wal_entry);
765 let decoded = WalEntry::decode(encoded.as_slice()).unwrap();
766 if skip_wal {
767 assert!(decoded.mutations.is_empty());
768 } else {
769 assert_eq!(decoded, ctx.wal_entry);
770 }
771 }
772
773 #[test]
774 fn test_all_request_skip_wal_keeps_entry_id_and_propagates_errors() {
775 let builder = VersionControlBuilder::new();
776 let region_id = builder.region_id();
777 let version_control = Arc::new(builder.build());
778 let mut ctx = RegionWriteCtx::new(
779 region_id,
780 &version_control,
781 Provider::raft_engine_provider(region_id.as_u64()),
782 None,
783 WriteSource::Request,
784 );
785 let (tx, rx) = oneshot::channel();
786 ctx.push_mutation(
787 OpType::Put as i32,
788 Some(Rows {
789 schema: vec![],
790 rows: vec![api::v1::Row::default(); 2],
791 }),
792 None,
793 OptionOutputTx::from(tx),
794 None,
795 true,
796 );
797 assert!(ctx.skip_wal());
798 assert!(ctx.wal_entry.mutations.is_empty());
799 assert_eq!(ctx.memtable_mutations.len(), 1);
800 assert_eq!(ctx.next_entry_id(), 1);
801 assert_eq!(ctx.next_sequence, 3);
802 ctx.set_error(Arc::new(
803 UnexpectedSnafu {
804 reason: "wal failed".to_string(),
805 }
806 .build(),
807 ));
808 drop(ctx);
809 assert!(rx.blocking_recv().unwrap().is_err());
810 }
811
812 #[test]
813 fn test_bulk_skip_wal_preserves_sequences() {
814 check_bulk_skip_wal_preserves_sequences(false);
815 check_bulk_skip_wal_preserves_sequences(true);
816 }
817
818 fn check_bulk_skip_wal_preserves_sequences(skip_wal: bool) {
819 let builder = VersionControlBuilder::new();
820 let region_id = builder.region_id();
821 let version_control = Arc::new(builder.build());
822 let mut ctx = RegionWriteCtx::new(
823 region_id,
824 &version_control,
825 Provider::raft_engine_provider(region_id.as_u64()),
826 None,
827 WriteSource::Request,
828 );
829 for skip in [skip_wal, false, skip_wal] {
831 assert!(ctx.push_bulk(OptionOutputTx::none(), new_bulk_part(), None, skip));
832 }
833 assert_eq!(ctx.next_sequence, 7);
834 assert_eq!(ctx.bulk_notifiers.len(), 3);
835 assert_eq!(
836 ctx.bulk_parts
837 .iter()
838 .map(|p| p.sequence)
839 .collect::<Vec<_>>(),
840 vec![1, 3, 5]
841 );
842 let encoded = crate::wal::encoder::WalEntryEncoder::new().encode_to_vec(&ctx.wal_entry);
843 let decoded = WalEntry::decode(encoded.as_slice()).unwrap();
844 assert_eq!(
845 decoded
846 .bulk_entries
847 .iter()
848 .map(|e| e.sequence)
849 .collect::<Vec<_>>(),
850 if skip_wal { vec![3] } else { vec![1, 3, 5] }
851 );
852 }
853
854 #[test]
855 fn test_set_error_marks_bulk_notifiers_failed() {
856 check_set_error_marks_bulk_notifiers_failed(false);
857 check_set_error_marks_bulk_notifiers_failed(true);
858 }
859
860 fn check_set_error_marks_bulk_notifiers_failed(skip_wal: bool) {
861 let builder = VersionControlBuilder::new();
862 let region_id = builder.region_id();
863 let version_control = Arc::new(builder.build());
864 let mut ctx = RegionWriteCtx::new(
865 region_id,
866 &version_control,
867 Provider::noop_provider(),
868 None,
869 WriteSource::Request,
870 );
871 let (tx, rx) = oneshot::channel();
872
873 assert!(ctx.push_bulk(OptionOutputTx::from(tx), new_bulk_part(), None, skip_wal));
874 assert_eq!(ctx.wal_entry.bulk_entries.len(), usize::from(!skip_wal));
875 assert_eq!(ctx.bulk_parts.len(), 1);
876 ctx.set_error(Arc::new(
877 UnexpectedSnafu {
878 reason: "wal failed".to_string(),
879 }
880 .build(),
881 ));
882 drop(ctx);
883
884 let result = rx.blocking_recv().unwrap();
885 assert!(result.is_err(), "bulk notifier should report WAL error");
886 }
887
888 #[tokio::test]
889 async fn test_skip_wal_writes_source_and_policy() {
890 use api::v1::helper::{tag_column_schema, time_index_column_schema};
891 use api::v1::value::ValueData;
892 use api::v1::{ColumnDataType, Row, Value};
893
894 for source in [WriteSource::Request, WriteSource::WalReplay] {
895 for skip_wal in [false, true] {
896 for bulk in [false, true] {
897 for fail_before_install in [false, true] {
898 let builder = VersionControlBuilder::new();
899 let region_id = builder.region_id();
900 let version_control = Arc::new(builder.build());
901 let mut ctx = RegionWriteCtx::new(
902 region_id,
903 &version_control,
904 Provider::raft_engine_provider(region_id.as_u64()),
905 None,
906 source,
907 );
908 let (tx, rx) = oneshot::channel();
909 if bulk {
910 let mut part = new_bulk_part();
911 part.fill_missing_columns(&ctx.version.metadata).unwrap();
912 assert!(ctx.push_bulk(OptionOutputTx::from(tx), part, None, skip_wal));
913 } else {
914 ctx.push_mutation(
915 OpType::Put as i32,
916 Some(Rows {
917 schema: vec![
918 time_index_column_schema(
919 "ts",
920 ColumnDataType::TimestampMillisecond,
921 ),
922 tag_column_schema("tag_0", ColumnDataType::String),
923 ],
924 rows: vec![Row {
925 values: vec![
926 crate::test_util::ts_ms_value(0),
927 Value {
928 value_data: Some(ValueData::StringValue(
929 "a".to_string(),
930 )),
931 },
932 ],
933 }],
934 }),
935 None,
936 OptionOutputTx::from(tx),
937 None,
938 skip_wal,
939 );
940 }
941 if fail_before_install {
942 ctx.set_error(Arc::new(
943 UnexpectedSnafu {
944 reason: "WAL append failed".to_string(),
945 }
946 .build(),
947 ));
948 }
949 ctx.write_memtables().await;
950 assert_eq!(
951 version_control
952 .current()
953 .version
954 .memtables
955 .has_skip_wal_writes(),
956 source == WriteSource::Request && skip_wal && !fail_before_install,
957 "source={source:?}, skip_wal={skip_wal}, bulk={bulk}, failed={fail_before_install}",
958 );
959 assert_eq!(
960 version_control.committed_sequence(),
961 if fail_before_install {
962 0
963 } else if bulk {
964 2
965 } else {
966 1
967 }
968 );
969 drop(ctx);
970 assert_eq!(rx.await.unwrap().is_err(), fail_before_install);
971 }
972 }
973 }
974 }
975 }
976
977 #[test]
978 fn test_skip_wal_writes_region_policy_and_empty_requests() {
979 for source in [WriteSource::Request, WriteSource::WalReplay] {
980 for noop in [false, true] {
981 for region_skip_wal in [false, true] {
982 let builder = VersionControlBuilder::new();
983 let region_id = builder.region_id();
984 let mut version = builder.build_version();
985 version.options.skip_wal = region_skip_wal;
986 let version_control =
987 Arc::new(crate::region::version::VersionControl::new(version));
988 let provider = if noop {
989 Provider::Noop
990 } else {
991 Provider::raft_engine_provider(region_id.as_u64())
992 };
993 let mut ctx =
994 RegionWriteCtx::new(region_id, &version_control, provider, None, source);
995 ctx.push_mutation(
996 OpType::Put as i32,
997 None,
998 None,
999 OptionOutputTx::none(),
1000 None,
1001 true,
1002 );
1003 assert!(!ctx.has_skip_wal_writes);
1004 ctx.push_mutation(
1005 OpType::Put as i32,
1006 Some(Rows {
1007 schema: vec![],
1008 rows: vec![api::v1::Row::default()],
1009 }),
1010 None,
1011 OptionOutputTx::none(),
1012 None,
1013 false,
1014 );
1015 assert_eq!(
1016 ctx.has_skip_wal_writes,
1017 source == WriteSource::Request && (noop || region_skip_wal)
1018 );
1019 }
1020 }
1021 }
1022 }
1023
1024 fn new_bulk_part() -> BulkPart {
1025 let schema = Arc::new(Schema::new(vec![Field::new(
1026 "ts",
1027 DataType::Timestamp(datatypes::arrow::datatypes::TimeUnit::Millisecond, None),
1028 false,
1029 )]));
1030 let arrays = vec![Arc::new(TimestampMillisecondArray::from(vec![1, 2])) as ArrayRef];
1031 let batch = DfRecordBatch::try_new(schema, arrays).unwrap();
1032
1033 BulkPart {
1034 batch,
1035 max_timestamp: 2,
1036 min_timestamp: 1,
1037 sequence: 0,
1038 min_sequence: 0,
1039 timestamp_index: 0,
1040 raw_data: None,
1041 }
1042 }
1043
1044 fn sequence_rows(num_rows: usize) -> Rows {
1045 use api::v1::helper::{tag_column_schema, time_index_column_schema};
1046 Rows {
1047 schema: vec![
1048 time_index_column_schema("ts", api::v1::ColumnDataType::TimestampMillisecond),
1049 tag_column_schema("tag_0", api::v1::ColumnDataType::String),
1050 ],
1051 rows: (0..num_rows)
1052 .map(|i| api::v1::Row {
1053 values: vec![
1054 crate::test_util::ts_ms_value(i as i64),
1055 api::v1::Value {
1056 value_data: Some(api::v1::value::ValueData::StringValue(
1057 "a".to_string(),
1058 )),
1059 },
1060 ],
1061 })
1062 .collect(),
1063 }
1064 }
1065
1066 #[tokio::test]
1067 async fn test_replay_sequence_maximum_below_allocation_floor() {
1068 let builder = VersionControlBuilder::new();
1069 let region_id = builder.region_id();
1070 let vc = Arc::new(builder.build());
1071 vc.update_last_sequence(100);
1072 let provider = Provider::raft_engine_provider(region_id.as_u64());
1073 let mut ctx = RegionWriteCtx::new(
1074 region_id,
1075 &vc,
1076 provider.clone(),
1077 None,
1078 WriteSource::WalReplay,
1079 );
1080 for (sequence, num_rows) in [(40, 2), (3, 1)] {
1081 ctx.push_mutation(
1082 OpType::Put as i32,
1083 Some(sequence_rows(num_rows)),
1084 None,
1085 OptionOutputTx::none(),
1086 Some(sequence),
1087 false,
1088 );
1089 }
1090 for sequence in [10, 2] {
1091 let mut part = new_bulk_part();
1092 part.fill_missing_columns(&ctx.version.metadata).unwrap();
1093 assert!(ctx.push_bulk(OptionOutputTx::none(), part, Some(sequence), false));
1094 }
1095 assert_eq!(ctx.wal_entry.mutations[0].sequence, 40);
1096 assert_eq!(ctx.bulk_parts[0].sequence, 10);
1097 assert_eq!(ctx.bulk_parts[1].sequence, 2);
1098 assert_eq!(vc.committed_sequence(), 0);
1099 ctx.set_next_entry_id(8);
1100 ctx.write_memtables().await;
1101 assert_eq!(vc.committed_sequence(), 41);
1102 assert_eq!(vc.current().last_sequence, 100);
1103 assert_eq!(vc.current().last_entry_id, 7);
1104
1105 let mut ctx = RegionWriteCtx::new(region_id, &vc, provider, None, WriteSource::Request);
1106 ctx.push_mutation(
1107 OpType::Put as i32,
1108 Some(sequence_rows(2)),
1109 None,
1110 OptionOutputTx::none(),
1111 None,
1112 true,
1113 );
1114 assert_eq!(ctx.memtable_mutations[0].0.sequence, 101);
1115 ctx.write_memtables().await;
1116 assert_eq!(vc.committed_sequence(), 102);
1117 assert_eq!(vc.current().last_sequence, 102);
1118 assert_eq!(vc.current().last_entry_id, 7);
1119 }
1120
1121 #[tokio::test]
1122 async fn test_empty_context_does_not_publish_allocation_floor() {
1123 let builder = VersionControlBuilder::new();
1124 let region_id = builder.region_id();
1125 let vc = Arc::new(builder.build());
1126 vc.update_last_sequence(100);
1127 for empty_objects in [false, true] {
1128 let mut ctx =
1129 RegionWriteCtx::new(region_id, &vc, Provider::Noop, None, WriteSource::WalReplay);
1130 if empty_objects {
1131 ctx.push_mutation(
1132 OpType::Put as i32,
1133 Some(sequence_rows(0)),
1134 None,
1135 OptionOutputTx::none(),
1136 Some(500),
1137 false,
1138 );
1139 ctx.push_mutation(
1140 OpType::Put as i32,
1141 None,
1142 None,
1143 OptionOutputTx::none(),
1144 Some(600),
1145 false,
1146 );
1147 let mut part = new_bulk_part();
1148 part.fill_missing_columns(&ctx.version.metadata).unwrap();
1149 part.batch = part.batch.slice(0, 0);
1150 assert!(ctx.push_bulk(OptionOutputTx::none(), part, Some(700), true));
1151 }
1152 ctx.set_next_entry_id(4);
1153 ctx.write_memtables().await;
1154 let current = vc.current();
1155 assert_eq!(current.last_sequence, 100);
1156 assert_eq!(current.committed_sequence, 0);
1157 assert_eq!(current.last_entry_id, 3);
1158 }
1159 }
1160
1161 #[tokio::test]
1162 async fn test_rejected_bulk_does_not_publish_sequence() {
1163 use datatypes::arrow::array::StringDictionaryBuilder;
1164 use datatypes::arrow::datatypes::Int32Type;
1165
1166 let builder = VersionControlBuilder::new();
1167 let vc = Arc::new(builder.build());
1168 vc.update_last_sequence(100);
1169 let mut ctx = RegionWriteCtx::new(
1170 builder.region_id(),
1171 &vc,
1172 Provider::Noop,
1173 None,
1174 WriteSource::Request,
1175 );
1176 let mut part = new_bulk_part();
1177 let mut dictionary = StringDictionaryBuilder::<Int32Type>::new();
1178 dictionary.append("a").unwrap();
1179 dictionary.append("b").unwrap();
1180 let dictionary = Arc::new(dictionary.finish()) as ArrayRef;
1181 let mut fields = part.batch.schema().fields().to_vec();
1182 fields.push(Arc::new(Field::new(
1183 "tag_0",
1184 dictionary.data_type().clone(),
1185 true,
1186 )));
1187 part.batch = DfRecordBatch::try_new(
1188 Arc::new(Schema::new(fields)),
1189 vec![part.batch.column(0).clone(), dictionary],
1190 )
1191 .unwrap();
1192 let (tx, rx) = oneshot::channel();
1193 assert!(!ctx.push_bulk(OptionOutputTx::from(tx), part, Some(500), false));
1194 assert!(rx.await.unwrap().is_err());
1195 ctx.write_memtables().await;
1196 assert_eq!(vc.committed_sequence(), 0);
1197 assert_eq!(vc.current().last_sequence, 100);
1198 }
1199}