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;
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={})",
183 self.region_ident, self.region_storage_path
184 )
185 }
186}
187
188#[serde_with::serde_as]
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
190pub struct OpenRegion {
191 pub region_ident: RegionIdent,
192 pub region_storage_path: String,
193 pub region_options: HashMap<String, String>,
194 #[serde(default)]
195 #[serde_as(as = "HashMap<serde_with::DisplayFromStr, _>")]
196 pub region_wal_options: HashMap<RegionNumber, String>,
197 #[serde(default)]
198 pub skip_wal_replay: bool,
199}
200
201impl OpenRegion {
202 pub fn new(
203 region_ident: RegionIdent,
204 path: &str,
205 region_options: HashMap<String, String>,
206 region_wal_options: HashMap<RegionNumber, String>,
207 skip_wal_replay: bool,
208 ) -> Self {
209 Self {
210 region_ident,
211 region_storage_path: path.to_string(),
212 region_options,
213 region_wal_options,
214 skip_wal_replay,
215 }
216 }
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
221pub struct DowngradeRegion {
222 pub region_id: RegionId,
224 #[serde(default)]
228 pub flush_timeout: Option<Duration>,
229}
230
231impl Display for DowngradeRegion {
232 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
233 write!(
234 f,
235 "DowngradeRegion(region_id={}, flush_timeout={:?})",
236 self.region_id, self.flush_timeout,
237 )
238 }
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
243pub struct UpgradeRegion {
244 pub region_id: RegionId,
246 pub last_entry_id: Option<u64>,
248 pub metadata_last_entry_id: Option<u64>,
250 #[serde(with = "humantime_serde")]
255 pub replay_timeout: Duration,
256 #[serde(default)]
258 pub location_id: Option<u64>,
259 #[serde(default, skip_serializing_if = "Option::is_none")]
260 pub replay_entry_id: Option<u64>,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
262 pub metadata_replay_entry_id: Option<u64>,
263}
264
265impl UpgradeRegion {
266 pub fn with_replay_entry_id(mut self, replay_entry_id: Option<u64>) -> Self {
268 self.replay_entry_id = replay_entry_id;
269 self
270 }
271
272 pub fn with_metadata_replay_entry_id(mut self, metadata_replay_entry_id: Option<u64>) -> Self {
274 self.metadata_replay_entry_id = metadata_replay_entry_id;
275 self
276 }
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
280pub enum CacheIdent {
282 FlowId(FlowId),
283 FlowNodeAddressChange(u64),
285 FlowName(FlowName),
286 TableId(TableId),
287 TableName(TableName),
288 SchemaName(SchemaName),
289 CreateFlow(CreateFlow),
290 DropFlow(DropFlow),
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
294pub struct CreateFlow {
295 pub flow_id: FlowId,
297 pub source_table_ids: Vec<TableId>,
298 pub partition_to_peer_mapping: Vec<(FlowPartitionId, Peer)>,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
303pub struct DropFlow {
304 pub flow_id: FlowId,
305 pub source_table_ids: Vec<TableId>,
306 pub flow_part2node_id: Vec<(FlowPartitionId, FlownodeId)>,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
312pub enum FlushStrategy {
313 #[default]
315 Sync,
316 Async,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
322pub enum FlushErrorStrategy {
323 #[default]
325 FailFast,
326 TryAll,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
333pub struct FlushRegions {
334 pub region_ids: Vec<RegionId>,
336 #[serde(default)]
338 pub strategy: FlushStrategy,
339 #[serde(default)]
341 pub error_strategy: FlushErrorStrategy,
342 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub reason: Option<RegionFlushReason>,
345}
346
347impl Display for FlushRegions {
348 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
349 write!(
350 f,
351 "FlushRegions(region_ids={:?}, strategy={:?}, error_strategy={:?}, reason={:?})",
352 self.region_ids, self.strategy, self.error_strategy, self.reason
353 )
354 }
355}
356
357impl FlushRegions {
358 pub fn sync_single(region_id: RegionId) -> Self {
360 Self {
361 region_ids: vec![region_id],
362 strategy: FlushStrategy::Sync,
363 error_strategy: FlushErrorStrategy::FailFast,
364 reason: None,
365 }
366 }
367
368 pub fn async_batch(region_ids: Vec<RegionId>) -> Self {
370 Self {
371 region_ids,
372 strategy: FlushStrategy::Async,
373 error_strategy: FlushErrorStrategy::TryAll,
374 reason: None,
375 }
376 }
377
378 pub fn sync_batch(region_ids: Vec<RegionId>, error_strategy: FlushErrorStrategy) -> Self {
380 Self {
381 region_ids,
382 strategy: FlushStrategy::Sync,
383 error_strategy,
384 reason: None,
385 }
386 }
387
388 pub fn with_reason(mut self, reason: RegionFlushReason) -> Self {
389 self.reason = Some(reason);
390 self
391 }
392
393 pub fn is_single_region(&self) -> bool {
395 self.region_ids.len() == 1
396 }
397
398 pub fn single_region_id(&self) -> Option<RegionId> {
400 if self.is_single_region() {
401 self.region_ids.first().copied()
402 } else {
403 None
404 }
405 }
406
407 pub fn is_hint(&self) -> bool {
409 matches!(self.strategy, FlushStrategy::Async)
410 }
411
412 pub fn is_sync(&self) -> bool {
414 matches!(self.strategy, FlushStrategy::Sync)
415 }
416}
417
418impl From<RegionId> for FlushRegions {
419 fn from(region_id: RegionId) -> Self {
420 Self::sync_single(region_id)
421 }
422}
423
424#[derive(Debug, Deserialize)]
425#[serde(untagged)]
426enum SingleOrMultiple<T> {
427 Single(T),
428 Multiple(Vec<T>),
429}
430
431fn single_or_multiple_from<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
432where
433 D: Deserializer<'de>,
434 T: Deserialize<'de>,
435{
436 let helper = SingleOrMultiple::<T>::deserialize(deserializer)?;
437 Ok(match helper {
438 SingleOrMultiple::Single(x) => vec![x],
439 SingleOrMultiple::Multiple(xs) => xs,
440 })
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
445pub struct GetFileRefs {
446 pub query_regions: Vec<RegionId>,
448 pub related_regions: HashMap<RegionId, HashSet<RegionId>>,
453}
454
455impl Display for GetFileRefs {
456 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
457 write!(f, "GetFileRefs(region_ids={:?})", self.query_regions)
458 }
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
463pub struct GcRegions {
464 pub regions: Vec<RegionId>,
466 pub file_refs_manifest: FileRefsManifest,
468 pub full_file_listing: bool,
470}
471
472impl Display for GcRegions {
473 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
474 write!(
475 f,
476 "GcRegion(regions={:?}, file_refs_count={}, full_file_listing={})",
477 self.regions,
478 self.file_refs_manifest.file_refs.len(),
479 self.full_file_listing
480 )
481 }
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
486pub struct GetFileRefsReply {
487 pub file_refs_manifest: FileRefsManifest,
489 pub success: bool,
491 pub error: Option<String>,
493}
494
495impl Display for GetFileRefsReply {
496 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
497 write!(
498 f,
499 "GetFileRefsReply(success={}, file_refs_count={}, error={:?})",
500 self.success,
501 self.file_refs_manifest.file_refs.len(),
502 self.error
503 )
504 }
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
509pub struct GcRegionsReply {
510 pub result: Result<GcReport, String>,
511}
512
513impl Display for GcRegionsReply {
514 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
515 write!(
516 f,
517 "GcReply(result={})",
518 match &self.result {
519 Ok(report) => format!(
520 "GcReport(deleted_files_count={}, need_retry_regions_count={})",
521 report.deleted_files.len(),
522 report.need_retry_regions.len()
523 ),
524 Err(err) => format!("Err({})", err),
525 }
526 )
527 }
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
531pub struct EnterStagingRegion {
532 pub region_id: RegionId,
533 #[serde(
534 alias = "partition_expr",
535 deserialize_with = "deserialize_enter_staging_partition_directive",
536 serialize_with = "serialize_enter_staging_partition_directive"
537 )]
538 pub partition_directive: StagingPartitionDirective,
539}
540
541impl Display for EnterStagingRegion {
542 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
543 write!(
544 f,
545 "EnterStagingRegion(region_id={}, partition_directive={})",
546 self.region_id, self.partition_directive
547 )
548 }
549}
550
551#[derive(Debug, Clone, PartialEq, Eq)]
552pub enum StagingPartitionDirective {
553 UpdatePartitionExpr(String),
554 RejectAllWrites,
555}
556
557impl StagingPartitionDirective {
558 pub fn as_partition_expr(&self) -> Option<&str> {
560 match self {
561 Self::UpdatePartitionExpr(expr) => Some(expr),
562 Self::RejectAllWrites => None,
563 }
564 }
565}
566
567impl Display for StagingPartitionDirective {
568 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
569 match self {
570 Self::UpdatePartitionExpr(expr) => write!(f, "UpdatePartitionExpr({})", expr),
571 Self::RejectAllWrites => write!(f, "RejectAllWrites"),
572 }
573 }
574}
575
576fn serialize_enter_staging_partition_directive<S>(
577 rule: &StagingPartitionDirective,
578 serializer: S,
579) -> std::result::Result<S::Ok, S::Error>
580where
581 S: Serializer,
582{
583 match rule {
584 StagingPartitionDirective::UpdatePartitionExpr(expr) => serializer.serialize_str(expr),
585 StagingPartitionDirective::RejectAllWrites => {
586 #[derive(Serialize)]
587 struct RejectAllWritesSer<'a> {
588 r#type: &'a str,
589 }
590
591 RejectAllWritesSer {
592 r#type: "reject_all_writes",
593 }
594 .serialize(serializer)
595 }
596 }
597}
598
599fn deserialize_enter_staging_partition_directive<'de, D>(
600 deserializer: D,
601) -> std::result::Result<StagingPartitionDirective, D::Error>
602where
603 D: Deserializer<'de>,
604{
605 #[derive(Deserialize)]
606 #[serde(untagged)]
607 enum Compat {
608 Legacy(String),
609 TypeTagged { r#type: String },
610 }
611
612 match Compat::deserialize(deserializer)? {
613 Compat::Legacy(expr) => Ok(StagingPartitionDirective::UpdatePartitionExpr(expr)),
614 Compat::TypeTagged { r#type } if r#type == "reject_all_writes" => {
615 Ok(StagingPartitionDirective::RejectAllWrites)
616 }
617 Compat::TypeTagged { r#type } => Err(serde::de::Error::custom(format!(
618 "Unknown enter staging partition directive type: {}",
619 r#type
620 ))),
621 }
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
626pub struct SyncRegion {
627 pub region_id: RegionId,
629 pub request: SyncRegionFromRequest,
631}
632
633impl Display for SyncRegion {
634 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
635 write!(
636 f,
637 "SyncRegion(region_id={}, request={:?})",
638 self.region_id, self.request
639 )
640 }
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
644pub struct RemapManifest {
645 pub region_id: RegionId,
646 pub input_regions: Vec<RegionId>,
648 pub region_mapping: HashMap<RegionId, Vec<RegionId>>,
650 pub new_partition_exprs: HashMap<RegionId, String>,
652}
653
654impl Display for RemapManifest {
655 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
656 write!(
657 f,
658 "RemapManifest(region_id={}, input_regions={:?}, region_mapping={:?}, new_partition_exprs={:?})",
659 self.region_id, self.input_regions, self.region_mapping, self.new_partition_exprs
660 )
661 }
662}
663
664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
665pub struct ApplyStagingManifest {
666 pub region_id: RegionId,
668 pub partition_expr: String,
670 pub central_region_id: RegionId,
672 pub manifest_path: String,
674}
675
676impl Display for ApplyStagingManifest {
677 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
678 write!(
679 f,
680 "ApplyStagingManifest(region_id={}, partition_expr={}, central_region_id={}, manifest_path={})",
681 self.region_id, self.partition_expr, self.central_region_id, self.manifest_path
682 )
683 }
684}
685
686#[derive(Debug, Clone, Serialize, Deserialize, Display, PartialEq)]
687pub enum Instruction {
688 #[serde(deserialize_with = "single_or_multiple_from", alias = "OpenRegion")]
690 OpenRegions(Vec<OpenRegion>),
691 #[serde(deserialize_with = "single_or_multiple_from", alias = "CloseRegion")]
693 CloseRegions(Vec<RegionIdent>),
694 #[serde(deserialize_with = "single_or_multiple_from", alias = "UpgradeRegion")]
696 UpgradeRegions(Vec<UpgradeRegion>),
697 #[serde(
698 deserialize_with = "single_or_multiple_from",
699 alias = "DowngradeRegion"
700 )]
701 DowngradeRegions(Vec<DowngradeRegion>),
703 InvalidateCaches(Vec<CacheIdent>),
705 FlushRegions(FlushRegions),
707 GetFileRefs(GetFileRefs),
709 GcRegions(GcRegions),
711 Suspend,
713 EnterStagingRegions(Vec<EnterStagingRegion>),
715 SyncRegions(Vec<SyncRegion>),
717 RemapManifest(RemapManifest),
719
720 ApplyStagingManifests(Vec<ApplyStagingManifest>),
722}
723
724impl Instruction {
725 pub fn into_open_regions(self) -> Option<Vec<OpenRegion>> {
727 match self {
728 Self::OpenRegions(open_regions) => Some(open_regions),
729 _ => None,
730 }
731 }
732
733 pub fn into_close_regions(self) -> Option<Vec<RegionIdent>> {
735 match self {
736 Self::CloseRegions(close_regions) => Some(close_regions),
737 _ => None,
738 }
739 }
740
741 pub fn into_flush_regions(self) -> Option<FlushRegions> {
743 match self {
744 Self::FlushRegions(flush_regions) => Some(flush_regions),
745 _ => None,
746 }
747 }
748
749 pub fn into_downgrade_regions(self) -> Option<Vec<DowngradeRegion>> {
751 match self {
752 Self::DowngradeRegions(downgrade_region) => Some(downgrade_region),
753 _ => None,
754 }
755 }
756
757 pub fn into_upgrade_regions(self) -> Option<Vec<UpgradeRegion>> {
759 match self {
760 Self::UpgradeRegions(upgrade_region) => Some(upgrade_region),
761 _ => None,
762 }
763 }
764
765 pub fn into_get_file_refs(self) -> Option<GetFileRefs> {
766 match self {
767 Self::GetFileRefs(get_file_refs) => Some(get_file_refs),
768 _ => None,
769 }
770 }
771
772 pub fn into_gc_regions(self) -> Option<GcRegions> {
773 match self {
774 Self::GcRegions(gc_regions) => Some(gc_regions),
775 _ => None,
776 }
777 }
778
779 pub fn into_enter_staging_regions(self) -> Option<Vec<EnterStagingRegion>> {
780 match self {
781 Self::EnterStagingRegions(enter_staging) => Some(enter_staging),
782 _ => None,
783 }
784 }
785
786 pub fn into_sync_regions(self) -> Option<Vec<SyncRegion>> {
787 match self {
788 Self::SyncRegions(sync_regions) => Some(sync_regions),
789 _ => None,
790 }
791 }
792}
793
794#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
796pub struct UpgradeRegionReply {
797 #[serde(default)]
800 pub region_id: RegionId,
801 pub ready: bool,
803 pub exists: bool,
805 pub error: Option<String>,
807}
808
809impl Display for UpgradeRegionReply {
810 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
811 write!(
812 f,
813 "(ready={}, exists={}, error={:?})",
814 self.ready, self.exists, self.error
815 )
816 }
817}
818
819#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
820pub struct DowngradeRegionsReply {
821 pub replies: Vec<DowngradeRegionReply>,
822}
823
824impl DowngradeRegionsReply {
825 pub fn new(replies: Vec<DowngradeRegionReply>) -> Self {
826 Self { replies }
827 }
828
829 pub fn single(reply: DowngradeRegionReply) -> Self {
830 Self::new(vec![reply])
831 }
832}
833
834#[derive(Deserialize)]
835#[serde(untagged)]
836enum DowngradeRegionsCompat {
837 Single(DowngradeRegionReply),
838 Multiple(DowngradeRegionsReply),
839}
840
841fn downgrade_regions_compat_from<'de, D>(deserializer: D) -> Result<DowngradeRegionsReply, D::Error>
842where
843 D: Deserializer<'de>,
844{
845 let helper = DowngradeRegionsCompat::deserialize(deserializer)?;
846 Ok(match helper {
847 DowngradeRegionsCompat::Single(x) => DowngradeRegionsReply::new(vec![x]),
848 DowngradeRegionsCompat::Multiple(reply) => reply,
849 })
850}
851
852#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
853pub struct UpgradeRegionsReply {
854 pub replies: Vec<UpgradeRegionReply>,
855}
856
857impl UpgradeRegionsReply {
858 pub fn new(replies: Vec<UpgradeRegionReply>) -> Self {
859 Self { replies }
860 }
861
862 pub fn single(reply: UpgradeRegionReply) -> Self {
863 Self::new(vec![reply])
864 }
865}
866
867#[derive(Deserialize)]
868#[serde(untagged)]
869enum UpgradeRegionsCompat {
870 Single(UpgradeRegionReply),
871 Multiple(UpgradeRegionsReply),
872}
873
874fn upgrade_regions_compat_from<'de, D>(deserializer: D) -> Result<UpgradeRegionsReply, D::Error>
875where
876 D: Deserializer<'de>,
877{
878 let helper = UpgradeRegionsCompat::deserialize(deserializer)?;
879 Ok(match helper {
880 UpgradeRegionsCompat::Single(x) => UpgradeRegionsReply::new(vec![x]),
881 UpgradeRegionsCompat::Multiple(reply) => reply,
882 })
883}
884
885#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
886pub struct EnterStagingRegionReply {
887 pub region_id: RegionId,
888 pub ready: bool,
890 pub exists: bool,
892 pub error: Option<String>,
894}
895
896#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
897pub struct EnterStagingRegionsReply {
898 pub replies: Vec<EnterStagingRegionReply>,
899}
900
901impl EnterStagingRegionsReply {
902 pub fn new(replies: Vec<EnterStagingRegionReply>) -> Self {
903 Self { replies }
904 }
905}
906
907#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
909pub struct SyncRegionReply {
910 pub region_id: RegionId,
912 pub ready: bool,
914 pub exists: bool,
916 pub error: Option<String>,
918}
919
920#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
922pub struct SyncRegionsReply {
923 pub replies: Vec<SyncRegionReply>,
924}
925
926impl SyncRegionsReply {
927 pub fn new(replies: Vec<SyncRegionReply>) -> Self {
928 Self { replies }
929 }
930}
931
932#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
933pub struct RemapManifestReply {
934 pub exists: bool,
936 pub manifest_paths: HashMap<RegionId, String>,
938 pub error: Option<String>,
940}
941
942impl Display for RemapManifestReply {
943 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
944 write!(
945 f,
946 "RemapManifestReply(manifest_paths={:?}, error={:?})",
947 self.manifest_paths, self.error
948 )
949 }
950}
951
952#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
953pub struct ApplyStagingManifestsReply {
954 pub replies: Vec<ApplyStagingManifestReply>,
955}
956
957impl ApplyStagingManifestsReply {
958 pub fn new(replies: Vec<ApplyStagingManifestReply>) -> Self {
959 Self { replies }
960 }
961}
962
963#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
964pub struct ApplyStagingManifestReply {
965 pub region_id: RegionId,
966 pub ready: bool,
968 pub exists: bool,
970 pub error: Option<String>,
972}
973
974#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
975#[serde(tag = "type", rename_all = "snake_case")]
976pub enum InstructionReply {
977 #[serde(alias = "open_region")]
978 OpenRegions(SimpleReply),
979 #[serde(alias = "close_region")]
980 CloseRegions(SimpleReply),
981 #[serde(
982 deserialize_with = "upgrade_regions_compat_from",
983 alias = "upgrade_region"
984 )]
985 UpgradeRegions(UpgradeRegionsReply),
986 #[serde(
987 alias = "downgrade_region",
988 deserialize_with = "downgrade_regions_compat_from"
989 )]
990 DowngradeRegions(DowngradeRegionsReply),
991 FlushRegions(FlushRegionReply),
992 GetFileRefs(GetFileRefsReply),
993 GcRegions(GcRegionsReply),
994 EnterStagingRegions(EnterStagingRegionsReply),
995 SyncRegions(SyncRegionsReply),
996 RemapManifest(RemapManifestReply),
997 ApplyStagingManifests(ApplyStagingManifestsReply),
998}
999
1000impl Display for InstructionReply {
1001 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1002 match self {
1003 Self::OpenRegions(reply) => write!(f, "InstructionReply::OpenRegions({})", reply),
1004 Self::CloseRegions(reply) => write!(f, "InstructionReply::CloseRegions({})", reply),
1005 Self::UpgradeRegions(reply) => {
1006 write!(f, "InstructionReply::UpgradeRegions({:?})", reply.replies)
1007 }
1008 Self::DowngradeRegions(reply) => {
1009 write!(f, "InstructionReply::DowngradeRegions({:?})", reply.replies)
1010 }
1011 Self::FlushRegions(reply) => write!(f, "InstructionReply::FlushRegions({})", reply),
1012 Self::GetFileRefs(reply) => write!(f, "InstructionReply::GetFileRefs({})", reply),
1013 Self::GcRegions(reply) => write!(f, "InstructionReply::GcRegion({})", reply),
1014 Self::EnterStagingRegions(reply) => {
1015 write!(
1016 f,
1017 "InstructionReply::EnterStagingRegions({:?})",
1018 reply.replies
1019 )
1020 }
1021 Self::SyncRegions(reply) => {
1022 write!(f, "InstructionReply::SyncRegions({:?})", reply.replies)
1023 }
1024 Self::RemapManifest(reply) => write!(f, "InstructionReply::RemapManifest({})", reply),
1025 Self::ApplyStagingManifests(reply) => write!(
1026 f,
1027 "InstructionReply::ApplyStagingManifests({:?})",
1028 reply.replies
1029 ),
1030 }
1031 }
1032}
1033
1034#[cfg(any(test, feature = "testing"))]
1035impl InstructionReply {
1036 pub fn expect_close_regions_reply(self) -> SimpleReply {
1037 match self {
1038 Self::CloseRegions(reply) => reply,
1039 _ => panic!("Expected CloseRegions reply"),
1040 }
1041 }
1042
1043 pub fn expect_open_regions_reply(self) -> SimpleReply {
1044 match self {
1045 Self::OpenRegions(reply) => reply,
1046 _ => panic!("Expected OpenRegions reply"),
1047 }
1048 }
1049
1050 pub fn expect_upgrade_regions_reply(self) -> Vec<UpgradeRegionReply> {
1051 match self {
1052 Self::UpgradeRegions(reply) => reply.replies,
1053 _ => panic!("Expected UpgradeRegion reply"),
1054 }
1055 }
1056
1057 pub fn expect_downgrade_regions_reply(self) -> Vec<DowngradeRegionReply> {
1058 match self {
1059 Self::DowngradeRegions(reply) => reply.replies,
1060 _ => panic!("Expected DowngradeRegion reply"),
1061 }
1062 }
1063
1064 pub fn expect_flush_regions_reply(self) -> FlushRegionReply {
1065 match self {
1066 Self::FlushRegions(reply) => reply,
1067 _ => panic!("Expected FlushRegions reply"),
1068 }
1069 }
1070
1071 pub fn expect_enter_staging_regions_reply(self) -> Vec<EnterStagingRegionReply> {
1072 match self {
1073 Self::EnterStagingRegions(reply) => reply.replies,
1074 _ => panic!("Expected EnterStagingRegion reply"),
1075 }
1076 }
1077
1078 pub fn expect_sync_regions_reply(self) -> Vec<SyncRegionReply> {
1079 match self {
1080 Self::SyncRegions(reply) => reply.replies,
1081 _ => panic!("Expected SyncRegion reply"),
1082 }
1083 }
1084
1085 pub fn expect_remap_manifest_reply(self) -> RemapManifestReply {
1086 match self {
1087 Self::RemapManifest(reply) => reply,
1088 _ => panic!("Expected RemapManifest reply"),
1089 }
1090 }
1091
1092 pub fn expect_apply_staging_manifests_reply(self) -> Vec<ApplyStagingManifestReply> {
1093 match self {
1094 Self::ApplyStagingManifests(reply) => reply.replies,
1095 _ => panic!("Expected ApplyStagingManifest reply"),
1096 }
1097 }
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102 use std::collections::HashSet;
1103
1104 use store_api::storage::{FileId, FileRef};
1105
1106 use super::*;
1107
1108 #[test]
1109 fn test_serialize_instruction() {
1110 let open_region = Instruction::OpenRegions(vec![OpenRegion::new(
1111 RegionIdent {
1112 datanode_id: 2,
1113 table_id: 1024,
1114 region_number: 1,
1115 engine: "mito2".to_string(),
1116 },
1117 "test/foo",
1118 HashMap::new(),
1119 HashMap::new(),
1120 false,
1121 )]);
1122
1123 let serialized = serde_json::to_string(&open_region).unwrap();
1124 assert_eq!(
1125 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}]}"#,
1126 serialized
1127 );
1128
1129 let close_region = Instruction::CloseRegions(vec![RegionIdent {
1130 datanode_id: 2,
1131 table_id: 1024,
1132 region_number: 1,
1133 engine: "mito2".to_string(),
1134 }]);
1135
1136 let serialized = serde_json::to_string(&close_region).unwrap();
1137 assert_eq!(
1138 r#"{"CloseRegions":[{"datanode_id":2,"table_id":1024,"region_number":1,"engine":"mito2"}]}"#,
1139 serialized
1140 );
1141
1142 let upgrade_region = Instruction::UpgradeRegions(vec![UpgradeRegion {
1143 region_id: RegionId::new(1024, 1),
1144 last_entry_id: None,
1145 metadata_last_entry_id: None,
1146 replay_timeout: Duration::from_millis(1000),
1147 location_id: None,
1148 replay_entry_id: None,
1149 metadata_replay_entry_id: None,
1150 }]);
1151
1152 let serialized = serde_json::to_string(&upgrade_region).unwrap();
1153 assert_eq!(
1154 r#"{"UpgradeRegions":[{"region_id":4398046511105,"last_entry_id":null,"metadata_last_entry_id":null,"replay_timeout":"1s","location_id":null}]}"#,
1155 serialized
1156 );
1157 }
1158
1159 #[test]
1160 fn test_serialize_instruction_reply() {
1161 let downgrade_region_reply = InstructionReply::DowngradeRegions(
1162 DowngradeRegionsReply::single(DowngradeRegionReply {
1163 region_id: RegionId::new(1024, 1),
1164 last_entry_id: None,
1165 metadata_last_entry_id: None,
1166 exists: true,
1167 error: None,
1168 }),
1169 );
1170
1171 let serialized = serde_json::to_string(&downgrade_region_reply).unwrap();
1172 assert_eq!(
1173 r#"{"type":"downgrade_regions","replies":[{"region_id":4398046511105,"last_entry_id":null,"metadata_last_entry_id":null,"exists":true,"error":null}]}"#,
1174 serialized
1175 );
1176
1177 let upgrade_region_reply =
1178 InstructionReply::UpgradeRegions(UpgradeRegionsReply::single(UpgradeRegionReply {
1179 region_id: RegionId::new(1024, 1),
1180 ready: true,
1181 exists: true,
1182 error: None,
1183 }));
1184 let serialized = serde_json::to_string(&upgrade_region_reply).unwrap();
1185 assert_eq!(
1186 r#"{"type":"upgrade_regions","replies":[{"region_id":4398046511105,"ready":true,"exists":true,"error":null}]}"#,
1187 serialized
1188 );
1189 }
1190
1191 #[test]
1192 fn test_deserialize_instruction() {
1193 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}}"#;
1195 let open_region_instruction: Instruction =
1196 serde_json::from_str(open_region_instruction).unwrap();
1197 let open_region = Instruction::OpenRegions(vec![OpenRegion::new(
1198 RegionIdent {
1199 datanode_id: 2,
1200 table_id: 1024,
1201 region_number: 1,
1202 engine: "mito2".to_string(),
1203 },
1204 "test/foo",
1205 HashMap::new(),
1206 HashMap::new(),
1207 false,
1208 )]);
1209 assert_eq!(open_region_instruction, open_region);
1210
1211 let close_region_instruction = r#"{"CloseRegion":{"datanode_id":2,"table_id":1024,"region_number":1,"engine":"mito2"}}"#;
1213 let close_region_instruction: Instruction =
1214 serde_json::from_str(close_region_instruction).unwrap();
1215 let close_region = Instruction::CloseRegions(vec![RegionIdent {
1216 datanode_id: 2,
1217 table_id: 1024,
1218 region_number: 1,
1219 engine: "mito2".to_string(),
1220 }]);
1221 assert_eq!(close_region_instruction, close_region);
1222
1223 let downgrade_region_instruction = r#"{"DowngradeRegions":{"region_id":4398046511105,"flush_timeout":{"secs":1,"nanos":0}}}"#;
1225 let downgrade_region_instruction: Instruction =
1226 serde_json::from_str(downgrade_region_instruction).unwrap();
1227 let downgrade_region = Instruction::DowngradeRegions(vec![DowngradeRegion {
1228 region_id: RegionId::new(1024, 1),
1229 flush_timeout: Some(Duration::from_millis(1000)),
1230 }]);
1231 assert_eq!(downgrade_region_instruction, downgrade_region);
1232
1233 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}}"#;
1235 let upgrade_region_instruction: Instruction =
1236 serde_json::from_str(upgrade_region_instruction).unwrap();
1237 let upgrade_region = Instruction::UpgradeRegions(vec![UpgradeRegion {
1238 region_id: RegionId::new(1024, 1),
1239 last_entry_id: None,
1240 metadata_last_entry_id: None,
1241 replay_timeout: Duration::from_millis(1000),
1242 location_id: None,
1243 replay_entry_id: None,
1244 metadata_replay_entry_id: None,
1245 }]);
1246 assert_eq!(upgrade_region_instruction, upgrade_region);
1247 }
1248
1249 #[test]
1250 fn test_deserialize_instruction_reply() {
1251 let close_region_instruction_reply =
1253 r#"{"result":true,"error":null,"type":"close_region"}"#;
1254 let close_region_instruction_reply: InstructionReply =
1255 serde_json::from_str(close_region_instruction_reply).unwrap();
1256 let close_region_reply = InstructionReply::CloseRegions(SimpleReply {
1257 result: true,
1258 error: None,
1259 });
1260 assert_eq!(close_region_instruction_reply, close_region_reply);
1261
1262 let open_region_instruction_reply = r#"{"result":true,"error":null,"type":"open_region"}"#;
1264 let open_region_instruction_reply: InstructionReply =
1265 serde_json::from_str(open_region_instruction_reply).unwrap();
1266 let open_region_reply = InstructionReply::OpenRegions(SimpleReply {
1267 result: true,
1268 error: None,
1269 });
1270 assert_eq!(open_region_instruction_reply, open_region_reply);
1271
1272 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"}"#;
1274 let downgrade_region_instruction_reply: InstructionReply =
1275 serde_json::from_str(downgrade_region_instruction_reply).unwrap();
1276 let downgrade_region_reply = InstructionReply::DowngradeRegions(
1277 DowngradeRegionsReply::single(DowngradeRegionReply {
1278 region_id: RegionId::new(1024, 1),
1279 last_entry_id: None,
1280 metadata_last_entry_id: None,
1281 exists: true,
1282 error: None,
1283 }),
1284 );
1285 assert_eq!(downgrade_region_instruction_reply, downgrade_region_reply);
1286
1287 let upgrade_region_instruction_reply = r#"{"region_id":4398046511105,"ready":true,"exists":true,"error":null,"type":"upgrade_region"}"#;
1289 let upgrade_region_instruction_reply: InstructionReply =
1290 serde_json::from_str(upgrade_region_instruction_reply).unwrap();
1291 let upgrade_region_reply =
1292 InstructionReply::UpgradeRegions(UpgradeRegionsReply::single(UpgradeRegionReply {
1293 region_id: RegionId::new(1024, 1),
1294 ready: true,
1295 exists: true,
1296 error: None,
1297 }));
1298 assert_eq!(upgrade_region_instruction_reply, upgrade_region_reply);
1299 }
1300
1301 #[test]
1302 fn test_enter_staging_partition_rule_compatibility() {
1303 let legacy = r#"{"region_id":4398046511105,"partition_expr":"{\"Expr\":{\"lhs\":{\"Column\":\"x\"},\"op\":\"GtEq\",\"rhs\":{\"Value\":{\"Int32\":0}}}}"}"#;
1304 let enter: EnterStagingRegion = serde_json::from_str(legacy).unwrap();
1305 assert_eq!(enter.region_id, RegionId::new(1024, 1));
1306 assert_eq!(
1307 enter.partition_directive,
1308 StagingPartitionDirective::UpdatePartitionExpr(
1309 "{\"Expr\":{\"lhs\":{\"Column\":\"x\"},\"op\":\"GtEq\",\"rhs\":{\"Value\":{\"Int32\":0}}}}"
1310 .to_string()
1311 )
1312 );
1313
1314 let serialized = serde_json::to_string(&enter).unwrap();
1315 assert!(serialized.contains("\"partition_directive\":\""));
1316 assert!(!serialized.contains("partition_expr"));
1317
1318 let reject = r#"{"region_id":4398046511105,"partition_expr":{"type":"reject_all_writes"}}"#;
1319 let enter: EnterStagingRegion = serde_json::from_str(reject).unwrap();
1320 assert_eq!(
1321 enter.partition_directive,
1322 StagingPartitionDirective::RejectAllWrites
1323 );
1324 }
1325
1326 #[derive(Debug, Clone, Serialize, Deserialize)]
1327 struct LegacyOpenRegion {
1328 region_ident: RegionIdent,
1329 region_storage_path: String,
1330 region_options: HashMap<String, String>,
1331 }
1332
1333 #[test]
1334 fn test_compatible_serialize_open_region() {
1335 let region_ident = RegionIdent {
1336 datanode_id: 2,
1337 table_id: 1024,
1338 region_number: 1,
1339 engine: "mito2".to_string(),
1340 };
1341 let region_storage_path = "test/foo".to_string();
1342 let region_options = HashMap::from([
1343 ("a".to_string(), "aa".to_string()),
1344 ("b".to_string(), "bb".to_string()),
1345 ]);
1346
1347 let legacy_open_region = LegacyOpenRegion {
1349 region_ident: region_ident.clone(),
1350 region_storage_path: region_storage_path.clone(),
1351 region_options: region_options.clone(),
1352 };
1353 let serialized = serde_json::to_string(&legacy_open_region).unwrap();
1354
1355 let deserialized = serde_json::from_str(&serialized).unwrap();
1357 let expected = OpenRegion {
1358 region_ident,
1359 region_storage_path,
1360 region_options,
1361 region_wal_options: HashMap::new(),
1362 skip_wal_replay: false,
1363 };
1364 assert_eq!(expected, deserialized);
1365 }
1366
1367 #[test]
1368 fn test_flush_regions_creation() {
1369 let region_id = RegionId::new(1024, 1);
1370
1371 let single_sync = FlushRegions::sync_single(region_id);
1373 assert_eq!(single_sync.region_ids, vec![region_id]);
1374 assert_eq!(single_sync.strategy, FlushStrategy::Sync);
1375 assert!(!single_sync.is_hint());
1376 assert!(single_sync.is_sync());
1377 assert_eq!(single_sync.error_strategy, FlushErrorStrategy::FailFast);
1378 assert_eq!(single_sync.reason, None);
1379 assert!(single_sync.is_single_region());
1380 assert_eq!(single_sync.single_region_id(), Some(region_id));
1381
1382 let region_ids = vec![RegionId::new(1024, 1), RegionId::new(1024, 2)];
1384 let batch_async = FlushRegions::async_batch(region_ids.clone());
1385 assert_eq!(batch_async.region_ids, region_ids);
1386 assert_eq!(batch_async.strategy, FlushStrategy::Async);
1387 assert!(batch_async.is_hint());
1388 assert!(!batch_async.is_sync());
1389 assert_eq!(batch_async.error_strategy, FlushErrorStrategy::TryAll);
1390 assert_eq!(batch_async.reason, None);
1391 assert!(!batch_async.is_single_region());
1392 assert_eq!(batch_async.single_region_id(), None);
1393
1394 let batch_sync = FlushRegions::sync_batch(region_ids.clone(), FlushErrorStrategy::FailFast);
1396 assert_eq!(batch_sync.region_ids, region_ids);
1397 assert_eq!(batch_sync.strategy, FlushStrategy::Sync);
1398 assert!(!batch_sync.is_hint());
1399 assert!(batch_sync.is_sync());
1400 assert_eq!(batch_sync.error_strategy, FlushErrorStrategy::FailFast);
1401 assert_eq!(batch_sync.reason, None);
1402
1403 let with_reason = batch_sync.with_reason(RegionFlushReason::RemoteWalPrune);
1404 assert_eq!(with_reason.reason, Some(RegionFlushReason::RemoteWalPrune));
1405 }
1406
1407 #[test]
1408 fn test_flush_regions_conversion() {
1409 let region_id = RegionId::new(1024, 1);
1410
1411 let from_region_id: FlushRegions = region_id.into();
1412 assert_eq!(from_region_id.region_ids, vec![region_id]);
1413 assert_eq!(from_region_id.strategy, FlushStrategy::Sync);
1414 assert!(!from_region_id.is_hint());
1415 assert!(from_region_id.is_sync());
1416
1417 let flush_regions = FlushRegions {
1419 region_ids: vec![region_id],
1420 strategy: FlushStrategy::Async,
1421 error_strategy: FlushErrorStrategy::TryAll,
1422 reason: None,
1423 };
1424 assert_eq!(flush_regions.region_ids, vec![region_id]);
1425 assert_eq!(flush_regions.strategy, FlushStrategy::Async);
1426 assert!(flush_regions.is_hint());
1427 assert!(!flush_regions.is_sync());
1428 }
1429
1430 #[test]
1431 fn test_flush_region_reply() {
1432 let region_id = RegionId::new(1024, 1);
1433
1434 let success_reply = FlushRegionReply::success_single(region_id);
1436 assert!(success_reply.overall_success);
1437 assert_eq!(success_reply.results.len(), 1);
1438 assert_eq!(success_reply.results[0].0, region_id);
1439 assert!(success_reply.results[0].1.is_ok());
1440
1441 let error_reply = FlushRegionReply::error_single(region_id, "test error".to_string());
1443 assert!(!error_reply.overall_success);
1444 assert_eq!(error_reply.results.len(), 1);
1445 assert_eq!(error_reply.results[0].0, region_id);
1446 assert!(error_reply.results[0].1.is_err());
1447
1448 let region_id2 = RegionId::new(1024, 2);
1450 let results = vec![
1451 (region_id, Ok(())),
1452 (region_id2, Err("flush failed".to_string())),
1453 ];
1454 let batch_reply = FlushRegionReply::from_results(results);
1455 assert!(!batch_reply.overall_success);
1456 assert_eq!(batch_reply.results.len(), 2);
1457
1458 let simple_reply = batch_reply.to_simple_reply();
1460 assert!(!simple_reply.result);
1461 assert!(simple_reply.error.is_some());
1462 assert!(simple_reply.error.unwrap().contains("flush failed"));
1463 }
1464
1465 #[test]
1466 fn test_serialize_flush_regions_instruction() {
1467 let region_id = RegionId::new(1024, 1);
1468 let flush_regions = FlushRegions::sync_single(region_id);
1469 let instruction = Instruction::FlushRegions(flush_regions.clone());
1470
1471 let serialized = serde_json::to_string(&instruction).unwrap();
1472 assert!(!serialized.contains("reason"));
1473 let deserialized: Instruction = serde_json::from_str(&serialized).unwrap();
1474
1475 match deserialized {
1476 Instruction::FlushRegions(fr) => {
1477 assert_eq!(fr.region_ids, vec![region_id]);
1478 assert_eq!(fr.strategy, FlushStrategy::Sync);
1479 assert_eq!(fr.error_strategy, FlushErrorStrategy::FailFast);
1480 assert_eq!(fr.reason, None);
1481 }
1482 _ => panic!("Expected FlushRegions instruction"),
1483 }
1484
1485 let legacy = r#"{"FlushRegions":{"region_ids":[4398046511105],"strategy":"Sync","error_strategy":"FailFast"}}"#;
1486 let deserialized: Instruction = serde_json::from_str(legacy).unwrap();
1487 match deserialized {
1488 Instruction::FlushRegions(fr) => {
1489 assert_eq!(fr.region_ids, vec![region_id]);
1490 assert_eq!(fr.strategy, FlushStrategy::Sync);
1491 assert_eq!(fr.error_strategy, FlushErrorStrategy::FailFast);
1492 assert_eq!(fr.reason, None);
1493 }
1494 _ => panic!("Expected FlushRegions instruction"),
1495 }
1496
1497 let flush_regions = FlushRegions::async_batch(vec![region_id])
1498 .with_reason(RegionFlushReason::RemoteWalPrune);
1499 let instruction = Instruction::FlushRegions(flush_regions);
1500 let serialized = serde_json::to_string(&instruction).unwrap();
1501 assert!(serialized.contains(r#""reason":"RemoteWalPrune""#));
1502 let deserialized: Instruction = serde_json::from_str(&serialized).unwrap();
1503 match deserialized {
1504 Instruction::FlushRegions(fr) => {
1505 assert_eq!(fr.reason, Some(RegionFlushReason::RemoteWalPrune));
1506 }
1507 _ => panic!("Expected FlushRegions instruction"),
1508 }
1509 }
1510
1511 #[test]
1512 fn test_serialize_flush_regions_batch_instruction() {
1513 let region_ids = vec![RegionId::new(1024, 1), RegionId::new(1024, 2)];
1514 let flush_regions =
1515 FlushRegions::sync_batch(region_ids.clone(), FlushErrorStrategy::TryAll);
1516 let instruction = Instruction::FlushRegions(flush_regions);
1517
1518 let serialized = serde_json::to_string(&instruction).unwrap();
1519 let deserialized: Instruction = serde_json::from_str(&serialized).unwrap();
1520
1521 match deserialized {
1522 Instruction::FlushRegions(fr) => {
1523 assert_eq!(fr.region_ids, region_ids);
1524 assert_eq!(fr.strategy, FlushStrategy::Sync);
1525 assert!(!fr.is_hint());
1526 assert!(fr.is_sync());
1527 assert_eq!(fr.error_strategy, FlushErrorStrategy::TryAll);
1528 assert_eq!(fr.reason, None);
1529 }
1530 _ => panic!("Expected FlushRegions instruction"),
1531 }
1532 }
1533
1534 #[test]
1535 fn test_serialize_get_file_refs_instruction_reply() {
1536 let mut manifest = FileRefsManifest::default();
1537 let r0 = RegionId::new(1024, 1);
1538 let r1 = RegionId::new(1024, 2);
1539 manifest.file_refs.insert(
1540 r0,
1541 HashSet::from([FileRef::new(r0, FileId::random(), None)]),
1542 );
1543 manifest.file_refs.insert(
1544 r1,
1545 HashSet::from([FileRef::new(r1, FileId::random(), None)]),
1546 );
1547 manifest.manifest_version.insert(r0, 10);
1548 manifest.manifest_version.insert(r1, 20);
1549
1550 let instruction_reply = InstructionReply::GetFileRefs(GetFileRefsReply {
1551 file_refs_manifest: manifest,
1552 success: true,
1553 error: None,
1554 });
1555
1556 let serialized = serde_json::to_string(&instruction_reply).unwrap();
1557 let deserialized = serde_json::from_str(&serialized).unwrap();
1558
1559 assert_eq!(instruction_reply, deserialized);
1560 }
1561}