1use std::collections::{HashMap, HashSet};
16use std::fmt::{Display, Formatter};
17use std::time::Duration;
18
19use serde::{Deserialize, Deserializer, Serialize, Serializer};
20use store_api::region_engine::SyncRegionFromRequest;
21use store_api::region_request::{RegionFlushReason, RegionRequirements};
22use store_api::storage::{FileRefsManifest, GcReport, RegionId, RegionNumber};
23use strum::Display;
24use table::metadata::TableId;
25use table::table_name::TableName;
26
27use crate::flow_name::FlowName;
28use crate::key::schema_name::SchemaName;
29use crate::key::{FlowId, FlowPartitionId};
30use crate::peer::Peer;
31use crate::{DatanodeId, FlownodeId};
32
33#[derive(Eq, Hash, PartialEq, Clone, Debug, Serialize, Deserialize)]
34pub struct RegionIdent {
35 pub datanode_id: DatanodeId,
36 pub table_id: TableId,
37 pub region_number: RegionNumber,
38 pub engine: String,
39}
40
41impl RegionIdent {
42 pub fn get_region_id(&self) -> RegionId {
43 RegionId::new(self.table_id, self.region_number)
44 }
45}
46
47impl Display for RegionIdent {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 write!(
50 f,
51 "RegionIdent(datanode_id='{}', table_id={}, region_number={}, engine = {})",
52 self.datanode_id, self.table_id, self.region_number, self.engine
53 )
54 }
55}
56
57#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
59pub struct DowngradeRegionReply {
60 #[serde(default)]
63 pub region_id: RegionId,
64 pub last_entry_id: Option<u64>,
66 pub metadata_last_entry_id: Option<u64>,
68 pub exists: bool,
70 pub error: Option<String>,
72}
73
74impl Display for DowngradeRegionReply {
75 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
76 write!(
77 f,
78 "(last_entry_id={:?}, exists={}, error={:?})",
79 self.last_entry_id, self.exists, self.error
80 )
81 }
82}
83
84#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
85pub struct SimpleReply {
86 pub result: bool,
87 pub error: Option<String>,
88}
89
90#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
92pub struct FlushRegionReply {
93 pub results: Vec<(RegionId, Result<(), String>)>,
97 pub overall_success: bool,
99}
100
101impl FlushRegionReply {
102 pub fn success_single(region_id: RegionId) -> Self {
104 Self {
105 results: vec![(region_id, Ok(()))],
106 overall_success: true,
107 }
108 }
109
110 pub fn error_single(region_id: RegionId, error: String) -> Self {
112 Self {
113 results: vec![(region_id, Err(error))],
114 overall_success: false,
115 }
116 }
117
118 pub fn from_results(results: Vec<(RegionId, Result<(), String>)>) -> Self {
120 let overall_success = results.iter().all(|(_, result)| result.is_ok());
121 Self {
122 results,
123 overall_success,
124 }
125 }
126
127 pub fn to_simple_reply(&self) -> SimpleReply {
129 if self.overall_success {
130 SimpleReply {
131 result: true,
132 error: None,
133 }
134 } else {
135 let errors: Vec<String> = self
136 .results
137 .iter()
138 .filter_map(|(region_id, result)| {
139 result
140 .as_ref()
141 .err()
142 .map(|err| format!("{}: {}", region_id, err))
143 })
144 .collect();
145 SimpleReply {
146 result: false,
147 error: Some(errors.join("; ")),
148 }
149 }
150 }
151}
152
153impl Display for SimpleReply {
154 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155 write!(f, "(result={}, error={:?})", self.result, self.error)
156 }
157}
158
159impl Display for FlushRegionReply {
160 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
161 let results_str = self
162 .results
163 .iter()
164 .map(|(region_id, result)| match result {
165 Ok(()) => format!("{}:OK", region_id),
166 Err(err) => format!("{}:ERR({})", region_id, err),
167 })
168 .collect::<Vec<_>>()
169 .join(", ");
170 write!(
171 f,
172 "(overall_success={}, results=[{}])",
173 self.overall_success, results_str
174 )
175 }
176}
177
178impl Display for OpenRegion {
179 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180 write!(
181 f,
182 "OpenRegion(region_ident={}, region_storage_path={}, reason={:?})",
183 self.region_ident, self.region_storage_path, self.reason
184 )
185 }
186}
187
188#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
190pub enum OpenRegionReason {
191 RegionMigration,
193 RegionFailover,
195 #[cfg(feature = "enterprise")]
197 RegionFollower,
198}
199
200#[serde_with::serde_as]
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
202pub struct OpenRegion {
203 pub region_ident: RegionIdent,
204 pub region_storage_path: String,
205 pub region_options: HashMap<String, String>,
206 #[serde(default)]
207 #[serde_as(as = "HashMap<serde_with::DisplayFromStr, _>")]
208 pub region_wal_options: HashMap<RegionNumber, String>,
209 #[serde(default)]
210 pub skip_wal_replay: bool,
211 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub reason: Option<OpenRegionReason>,
213 #[serde(default)]
214 pub requirements: RegionRequirements,
215}
216
217impl OpenRegion {
218 pub fn new(
219 region_ident: RegionIdent,
220 path: &str,
221 region_options: HashMap<String, String>,
222 region_wal_options: HashMap<RegionNumber, String>,
223 skip_wal_replay: bool,
224 reason: Option<OpenRegionReason>,
225 requirements: RegionRequirements,
226 ) -> Self {
227 Self {
228 region_ident,
229 region_storage_path: path.to_string(),
230 region_options,
231 region_wal_options,
232 skip_wal_replay,
233 reason,
234 requirements,
235 }
236 }
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
241pub struct DowngradeRegion {
242 pub region_id: RegionId,
244 #[serde(default)]
248 pub flush_timeout: Option<Duration>,
249}
250
251impl Display for DowngradeRegion {
252 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
253 write!(
254 f,
255 "DowngradeRegion(region_id={}, flush_timeout={:?})",
256 self.region_id, self.flush_timeout,
257 )
258 }
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
263pub struct UpgradeRegion {
264 pub region_id: RegionId,
266 pub last_entry_id: Option<u64>,
268 pub metadata_last_entry_id: Option<u64>,
270 #[serde(with = "humantime_serde")]
275 pub replay_timeout: Duration,
276 #[serde(default)]
278 pub location_id: Option<u64>,
279 #[serde(default, skip_serializing_if = "Option::is_none")]
280 pub replay_entry_id: Option<u64>,
281 #[serde(default, skip_serializing_if = "Option::is_none")]
282 pub metadata_replay_entry_id: Option<u64>,
283}
284
285impl UpgradeRegion {
286 pub fn with_replay_entry_id(mut self, replay_entry_id: Option<u64>) -> Self {
288 self.replay_entry_id = replay_entry_id;
289 self
290 }
291
292 pub fn with_metadata_replay_entry_id(mut self, metadata_replay_entry_id: Option<u64>) -> Self {
294 self.metadata_replay_entry_id = metadata_replay_entry_id;
295 self
296 }
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
300pub enum CacheIdent {
302 FlowId(FlowId),
303 FlowNodeAddressChange(u64),
305 FlowName(FlowName),
306 TableId(TableId),
307 TableName(TableName),
308 SchemaName(SchemaName),
309 CreateFlow(CreateFlow),
310 DropFlow(DropFlow),
311 User(UserCacheIdent),
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
316pub struct UserCacheIdent {
317 pub catalog: String,
318 pub username: String,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
322pub struct CreateFlow {
323 pub flow_id: FlowId,
325 pub source_table_ids: Vec<TableId>,
326 pub partition_to_peer_mapping: Vec<(FlowPartitionId, Peer)>,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
331pub struct DropFlow {
332 pub flow_id: FlowId,
333 pub source_table_ids: Vec<TableId>,
334 pub flow_part2node_id: Vec<(FlowPartitionId, FlownodeId)>,
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
340pub enum FlushStrategy {
341 #[default]
343 Sync,
344 Async,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
350pub enum FlushErrorStrategy {
351 #[default]
353 FailFast,
354 TryAll,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
361pub struct FlushRegions {
362 pub region_ids: Vec<RegionId>,
364 #[serde(default)]
366 pub strategy: FlushStrategy,
367 #[serde(default)]
369 pub error_strategy: FlushErrorStrategy,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
372 pub reason: Option<RegionFlushReason>,
373}
374
375impl Display for FlushRegions {
376 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
377 write!(
378 f,
379 "FlushRegions(region_ids={:?}, strategy={:?}, error_strategy={:?}, reason={:?})",
380 self.region_ids, self.strategy, self.error_strategy, self.reason
381 )
382 }
383}
384
385impl FlushRegions {
386 pub fn sync_single(region_id: RegionId) -> Self {
388 Self {
389 region_ids: vec![region_id],
390 strategy: FlushStrategy::Sync,
391 error_strategy: FlushErrorStrategy::FailFast,
392 reason: None,
393 }
394 }
395
396 pub fn async_batch(region_ids: Vec<RegionId>) -> Self {
398 Self {
399 region_ids,
400 strategy: FlushStrategy::Async,
401 error_strategy: FlushErrorStrategy::TryAll,
402 reason: None,
403 }
404 }
405
406 pub fn sync_batch(region_ids: Vec<RegionId>, error_strategy: FlushErrorStrategy) -> Self {
408 Self {
409 region_ids,
410 strategy: FlushStrategy::Sync,
411 error_strategy,
412 reason: None,
413 }
414 }
415
416 pub fn with_reason(mut self, reason: RegionFlushReason) -> Self {
417 self.reason = Some(reason);
418 self
419 }
420
421 pub fn is_single_region(&self) -> bool {
423 self.region_ids.len() == 1
424 }
425
426 pub fn single_region_id(&self) -> Option<RegionId> {
428 if self.is_single_region() {
429 self.region_ids.first().copied()
430 } else {
431 None
432 }
433 }
434
435 pub fn is_hint(&self) -> bool {
437 matches!(self.strategy, FlushStrategy::Async)
438 }
439
440 pub fn is_sync(&self) -> bool {
442 matches!(self.strategy, FlushStrategy::Sync)
443 }
444}
445
446impl From<RegionId> for FlushRegions {
447 fn from(region_id: RegionId) -> Self {
448 Self::sync_single(region_id)
449 }
450}
451
452#[derive(Debug, Deserialize)]
453#[serde(untagged)]
454enum SingleOrMultiple<T> {
455 Single(T),
456 Multiple(Vec<T>),
457}
458
459fn single_or_multiple_from<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
460where
461 D: Deserializer<'de>,
462 T: Deserialize<'de>,
463{
464 let helper = SingleOrMultiple::<T>::deserialize(deserializer)?;
465 Ok(match helper {
466 SingleOrMultiple::Single(x) => vec![x],
467 SingleOrMultiple::Multiple(xs) => xs,
468 })
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
473pub struct GetFileRefs {
474 pub query_regions: Vec<RegionId>,
476 pub related_regions: HashMap<RegionId, HashSet<RegionId>>,
481}
482
483impl Display for GetFileRefs {
484 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
485 write!(f, "GetFileRefs(region_ids={:?})", self.query_regions)
486 }
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
491pub struct GcRegions {
492 pub regions: Vec<RegionId>,
494 pub file_refs_manifest: FileRefsManifest,
496 pub full_file_listing: bool,
498}
499
500impl Display for GcRegions {
501 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
502 write!(
503 f,
504 "GcRegion(regions={:?}, file_refs_count={}, full_file_listing={})",
505 self.regions,
506 self.file_refs_manifest.file_refs.len(),
507 self.full_file_listing
508 )
509 }
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
514pub struct GetFileRefsReply {
515 pub file_refs_manifest: FileRefsManifest,
517 pub success: bool,
519 pub error: Option<String>,
521}
522
523impl Display for GetFileRefsReply {
524 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
525 write!(
526 f,
527 "GetFileRefsReply(success={}, file_refs_count={}, error={:?})",
528 self.success,
529 self.file_refs_manifest.file_refs.len(),
530 self.error
531 )
532 }
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
537pub struct GcRegionsReply {
538 pub result: Result<GcReport, String>,
539}
540
541impl Display for GcRegionsReply {
542 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
543 write!(
544 f,
545 "GcReply(result={})",
546 match &self.result {
547 Ok(report) => format!(
548 "GcReport(deleted_files_count={}, need_retry_regions_count={})",
549 report.deleted_files.len(),
550 report.need_retry_regions.len()
551 ),
552 Err(err) => format!("Err({})", err),
553 }
554 )
555 }
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
559pub struct EnterStagingRegion {
560 pub region_id: RegionId,
561 #[serde(
562 alias = "partition_expr",
563 deserialize_with = "deserialize_enter_staging_partition_directive",
564 serialize_with = "serialize_enter_staging_partition_directive"
565 )]
566 pub partition_directive: StagingPartitionDirective,
567}
568
569impl Display for EnterStagingRegion {
570 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
571 write!(
572 f,
573 "EnterStagingRegion(region_id={}, partition_directive={})",
574 self.region_id, self.partition_directive
575 )
576 }
577}
578
579#[derive(Debug, Clone, PartialEq, Eq)]
580pub enum StagingPartitionDirective {
581 UpdatePartitionExpr(String),
582 RejectAllWrites,
583}
584
585impl StagingPartitionDirective {
586 pub fn as_partition_expr(&self) -> Option<&str> {
588 match self {
589 Self::UpdatePartitionExpr(expr) => Some(expr),
590 Self::RejectAllWrites => None,
591 }
592 }
593}
594
595impl Display for StagingPartitionDirective {
596 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
597 match self {
598 Self::UpdatePartitionExpr(expr) => write!(f, "UpdatePartitionExpr({})", expr),
599 Self::RejectAllWrites => write!(f, "RejectAllWrites"),
600 }
601 }
602}
603
604fn serialize_enter_staging_partition_directive<S>(
605 rule: &StagingPartitionDirective,
606 serializer: S,
607) -> std::result::Result<S::Ok, S::Error>
608where
609 S: Serializer,
610{
611 match rule {
612 StagingPartitionDirective::UpdatePartitionExpr(expr) => serializer.serialize_str(expr),
613 StagingPartitionDirective::RejectAllWrites => {
614 #[derive(Serialize)]
615 struct RejectAllWritesSer<'a> {
616 r#type: &'a str,
617 }
618
619 RejectAllWritesSer {
620 r#type: "reject_all_writes",
621 }
622 .serialize(serializer)
623 }
624 }
625}
626
627fn deserialize_enter_staging_partition_directive<'de, D>(
628 deserializer: D,
629) -> std::result::Result<StagingPartitionDirective, D::Error>
630where
631 D: Deserializer<'de>,
632{
633 #[derive(Deserialize)]
634 #[serde(untagged)]
635 enum Compat {
636 Legacy(String),
637 TypeTagged { r#type: String },
638 }
639
640 match Compat::deserialize(deserializer)? {
641 Compat::Legacy(expr) => Ok(StagingPartitionDirective::UpdatePartitionExpr(expr)),
642 Compat::TypeTagged { r#type } if r#type == "reject_all_writes" => {
643 Ok(StagingPartitionDirective::RejectAllWrites)
644 }
645 Compat::TypeTagged { r#type } => Err(serde::de::Error::custom(format!(
646 "Unknown enter staging partition directive type: {}",
647 r#type
648 ))),
649 }
650}
651
652#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
654pub struct SyncRegion {
655 pub region_id: RegionId,
657 pub request: SyncRegionFromRequest,
659}
660
661impl Display for SyncRegion {
662 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
663 write!(
664 f,
665 "SyncRegion(region_id={}, request={:?})",
666 self.region_id, self.request
667 )
668 }
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
672pub struct RemapManifest {
673 pub region_id: RegionId,
674 pub input_regions: Vec<RegionId>,
676 pub region_mapping: HashMap<RegionId, Vec<RegionId>>,
678 pub new_partition_exprs: HashMap<RegionId, String>,
680}
681
682impl Display for RemapManifest {
683 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
684 write!(
685 f,
686 "RemapManifest(region_id={}, input_regions={:?}, region_mapping={:?}, new_partition_exprs={:?})",
687 self.region_id, self.input_regions, self.region_mapping, self.new_partition_exprs
688 )
689 }
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
693pub struct ApplyStagingManifest {
694 pub region_id: RegionId,
696 pub partition_expr: String,
698 pub central_region_id: RegionId,
700 pub manifest_path: String,
702}
703
704impl Display for ApplyStagingManifest {
705 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
706 write!(
707 f,
708 "ApplyStagingManifest(region_id={}, partition_expr={}, central_region_id={}, manifest_path={})",
709 self.region_id, self.partition_expr, self.central_region_id, self.manifest_path
710 )
711 }
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize, Display, PartialEq)]
715pub enum Instruction {
716 #[serde(deserialize_with = "single_or_multiple_from", alias = "OpenRegion")]
718 OpenRegions(Vec<OpenRegion>),
719 #[serde(deserialize_with = "single_or_multiple_from", alias = "CloseRegion")]
721 CloseRegions(Vec<RegionIdent>),
722 #[serde(deserialize_with = "single_or_multiple_from", alias = "UpgradeRegion")]
724 UpgradeRegions(Vec<UpgradeRegion>),
725 #[serde(
726 deserialize_with = "single_or_multiple_from",
727 alias = "DowngradeRegion"
728 )]
729 DowngradeRegions(Vec<DowngradeRegion>),
731 InvalidateCaches(Vec<CacheIdent>),
733 FlushRegions(FlushRegions),
735 GetFileRefs(GetFileRefs),
737 GcRegions(GcRegions),
739 Suspend,
741 EnterStagingRegions(Vec<EnterStagingRegion>),
743 SyncRegions(Vec<SyncRegion>),
745 RemapManifest(RemapManifest),
747
748 ApplyStagingManifests(Vec<ApplyStagingManifest>),
750}
751
752impl Instruction {
753 pub fn into_open_regions(self) -> Option<Vec<OpenRegion>> {
755 match self {
756 Self::OpenRegions(open_regions) => Some(open_regions),
757 _ => None,
758 }
759 }
760
761 pub fn into_close_regions(self) -> Option<Vec<RegionIdent>> {
763 match self {
764 Self::CloseRegions(close_regions) => Some(close_regions),
765 _ => None,
766 }
767 }
768
769 pub fn into_flush_regions(self) -> Option<FlushRegions> {
771 match self {
772 Self::FlushRegions(flush_regions) => Some(flush_regions),
773 _ => None,
774 }
775 }
776
777 pub fn into_downgrade_regions(self) -> Option<Vec<DowngradeRegion>> {
779 match self {
780 Self::DowngradeRegions(downgrade_region) => Some(downgrade_region),
781 _ => None,
782 }
783 }
784
785 pub fn into_upgrade_regions(self) -> Option<Vec<UpgradeRegion>> {
787 match self {
788 Self::UpgradeRegions(upgrade_region) => Some(upgrade_region),
789 _ => None,
790 }
791 }
792
793 pub fn into_get_file_refs(self) -> Option<GetFileRefs> {
794 match self {
795 Self::GetFileRefs(get_file_refs) => Some(get_file_refs),
796 _ => None,
797 }
798 }
799
800 pub fn into_gc_regions(self) -> Option<GcRegions> {
801 match self {
802 Self::GcRegions(gc_regions) => Some(gc_regions),
803 _ => None,
804 }
805 }
806
807 pub fn into_enter_staging_regions(self) -> Option<Vec<EnterStagingRegion>> {
808 match self {
809 Self::EnterStagingRegions(enter_staging) => Some(enter_staging),
810 _ => None,
811 }
812 }
813
814 pub fn into_sync_regions(self) -> Option<Vec<SyncRegion>> {
815 match self {
816 Self::SyncRegions(sync_regions) => Some(sync_regions),
817 _ => None,
818 }
819 }
820}
821
822#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
824pub struct UpgradeRegionReply {
825 #[serde(default)]
828 pub region_id: RegionId,
829 pub ready: bool,
831 pub exists: bool,
833 pub error: Option<String>,
835}
836
837impl Display for UpgradeRegionReply {
838 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
839 write!(
840 f,
841 "(ready={}, exists={}, error={:?})",
842 self.ready, self.exists, self.error
843 )
844 }
845}
846
847#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
848pub struct DowngradeRegionsReply {
849 pub replies: Vec<DowngradeRegionReply>,
850}
851
852impl DowngradeRegionsReply {
853 pub fn new(replies: Vec<DowngradeRegionReply>) -> Self {
854 Self { replies }
855 }
856
857 pub fn single(reply: DowngradeRegionReply) -> Self {
858 Self::new(vec![reply])
859 }
860}
861
862#[derive(Deserialize)]
863#[serde(untagged)]
864enum DowngradeRegionsCompat {
865 Single(DowngradeRegionReply),
866 Multiple(DowngradeRegionsReply),
867}
868
869fn downgrade_regions_compat_from<'de, D>(deserializer: D) -> Result<DowngradeRegionsReply, D::Error>
870where
871 D: Deserializer<'de>,
872{
873 let helper = DowngradeRegionsCompat::deserialize(deserializer)?;
874 Ok(match helper {
875 DowngradeRegionsCompat::Single(x) => DowngradeRegionsReply::new(vec![x]),
876 DowngradeRegionsCompat::Multiple(reply) => reply,
877 })
878}
879
880#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
881pub struct UpgradeRegionsReply {
882 pub replies: Vec<UpgradeRegionReply>,
883}
884
885impl UpgradeRegionsReply {
886 pub fn new(replies: Vec<UpgradeRegionReply>) -> Self {
887 Self { replies }
888 }
889
890 pub fn single(reply: UpgradeRegionReply) -> Self {
891 Self::new(vec![reply])
892 }
893}
894
895#[derive(Deserialize)]
896#[serde(untagged)]
897enum UpgradeRegionsCompat {
898 Single(UpgradeRegionReply),
899 Multiple(UpgradeRegionsReply),
900}
901
902fn upgrade_regions_compat_from<'de, D>(deserializer: D) -> Result<UpgradeRegionsReply, D::Error>
903where
904 D: Deserializer<'de>,
905{
906 let helper = UpgradeRegionsCompat::deserialize(deserializer)?;
907 Ok(match helper {
908 UpgradeRegionsCompat::Single(x) => UpgradeRegionsReply::new(vec![x]),
909 UpgradeRegionsCompat::Multiple(reply) => reply,
910 })
911}
912
913#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
914pub struct EnterStagingRegionReply {
915 pub region_id: RegionId,
916 pub ready: bool,
918 pub exists: bool,
920 pub error: Option<String>,
922}
923
924#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
925pub struct EnterStagingRegionsReply {
926 pub replies: Vec<EnterStagingRegionReply>,
927}
928
929impl EnterStagingRegionsReply {
930 pub fn new(replies: Vec<EnterStagingRegionReply>) -> Self {
931 Self { replies }
932 }
933}
934
935#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
937pub struct SyncRegionReply {
938 pub region_id: RegionId,
940 pub ready: bool,
942 pub exists: bool,
944 pub error: Option<String>,
946}
947
948#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
950pub struct SyncRegionsReply {
951 pub replies: Vec<SyncRegionReply>,
952}
953
954impl SyncRegionsReply {
955 pub fn new(replies: Vec<SyncRegionReply>) -> Self {
956 Self { replies }
957 }
958}
959
960#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
961pub struct RemapManifestReply {
962 pub exists: bool,
964 pub manifest_paths: HashMap<RegionId, String>,
966 pub error: Option<String>,
968}
969
970impl Display for RemapManifestReply {
971 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
972 write!(
973 f,
974 "RemapManifestReply(manifest_paths={:?}, error={:?})",
975 self.manifest_paths, self.error
976 )
977 }
978}
979
980#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
981pub struct ApplyStagingManifestsReply {
982 pub replies: Vec<ApplyStagingManifestReply>,
983}
984
985impl ApplyStagingManifestsReply {
986 pub fn new(replies: Vec<ApplyStagingManifestReply>) -> Self {
987 Self { replies }
988 }
989}
990
991#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
992pub struct ApplyStagingManifestReply {
993 pub region_id: RegionId,
994 pub ready: bool,
996 pub exists: bool,
998 pub error: Option<String>,
1000}
1001
1002#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
1003#[serde(tag = "type", rename_all = "snake_case")]
1004pub enum InstructionReply {
1005 #[serde(alias = "open_region")]
1006 OpenRegions(SimpleReply),
1007 #[serde(alias = "close_region")]
1008 CloseRegions(SimpleReply),
1009 #[serde(
1010 deserialize_with = "upgrade_regions_compat_from",
1011 alias = "upgrade_region"
1012 )]
1013 UpgradeRegions(UpgradeRegionsReply),
1014 #[serde(
1015 alias = "downgrade_region",
1016 deserialize_with = "downgrade_regions_compat_from"
1017 )]
1018 DowngradeRegions(DowngradeRegionsReply),
1019 FlushRegions(FlushRegionReply),
1020 GetFileRefs(GetFileRefsReply),
1021 GcRegions(GcRegionsReply),
1022 EnterStagingRegions(EnterStagingRegionsReply),
1023 SyncRegions(SyncRegionsReply),
1024 RemapManifest(RemapManifestReply),
1025 ApplyStagingManifests(ApplyStagingManifestsReply),
1026}
1027
1028impl Display for InstructionReply {
1029 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1030 match self {
1031 Self::OpenRegions(reply) => write!(f, "InstructionReply::OpenRegions({})", reply),
1032 Self::CloseRegions(reply) => write!(f, "InstructionReply::CloseRegions({})", reply),
1033 Self::UpgradeRegions(reply) => {
1034 write!(f, "InstructionReply::UpgradeRegions({:?})", reply.replies)
1035 }
1036 Self::DowngradeRegions(reply) => {
1037 write!(f, "InstructionReply::DowngradeRegions({:?})", reply.replies)
1038 }
1039 Self::FlushRegions(reply) => write!(f, "InstructionReply::FlushRegions({})", reply),
1040 Self::GetFileRefs(reply) => write!(f, "InstructionReply::GetFileRefs({})", reply),
1041 Self::GcRegions(reply) => write!(f, "InstructionReply::GcRegion({})", reply),
1042 Self::EnterStagingRegions(reply) => {
1043 write!(
1044 f,
1045 "InstructionReply::EnterStagingRegions({:?})",
1046 reply.replies
1047 )
1048 }
1049 Self::SyncRegions(reply) => {
1050 write!(f, "InstructionReply::SyncRegions({:?})", reply.replies)
1051 }
1052 Self::RemapManifest(reply) => write!(f, "InstructionReply::RemapManifest({})", reply),
1053 Self::ApplyStagingManifests(reply) => write!(
1054 f,
1055 "InstructionReply::ApplyStagingManifests({:?})",
1056 reply.replies
1057 ),
1058 }
1059 }
1060}
1061
1062#[cfg(any(test, feature = "testing"))]
1063impl InstructionReply {
1064 pub fn expect_close_regions_reply(self) -> SimpleReply {
1065 match self {
1066 Self::CloseRegions(reply) => reply,
1067 _ => panic!("Expected CloseRegions reply"),
1068 }
1069 }
1070
1071 pub fn expect_open_regions_reply(self) -> SimpleReply {
1072 match self {
1073 Self::OpenRegions(reply) => reply,
1074 _ => panic!("Expected OpenRegions reply"),
1075 }
1076 }
1077
1078 pub fn expect_upgrade_regions_reply(self) -> Vec<UpgradeRegionReply> {
1079 match self {
1080 Self::UpgradeRegions(reply) => reply.replies,
1081 _ => panic!("Expected UpgradeRegion reply"),
1082 }
1083 }
1084
1085 pub fn expect_downgrade_regions_reply(self) -> Vec<DowngradeRegionReply> {
1086 match self {
1087 Self::DowngradeRegions(reply) => reply.replies,
1088 _ => panic!("Expected DowngradeRegion reply"),
1089 }
1090 }
1091
1092 pub fn expect_flush_regions_reply(self) -> FlushRegionReply {
1093 match self {
1094 Self::FlushRegions(reply) => reply,
1095 _ => panic!("Expected FlushRegions reply"),
1096 }
1097 }
1098
1099 pub fn expect_enter_staging_regions_reply(self) -> Vec<EnterStagingRegionReply> {
1100 match self {
1101 Self::EnterStagingRegions(reply) => reply.replies,
1102 _ => panic!("Expected EnterStagingRegion reply"),
1103 }
1104 }
1105
1106 pub fn expect_sync_regions_reply(self) -> Vec<SyncRegionReply> {
1107 match self {
1108 Self::SyncRegions(reply) => reply.replies,
1109 _ => panic!("Expected SyncRegion reply"),
1110 }
1111 }
1112
1113 pub fn expect_remap_manifest_reply(self) -> RemapManifestReply {
1114 match self {
1115 Self::RemapManifest(reply) => reply,
1116 _ => panic!("Expected RemapManifest reply"),
1117 }
1118 }
1119
1120 pub fn expect_apply_staging_manifests_reply(self) -> Vec<ApplyStagingManifestReply> {
1121 match self {
1122 Self::ApplyStagingManifests(reply) => reply.replies,
1123 _ => panic!("Expected ApplyStagingManifest reply"),
1124 }
1125 }
1126}
1127
1128#[cfg(test)]
1129mod tests {
1130 use std::collections::HashSet;
1131
1132 use store_api::storage::{FileId, FileRef};
1133
1134 use super::*;
1135
1136 #[test]
1137 fn test_serialize_instruction() {
1138 let open_region = Instruction::OpenRegions(vec![OpenRegion::new(
1139 RegionIdent {
1140 datanode_id: 2,
1141 table_id: 1024,
1142 region_number: 1,
1143 engine: "mito2".to_string(),
1144 },
1145 "test/foo",
1146 HashMap::new(),
1147 HashMap::new(),
1148 false,
1149 None,
1150 RegionRequirements::empty(),
1151 )]);
1152
1153 let serialized = serde_json::to_string(&open_region).unwrap();
1154 assert_eq!(
1155 r#"{"OpenRegions":[{"region_ident":{"datanode_id":2,"table_id":1024,"region_number":1,"engine":"mito2"},"region_storage_path":"test/foo","region_options":{},"region_wal_options":{},"skip_wal_replay":false,"requirements":{"object_storage":false}}]}"#,
1156 serialized
1157 );
1158
1159 let close_region = Instruction::CloseRegions(vec![RegionIdent {
1160 datanode_id: 2,
1161 table_id: 1024,
1162 region_number: 1,
1163 engine: "mito2".to_string(),
1164 }]);
1165
1166 let serialized = serde_json::to_string(&close_region).unwrap();
1167 assert_eq!(
1168 r#"{"CloseRegions":[{"datanode_id":2,"table_id":1024,"region_number":1,"engine":"mito2"}]}"#,
1169 serialized
1170 );
1171
1172 let upgrade_region = Instruction::UpgradeRegions(vec![UpgradeRegion {
1173 region_id: RegionId::new(1024, 1),
1174 last_entry_id: None,
1175 metadata_last_entry_id: None,
1176 replay_timeout: Duration::from_millis(1000),
1177 location_id: None,
1178 replay_entry_id: None,
1179 metadata_replay_entry_id: None,
1180 }]);
1181
1182 let serialized = serde_json::to_string(&upgrade_region).unwrap();
1183 assert_eq!(
1184 r#"{"UpgradeRegions":[{"region_id":4398046511105,"last_entry_id":null,"metadata_last_entry_id":null,"replay_timeout":"1s","location_id":null}]}"#,
1185 serialized
1186 );
1187 }
1188
1189 #[test]
1190 fn test_serialize_instruction_reply() {
1191 let downgrade_region_reply = InstructionReply::DowngradeRegions(
1192 DowngradeRegionsReply::single(DowngradeRegionReply {
1193 region_id: RegionId::new(1024, 1),
1194 last_entry_id: None,
1195 metadata_last_entry_id: None,
1196 exists: true,
1197 error: None,
1198 }),
1199 );
1200
1201 let serialized = serde_json::to_string(&downgrade_region_reply).unwrap();
1202 assert_eq!(
1203 r#"{"type":"downgrade_regions","replies":[{"region_id":4398046511105,"last_entry_id":null,"metadata_last_entry_id":null,"exists":true,"error":null}]}"#,
1204 serialized
1205 );
1206
1207 let upgrade_region_reply =
1208 InstructionReply::UpgradeRegions(UpgradeRegionsReply::single(UpgradeRegionReply {
1209 region_id: RegionId::new(1024, 1),
1210 ready: true,
1211 exists: true,
1212 error: None,
1213 }));
1214 let serialized = serde_json::to_string(&upgrade_region_reply).unwrap();
1215 assert_eq!(
1216 r#"{"type":"upgrade_regions","replies":[{"region_id":4398046511105,"ready":true,"exists":true,"error":null}]}"#,
1217 serialized
1218 );
1219 }
1220
1221 #[test]
1222 fn test_deserialize_instruction() {
1223 let open_region_instruction = r#"{"OpenRegion":{"region_ident":{"datanode_id":2,"table_id":1024,"region_number":1,"engine":"mito2"},"region_storage_path":"test/foo","region_options":{},"region_wal_options":{},"skip_wal_replay":false}}"#;
1225 let open_region_instruction: Instruction =
1226 serde_json::from_str(open_region_instruction).unwrap();
1227 let open_region = Instruction::OpenRegions(vec![OpenRegion::new(
1228 RegionIdent {
1229 datanode_id: 2,
1230 table_id: 1024,
1231 region_number: 1,
1232 engine: "mito2".to_string(),
1233 },
1234 "test/foo",
1235 HashMap::new(),
1236 HashMap::new(),
1237 false,
1238 None,
1239 RegionRequirements::empty(),
1240 )]);
1241 assert_eq!(open_region_instruction, open_region);
1242
1243 let close_region_instruction = r#"{"CloseRegion":{"datanode_id":2,"table_id":1024,"region_number":1,"engine":"mito2"}}"#;
1245 let close_region_instruction: Instruction =
1246 serde_json::from_str(close_region_instruction).unwrap();
1247 let close_region = Instruction::CloseRegions(vec![RegionIdent {
1248 datanode_id: 2,
1249 table_id: 1024,
1250 region_number: 1,
1251 engine: "mito2".to_string(),
1252 }]);
1253 assert_eq!(close_region_instruction, close_region);
1254
1255 let downgrade_region_instruction = r#"{"DowngradeRegions":{"region_id":4398046511105,"flush_timeout":{"secs":1,"nanos":0}}}"#;
1257 let downgrade_region_instruction: Instruction =
1258 serde_json::from_str(downgrade_region_instruction).unwrap();
1259 let downgrade_region = Instruction::DowngradeRegions(vec![DowngradeRegion {
1260 region_id: RegionId::new(1024, 1),
1261 flush_timeout: Some(Duration::from_millis(1000)),
1262 }]);
1263 assert_eq!(downgrade_region_instruction, downgrade_region);
1264
1265 let upgrade_region_instruction = r#"{"UpgradeRegion":{"region_id":4398046511105,"last_entry_id":null,"metadata_last_entry_id":null,"replay_timeout":"1s","location_id":null,"replay_entry_id":null,"metadata_replay_entry_id":null}}"#;
1267 let upgrade_region_instruction: Instruction =
1268 serde_json::from_str(upgrade_region_instruction).unwrap();
1269 let upgrade_region = Instruction::UpgradeRegions(vec![UpgradeRegion {
1270 region_id: RegionId::new(1024, 1),
1271 last_entry_id: None,
1272 metadata_last_entry_id: None,
1273 replay_timeout: Duration::from_millis(1000),
1274 location_id: None,
1275 replay_entry_id: None,
1276 metadata_replay_entry_id: None,
1277 }]);
1278 assert_eq!(upgrade_region_instruction, upgrade_region);
1279 }
1280
1281 #[test]
1282 fn test_deserialize_instruction_reply() {
1283 let close_region_instruction_reply =
1285 r#"{"result":true,"error":null,"type":"close_region"}"#;
1286 let close_region_instruction_reply: InstructionReply =
1287 serde_json::from_str(close_region_instruction_reply).unwrap();
1288 let close_region_reply = InstructionReply::CloseRegions(SimpleReply {
1289 result: true,
1290 error: None,
1291 });
1292 assert_eq!(close_region_instruction_reply, close_region_reply);
1293
1294 let open_region_instruction_reply = r#"{"result":true,"error":null,"type":"open_region"}"#;
1296 let open_region_instruction_reply: InstructionReply =
1297 serde_json::from_str(open_region_instruction_reply).unwrap();
1298 let open_region_reply = InstructionReply::OpenRegions(SimpleReply {
1299 result: true,
1300 error: None,
1301 });
1302 assert_eq!(open_region_instruction_reply, open_region_reply);
1303
1304 let downgrade_region_instruction_reply = r#"{"region_id":4398046511105,"last_entry_id":null,"metadata_last_entry_id":null,"exists":true,"error":null,"type":"downgrade_region"}"#;
1306 let downgrade_region_instruction_reply: InstructionReply =
1307 serde_json::from_str(downgrade_region_instruction_reply).unwrap();
1308 let downgrade_region_reply = InstructionReply::DowngradeRegions(
1309 DowngradeRegionsReply::single(DowngradeRegionReply {
1310 region_id: RegionId::new(1024, 1),
1311 last_entry_id: None,
1312 metadata_last_entry_id: None,
1313 exists: true,
1314 error: None,
1315 }),
1316 );
1317 assert_eq!(downgrade_region_instruction_reply, downgrade_region_reply);
1318
1319 let upgrade_region_instruction_reply = r#"{"region_id":4398046511105,"ready":true,"exists":true,"error":null,"type":"upgrade_region"}"#;
1321 let upgrade_region_instruction_reply: InstructionReply =
1322 serde_json::from_str(upgrade_region_instruction_reply).unwrap();
1323 let upgrade_region_reply =
1324 InstructionReply::UpgradeRegions(UpgradeRegionsReply::single(UpgradeRegionReply {
1325 region_id: RegionId::new(1024, 1),
1326 ready: true,
1327 exists: true,
1328 error: None,
1329 }));
1330 assert_eq!(upgrade_region_instruction_reply, upgrade_region_reply);
1331 }
1332
1333 #[test]
1334 fn test_enter_staging_partition_rule_compatibility() {
1335 let legacy = r#"{"region_id":4398046511105,"partition_expr":"{\"Expr\":{\"lhs\":{\"Column\":\"x\"},\"op\":\"GtEq\",\"rhs\":{\"Value\":{\"Int32\":0}}}}"}"#;
1336 let enter: EnterStagingRegion = serde_json::from_str(legacy).unwrap();
1337 assert_eq!(enter.region_id, RegionId::new(1024, 1));
1338 assert_eq!(
1339 enter.partition_directive,
1340 StagingPartitionDirective::UpdatePartitionExpr(
1341 "{\"Expr\":{\"lhs\":{\"Column\":\"x\"},\"op\":\"GtEq\",\"rhs\":{\"Value\":{\"Int32\":0}}}}"
1342 .to_string()
1343 )
1344 );
1345
1346 let serialized = serde_json::to_string(&enter).unwrap();
1347 assert!(serialized.contains("\"partition_directive\":\""));
1348 assert!(!serialized.contains("partition_expr"));
1349
1350 let reject = r#"{"region_id":4398046511105,"partition_expr":{"type":"reject_all_writes"}}"#;
1351 let enter: EnterStagingRegion = serde_json::from_str(reject).unwrap();
1352 assert_eq!(
1353 enter.partition_directive,
1354 StagingPartitionDirective::RejectAllWrites
1355 );
1356 }
1357
1358 #[derive(Debug, Clone, Serialize, Deserialize)]
1359 struct LegacyOpenRegion {
1360 region_ident: RegionIdent,
1361 region_storage_path: String,
1362 region_options: HashMap<String, String>,
1363 }
1364
1365 #[test]
1366 fn test_compatible_serialize_open_region() {
1367 let region_ident = RegionIdent {
1368 datanode_id: 2,
1369 table_id: 1024,
1370 region_number: 1,
1371 engine: "mito2".to_string(),
1372 };
1373 let region_storage_path = "test/foo".to_string();
1374 let region_options = HashMap::from([
1375 ("a".to_string(), "aa".to_string()),
1376 ("b".to_string(), "bb".to_string()),
1377 ]);
1378
1379 let legacy_open_region = LegacyOpenRegion {
1381 region_ident: region_ident.clone(),
1382 region_storage_path: region_storage_path.clone(),
1383 region_options: region_options.clone(),
1384 };
1385 let serialized = serde_json::to_string(&legacy_open_region).unwrap();
1386
1387 let deserialized = serde_json::from_str(&serialized).unwrap();
1389 let expected = OpenRegion {
1390 region_ident,
1391 region_storage_path,
1392 region_options,
1393 region_wal_options: HashMap::new(),
1394 skip_wal_replay: false,
1395 reason: None,
1396 requirements: RegionRequirements::empty(),
1397 };
1398 assert_eq!(expected, deserialized);
1399 }
1400
1401 #[test]
1402 fn test_serialize_open_region_with_reason_and_requirements() {
1403 let open_region = OpenRegion::new(
1404 RegionIdent {
1405 datanode_id: 2,
1406 table_id: 1024,
1407 region_number: 1,
1408 engine: "mito2".to_string(),
1409 },
1410 "test/foo",
1411 HashMap::new(),
1412 HashMap::new(),
1413 false,
1414 Some(OpenRegionReason::RegionMigration),
1415 RegionRequirements::object_storage(),
1416 );
1417
1418 let serialized = serde_json::to_string(&open_region).unwrap();
1419 assert!(serialized.contains(r#""reason":"RegionMigration""#));
1420 assert!(serialized.contains(r#""object_storage":true"#));
1421
1422 let deserialized: OpenRegion = serde_json::from_str(&serialized).unwrap();
1423 assert_eq!(Some(OpenRegionReason::RegionMigration), deserialized.reason);
1424 assert_eq!(
1425 RegionRequirements::object_storage(),
1426 deserialized.requirements
1427 );
1428 }
1429
1430 #[test]
1431 fn test_flush_regions_creation() {
1432 let region_id = RegionId::new(1024, 1);
1433
1434 let single_sync = FlushRegions::sync_single(region_id);
1436 assert_eq!(single_sync.region_ids, vec![region_id]);
1437 assert_eq!(single_sync.strategy, FlushStrategy::Sync);
1438 assert!(!single_sync.is_hint());
1439 assert!(single_sync.is_sync());
1440 assert_eq!(single_sync.error_strategy, FlushErrorStrategy::FailFast);
1441 assert_eq!(single_sync.reason, None);
1442 assert!(single_sync.is_single_region());
1443 assert_eq!(single_sync.single_region_id(), Some(region_id));
1444
1445 let region_ids = vec![RegionId::new(1024, 1), RegionId::new(1024, 2)];
1447 let batch_async = FlushRegions::async_batch(region_ids.clone());
1448 assert_eq!(batch_async.region_ids, region_ids);
1449 assert_eq!(batch_async.strategy, FlushStrategy::Async);
1450 assert!(batch_async.is_hint());
1451 assert!(!batch_async.is_sync());
1452 assert_eq!(batch_async.error_strategy, FlushErrorStrategy::TryAll);
1453 assert_eq!(batch_async.reason, None);
1454 assert!(!batch_async.is_single_region());
1455 assert_eq!(batch_async.single_region_id(), None);
1456
1457 let batch_sync = FlushRegions::sync_batch(region_ids.clone(), FlushErrorStrategy::FailFast);
1459 assert_eq!(batch_sync.region_ids, region_ids);
1460 assert_eq!(batch_sync.strategy, FlushStrategy::Sync);
1461 assert!(!batch_sync.is_hint());
1462 assert!(batch_sync.is_sync());
1463 assert_eq!(batch_sync.error_strategy, FlushErrorStrategy::FailFast);
1464 assert_eq!(batch_sync.reason, None);
1465
1466 let with_reason = batch_sync.with_reason(RegionFlushReason::RemoteWalPrune);
1467 assert_eq!(with_reason.reason, Some(RegionFlushReason::RemoteWalPrune));
1468 }
1469
1470 #[test]
1471 fn test_flush_regions_conversion() {
1472 let region_id = RegionId::new(1024, 1);
1473
1474 let from_region_id: FlushRegions = region_id.into();
1475 assert_eq!(from_region_id.region_ids, vec![region_id]);
1476 assert_eq!(from_region_id.strategy, FlushStrategy::Sync);
1477 assert!(!from_region_id.is_hint());
1478 assert!(from_region_id.is_sync());
1479
1480 let flush_regions = FlushRegions {
1482 region_ids: vec![region_id],
1483 strategy: FlushStrategy::Async,
1484 error_strategy: FlushErrorStrategy::TryAll,
1485 reason: None,
1486 };
1487 assert_eq!(flush_regions.region_ids, vec![region_id]);
1488 assert_eq!(flush_regions.strategy, FlushStrategy::Async);
1489 assert!(flush_regions.is_hint());
1490 assert!(!flush_regions.is_sync());
1491 }
1492
1493 #[test]
1494 fn test_flush_region_reply() {
1495 let region_id = RegionId::new(1024, 1);
1496
1497 let success_reply = FlushRegionReply::success_single(region_id);
1499 assert!(success_reply.overall_success);
1500 assert_eq!(success_reply.results.len(), 1);
1501 assert_eq!(success_reply.results[0].0, region_id);
1502 assert!(success_reply.results[0].1.is_ok());
1503
1504 let error_reply = FlushRegionReply::error_single(region_id, "test error".to_string());
1506 assert!(!error_reply.overall_success);
1507 assert_eq!(error_reply.results.len(), 1);
1508 assert_eq!(error_reply.results[0].0, region_id);
1509 assert!(error_reply.results[0].1.is_err());
1510
1511 let region_id2 = RegionId::new(1024, 2);
1513 let results = vec![
1514 (region_id, Ok(())),
1515 (region_id2, Err("flush failed".to_string())),
1516 ];
1517 let batch_reply = FlushRegionReply::from_results(results);
1518 assert!(!batch_reply.overall_success);
1519 assert_eq!(batch_reply.results.len(), 2);
1520
1521 let simple_reply = batch_reply.to_simple_reply();
1523 assert!(!simple_reply.result);
1524 assert!(simple_reply.error.is_some());
1525 assert!(simple_reply.error.unwrap().contains("flush failed"));
1526 }
1527
1528 #[test]
1529 fn test_serialize_flush_regions_instruction() {
1530 let region_id = RegionId::new(1024, 1);
1531 let flush_regions = FlushRegions::sync_single(region_id);
1532 let instruction = Instruction::FlushRegions(flush_regions.clone());
1533
1534 let serialized = serde_json::to_string(&instruction).unwrap();
1535 assert!(!serialized.contains("reason"));
1536 let deserialized: Instruction = serde_json::from_str(&serialized).unwrap();
1537
1538 match deserialized {
1539 Instruction::FlushRegions(fr) => {
1540 assert_eq!(fr.region_ids, vec![region_id]);
1541 assert_eq!(fr.strategy, FlushStrategy::Sync);
1542 assert_eq!(fr.error_strategy, FlushErrorStrategy::FailFast);
1543 assert_eq!(fr.reason, None);
1544 }
1545 _ => panic!("Expected FlushRegions instruction"),
1546 }
1547
1548 let legacy = r#"{"FlushRegions":{"region_ids":[4398046511105],"strategy":"Sync","error_strategy":"FailFast"}}"#;
1549 let deserialized: Instruction = serde_json::from_str(legacy).unwrap();
1550 match deserialized {
1551 Instruction::FlushRegions(fr) => {
1552 assert_eq!(fr.region_ids, vec![region_id]);
1553 assert_eq!(fr.strategy, FlushStrategy::Sync);
1554 assert_eq!(fr.error_strategy, FlushErrorStrategy::FailFast);
1555 assert_eq!(fr.reason, None);
1556 }
1557 _ => panic!("Expected FlushRegions instruction"),
1558 }
1559
1560 let flush_regions = FlushRegions::async_batch(vec![region_id])
1561 .with_reason(RegionFlushReason::RemoteWalPrune);
1562 let instruction = Instruction::FlushRegions(flush_regions);
1563 let serialized = serde_json::to_string(&instruction).unwrap();
1564 assert!(serialized.contains(r#""reason":"RemoteWalPrune""#));
1565 let deserialized: Instruction = serde_json::from_str(&serialized).unwrap();
1566 match deserialized {
1567 Instruction::FlushRegions(fr) => {
1568 assert_eq!(fr.reason, Some(RegionFlushReason::RemoteWalPrune));
1569 }
1570 _ => panic!("Expected FlushRegions instruction"),
1571 }
1572 }
1573
1574 #[test]
1575 fn test_serialize_flush_regions_batch_instruction() {
1576 let region_ids = vec![RegionId::new(1024, 1), RegionId::new(1024, 2)];
1577 let flush_regions =
1578 FlushRegions::sync_batch(region_ids.clone(), FlushErrorStrategy::TryAll);
1579 let instruction = Instruction::FlushRegions(flush_regions);
1580
1581 let serialized = serde_json::to_string(&instruction).unwrap();
1582 let deserialized: Instruction = serde_json::from_str(&serialized).unwrap();
1583
1584 match deserialized {
1585 Instruction::FlushRegions(fr) => {
1586 assert_eq!(fr.region_ids, region_ids);
1587 assert_eq!(fr.strategy, FlushStrategy::Sync);
1588 assert!(!fr.is_hint());
1589 assert!(fr.is_sync());
1590 assert_eq!(fr.error_strategy, FlushErrorStrategy::TryAll);
1591 assert_eq!(fr.reason, None);
1592 }
1593 _ => panic!("Expected FlushRegions instruction"),
1594 }
1595 }
1596
1597 #[test]
1598 fn test_serialize_get_file_refs_instruction_reply() {
1599 let mut manifest = FileRefsManifest::default();
1600 let r0 = RegionId::new(1024, 1);
1601 let r1 = RegionId::new(1024, 2);
1602 manifest.file_refs.insert(
1603 r0,
1604 HashSet::from([FileRef::new(r0, FileId::random(), None)]),
1605 );
1606 manifest.file_refs.insert(
1607 r1,
1608 HashSet::from([FileRef::new(r1, FileId::random(), None)]),
1609 );
1610 manifest.manifest_version.insert(r0, 10);
1611 manifest.manifest_version.insert(r1, 20);
1612
1613 let instruction_reply = InstructionReply::GetFileRefs(GetFileRefsReply {
1614 file_refs_manifest: manifest,
1615 success: true,
1616 error: None,
1617 });
1618
1619 let serialized = serde_json::to_string(&instruction_reply).unwrap();
1620 let deserialized = serde_json::from_str(&serialized).unwrap();
1621
1622 assert_eq!(instruction_reply, deserialized);
1623 }
1624}