1pub mod dialect;
7
8pub use dialect::Backend;
9
10use serde::{Deserialize, Serialize};
11use std::fmt;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum BranchResult {
16 Success,
18 Failure,
20 Ongoing,
22 Unknown,
27}
28
29impl BranchResult {
30 pub fn from_http(status: u16, body: &str) -> Self {
33 if body.contains("FAILURE") {
34 return Self::Failure;
35 }
36 if body.contains("ONGOING") {
37 return Self::Ongoing;
38 }
39 match status {
40 200..=299 => Self::Success,
41 409 => Self::Failure,
42 425 => Self::Ongoing,
43 _ => Self::Unknown,
44 }
45 }
46
47 pub fn from_grpc(code: i32) -> Self {
69 match code {
70 GRPC_OK => Self::Success,
71 GRPC_ABORTED => Self::Failure,
72 GRPC_FAILED_PRECONDITION => Self::Ongoing,
73 _ => Self::Unknown,
76 }
77 }
78}
79
80pub const GRPC_OK: i32 = 0;
82pub const GRPC_ABORTED: i32 = 10;
84pub const GRPC_FAILED_PRECONDITION: i32 = 9;
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "lowercase")]
89pub enum TransType {
90 Saga,
91 Tcc,
92 Msg,
93 Xa,
94 Workflow,
97}
98
99impl fmt::Display for TransType {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 let s = match self {
102 Self::Saga => "saga",
103 Self::Tcc => "tcc",
104 Self::Msg => "msg",
105 Self::Xa => "xa",
106 Self::Workflow => "workflow",
107 };
108 f.write_str(s)
109 }
110}
111
112impl TransType {
113 pub fn parse(s: &str) -> Option<Self> {
114 match s {
115 "saga" => Some(Self::Saga),
116 "tcc" => Some(Self::Tcc),
117 "msg" => Some(Self::Msg),
118 "xa" => Some(Self::Xa),
119 "workflow" => Some(Self::Workflow),
120 _ => None,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
126#[serde(rename_all = "lowercase")]
127pub enum GlobalStatus {
128 Prepared,
130 Submitted,
132 Aborting,
134 Succeed,
136 Failed,
138}
139
140impl GlobalStatus {
141 pub fn as_str(&self) -> &'static str {
142 match self {
143 Self::Prepared => "prepared",
144 Self::Submitted => "submitted",
145 Self::Aborting => "aborting",
146 Self::Succeed => "succeed",
147 Self::Failed => "failed",
148 }
149 }
150
151 pub fn parse(s: &str) -> Option<Self> {
152 match s {
153 "prepared" => Some(Self::Prepared),
154 "submitted" => Some(Self::Submitted),
155 "aborting" => Some(Self::Aborting),
156 "succeed" => Some(Self::Succeed),
157 "failed" => Some(Self::Failed),
158 _ => None,
159 }
160 }
161
162 pub fn is_final(&self) -> bool {
164 matches!(self, Self::Succeed | Self::Failed)
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
169#[serde(rename_all = "lowercase")]
170pub enum BranchStatus {
171 Prepared,
172 Succeed,
173 Failed,
174}
175
176impl BranchStatus {
177 pub fn as_str(&self) -> &'static str {
178 match self {
179 Self::Prepared => "prepared",
180 Self::Succeed => "succeed",
181 Self::Failed => "failed",
182 }
183 }
184 pub fn parse(s: &str) -> Option<Self> {
185 match s {
186 "prepared" => Some(Self::Prepared),
187 "succeed" => Some(Self::Succeed),
188 "failed" => Some(Self::Failed),
189 _ => None,
190 }
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
196#[serde(rename_all = "lowercase")]
197pub enum BranchOp {
198 Action,
199 Compensate,
200 Try,
201 Confirm,
202 Cancel,
203 Commit,
205 Rollback,
206}
207
208impl BranchOp {
209 pub fn as_str(&self) -> &'static str {
210 match self {
211 Self::Action => "action",
212 Self::Compensate => "compensate",
213 Self::Try => "try",
214 Self::Confirm => "confirm",
215 Self::Cancel => "cancel",
216 Self::Commit => "commit",
217 Self::Rollback => "rollback",
218 }
219 }
220
221 pub fn parse(s: &str) -> Option<Self> {
222 match s {
223 "action" => Some(Self::Action),
224 "compensate" => Some(Self::Compensate),
225 "try" => Some(Self::Try),
226 "confirm" => Some(Self::Confirm),
227 "cancel" => Some(Self::Cancel),
228 "commit" => Some(Self::Commit),
229 "rollback" => Some(Self::Rollback),
230 _ => None,
231 }
232 }
233
234 pub fn origin_op(&self) -> Option<BranchOp> {
236 match self {
237 Self::Cancel => Some(Self::Try),
238 Self::Compensate => Some(Self::Action),
239 Self::Rollback => Some(Self::Action),
240 _ => None,
241 }
242 }
243
244 pub fn is_compensating(&self) -> bool {
245 self.origin_op().is_some()
246 }
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct SagaStep {
252 pub action: String,
253 pub compensate: String,
254 #[serde(default)]
260 pub payload: String,
261}
262
263impl SagaStep {
264 pub fn new(action: &str, compensate: &str) -> Self {
266 Self {
267 action: action.to_string(),
268 compensate: compensate.to_string(),
269 payload: String::new(),
270 }
271 }
272
273 pub fn with_payload(action: &str, compensate: &str, payload: &str) -> Self {
275 Self {
276 action: action.to_string(),
277 compensate: compensate.to_string(),
278 payload: payload.to_string(),
279 }
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Eq)]
285pub enum Advance {
286 Call { index: usize, op: BranchOp },
288 Finish(GlobalStatus),
290 Wait,
292 RunWorkflow,
297}
298
299pub fn saga_advance(
303 status: GlobalStatus,
304 actions: &[BranchStatus],
305 compensates: &[BranchStatus],
306) -> Advance {
307 debug_assert_eq!(actions.len(), compensates.len());
308 match status {
309 GlobalStatus::Submitted => {
310 for (i, st) in actions.iter().enumerate() {
312 match st {
313 BranchStatus::Succeed => continue,
314 BranchStatus::Prepared => {
315 return Advance::Call {
316 index: i,
317 op: BranchOp::Action,
318 }
319 }
320 BranchStatus::Failed => return Advance::Finish(GlobalStatus::Aborting),
322 }
323 }
324 Advance::Finish(GlobalStatus::Succeed)
325 }
326 GlobalStatus::Aborting => {
327 for i in (0..compensates.len()).rev() {
330 if compensates[i] == BranchStatus::Prepared {
331 return Advance::Call {
332 index: i,
333 op: BranchOp::Compensate,
334 };
335 }
336 }
337 Advance::Finish(GlobalStatus::Failed)
338 }
339 GlobalStatus::Prepared => Advance::Wait,
340 s => Advance::Finish(s),
341 }
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq)]
349pub struct RetryPolicy {
350 pub initial: i64,
352 pub max: i64,
354}
355
356impl Default for RetryPolicy {
357 fn default() -> Self {
358 Self {
360 initial: 10,
361 max: 300,
362 }
363 }
364}
365
366impl RetryPolicy {
367 pub fn from_env() -> Self {
369 let d = Self::default();
370 let get = |k: &str, fallback: i64| {
371 std::env::var(k)
372 .ok()
373 .and_then(|v| v.parse::<i64>().ok())
374 .filter(|v| *v > 0)
375 .unwrap_or(fallback)
376 };
377 let initial = get("DTMRS_RETRY_INTERVAL", d.initial);
378 let max = get("DTMRS_RETRY_MAX_INTERVAL", d.max);
379 Self {
381 initial,
382 max: max.max(initial),
383 }
384 }
385}
386
387pub fn next_interval_with(cur: i64, p: RetryPolicy) -> i64 {
389 if cur <= 0 {
390 return p.initial;
391 }
392 (cur * 2).min(p.max)
393}
394
395pub fn next_interval(cur: i64) -> i64 {
397 next_interval_with(cur, RetryPolicy::default())
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403 use BranchStatus::{Failed, Prepared, Succeed};
404
405 #[test]
406 fn 超时不能当失败() {
407 assert_eq!(BranchResult::from_http(504, ""), BranchResult::Unknown);
409 assert_eq!(BranchResult::from_http(500, ""), BranchResult::Unknown);
410 assert_eq!(BranchResult::from_http(409, ""), BranchResult::Failure);
412 assert_eq!(
413 BranchResult::from_http(200, r#"{"dtm_result":"FAILURE"}"#),
414 BranchResult::Failure
415 );
416 assert_eq!(BranchResult::from_http(425, ""), BranchResult::Ongoing);
417 assert_eq!(BranchResult::from_http(200, "ok"), BranchResult::Success);
418 }
419
420 #[test]
421 fn grpc只有aborted才算失败() {
422 assert_eq!(BranchResult::from_grpc(0), BranchResult::Success);
423 assert_eq!(BranchResult::from_grpc(10), BranchResult::Failure);
425 assert_eq!(BranchResult::from_grpc(9), BranchResult::Ongoing);
426
427 for code in 0..=15 {
431 let want = match code {
432 0 => BranchResult::Success,
433 10 => BranchResult::Failure,
434 9 => BranchResult::Ongoing,
435 _ => BranchResult::Unknown,
436 };
437 assert_eq!(BranchResult::from_grpc(code), want, "gRPC 码 {code} 判错了");
438 }
439 assert_eq!(
441 BranchResult::from_grpc(1),
442 BranchResult::Unknown,
443 "CANCELLED 是我们自己放弃,不是对方拒绝"
444 );
445 assert_eq!(
446 BranchResult::from_grpc(4),
447 BranchResult::Unknown,
448 "DEADLINE_EXCEEDED 绝不能当失败"
449 );
450 assert_eq!(
451 BranchResult::from_grpc(14),
452 BranchResult::Unknown,
453 "UNAVAILABLE 绝不能当失败"
454 );
455 assert_eq!(BranchResult::from_grpc(99), BranchResult::Unknown);
457 assert_eq!(BranchResult::from_grpc(-1), BranchResult::Unknown);
458 }
459
460 #[test]
461 fn grpc与http的判定语义一致() {
462 for (http, grpc) in [(200u16, 0i32), (409, 10), (425, 9), (500, 13), (503, 14)] {
465 assert_eq!(
466 BranchResult::from_http(http, ""),
467 BranchResult::from_grpc(grpc),
468 "HTTP {http} 与 gRPC {grpc} 应当判定一致"
469 );
470 }
471 }
472
473 #[test]
474 fn 正向按序推进() {
475 let a = [Prepared, Prepared];
476 let c = [Prepared, Prepared];
477 assert_eq!(
478 saga_advance(GlobalStatus::Submitted, &a, &c),
479 Advance::Call {
480 index: 0,
481 op: BranchOp::Action
482 }
483 );
484 let a = [Succeed, Prepared];
485 assert_eq!(
486 saga_advance(GlobalStatus::Submitted, &a, &c),
487 Advance::Call {
488 index: 1,
489 op: BranchOp::Action
490 }
491 );
492 let a = [Succeed, Succeed];
493 assert_eq!(
494 saga_advance(GlobalStatus::Submitted, &a, &c),
495 Advance::Finish(GlobalStatus::Succeed)
496 );
497 }
498
499 #[test]
500 fn 补偿必须逆序() {
501 let a = [Succeed, Failed];
502 let c = [Prepared, Prepared];
503 assert_eq!(
505 saga_advance(GlobalStatus::Aborting, &a, &c),
506 Advance::Call {
507 index: 1,
508 op: BranchOp::Compensate
509 }
510 );
511 let c = [Prepared, Succeed];
512 assert_eq!(
513 saga_advance(GlobalStatus::Aborting, &a, &c),
514 Advance::Call {
515 index: 0,
516 op: BranchOp::Compensate
517 }
518 );
519 let c = [Succeed, Succeed];
520 assert_eq!(
521 saga_advance(GlobalStatus::Aborting, &a, &c),
522 Advance::Finish(GlobalStatus::Failed)
523 );
524 }
525
526 #[test]
527 fn 没跑过的分支也要补偿() {
528 let a = [Prepared, Prepared];
531 let c = [Prepared, Prepared];
532 assert_eq!(
533 saga_advance(GlobalStatus::Aborting, &a, &c),
534 Advance::Call {
535 index: 1,
536 op: BranchOp::Compensate
537 }
538 );
539 }
540
541 #[test]
542 fn 终态不再推进() {
543 for s in [GlobalStatus::Succeed, GlobalStatus::Failed] {
544 assert!(s.is_final());
545 assert_eq!(saga_advance(s, &[], &[]), Advance::Finish(s));
546 }
547 }
548
549 #[test]
550 fn 补偿操作能找到正向操作() {
551 assert_eq!(BranchOp::Compensate.origin_op(), Some(BranchOp::Action));
552 assert_eq!(BranchOp::Cancel.origin_op(), Some(BranchOp::Try));
553 assert_eq!(BranchOp::Action.origin_op(), None);
554 assert!(BranchOp::Compensate.is_compensating());
555 assert!(!BranchOp::Try.is_compensating());
556 }
557
558 #[test]
559 fn 退避有上限() {
560 assert_eq!(next_interval(0), 10);
561 assert_eq!(next_interval(10), 20);
562 assert_eq!(next_interval(200), 300);
563 assert_eq!(next_interval(300), 300);
564 }
565
566 #[test]
567 fn 退避策略可配且默认值不变() {
568 let d = RetryPolicy::default();
570 assert_eq!((d.initial, d.max), (10, 300));
571
572 let fast = RetryPolicy { initial: 1, max: 5 };
574 assert_eq!(next_interval_with(0, fast), 1);
575 assert_eq!(next_interval_with(1, fast), 2);
576 assert_eq!(next_interval_with(4, fast), 5, "封顶");
577 assert_eq!(next_interval_with(5, fast), 5);
578 }
579
580 #[test]
581 fn 上限小于初始值时不让退避反向增长() {
582 let p = RetryPolicy {
584 initial: 60,
585 max: 10,
586 };
587 let fixed = RetryPolicy {
588 initial: p.initial,
589 max: p.max.max(p.initial),
590 };
591 assert_eq!(next_interval_with(0, fixed), 60);
592 assert_eq!(next_interval_with(60, fixed), 60, "不该缩到 10");
593 }
594
595 #[test]
596 fn 步骤的payload默认为空且能带数据() {
597 let a = SagaStep::new("http://a", "http://c");
598 assert_eq!(a.payload, "");
599 let b = SagaStep::with_payload("http://a", "http://c", r#"{"amount":100}"#);
600 assert_eq!(b.payload, r#"{"amount":100}"#);
601
602 let old: SagaStep =
604 serde_json::from_str(r#"{"action":"http://a","compensate":"http://c"}"#)
605 .expect("老格式必须能解,否则升级后存量事务全推不动");
606 assert_eq!(old.payload, "");
607 }
608}
609
610pub fn tcc_advance(
624 status: GlobalStatus,
625 confirms: &[BranchStatus],
626 cancels: &[BranchStatus],
627) -> Advance {
628 debug_assert_eq!(confirms.len(), cancels.len());
629 match status {
630 GlobalStatus::Prepared => Advance::Wait,
632 GlobalStatus::Submitted => {
633 for (i, st) in confirms.iter().enumerate() {
634 match st {
635 BranchStatus::Succeed => continue,
636 BranchStatus::Prepared | BranchStatus::Failed => {
638 return Advance::Call {
639 index: i,
640 op: BranchOp::Confirm,
641 }
642 }
643 }
644 }
645 Advance::Finish(GlobalStatus::Succeed)
646 }
647 GlobalStatus::Aborting => {
648 for i in (0..cancels.len()).rev() {
650 if cancels[i] != BranchStatus::Succeed {
651 return Advance::Call {
652 index: i,
653 op: BranchOp::Cancel,
654 };
655 }
656 }
657 Advance::Finish(GlobalStatus::Failed)
658 }
659 s => Advance::Finish(s),
660 }
661}
662
663pub fn msg_advance(status: GlobalStatus, actions: &[BranchStatus]) -> Advance {
677 match status {
678 GlobalStatus::Prepared => Advance::Wait,
680 GlobalStatus::Submitted => {
681 for (i, st) in actions.iter().enumerate() {
682 if *st != BranchStatus::Succeed {
683 return Advance::Call {
684 index: i,
685 op: BranchOp::Action,
686 };
687 }
688 }
689 Advance::Finish(GlobalStatus::Succeed)
690 }
691 GlobalStatus::Aborting => Advance::Finish(GlobalStatus::Failed),
693 s => Advance::Finish(s),
694 }
695}
696
697#[cfg(test)]
698mod tcc_msg_tests {
699 use super::*;
700 use BranchStatus::{Failed, Prepared, Succeed};
701
702 #[test]
703 fn tcc的try阶段tc不插手() {
704 assert_eq!(
705 tcc_advance(GlobalStatus::Prepared, &[Prepared], &[Prepared]),
706 Advance::Wait
707 );
708 }
709
710 #[test]
711 fn tcc按序confirm() {
712 let c = [Prepared, Prepared];
713 let x = [Prepared, Prepared];
714 assert_eq!(
715 tcc_advance(GlobalStatus::Submitted, &c, &x),
716 Advance::Call {
717 index: 0,
718 op: BranchOp::Confirm
719 }
720 );
721 assert_eq!(
722 tcc_advance(GlobalStatus::Submitted, &[Succeed, Prepared], &x),
723 Advance::Call {
724 index: 1,
725 op: BranchOp::Confirm
726 }
727 );
728 assert_eq!(
729 tcc_advance(GlobalStatus::Submitted, &[Succeed, Succeed], &x),
730 Advance::Finish(GlobalStatus::Succeed)
731 );
732 }
733
734 #[test]
735 fn confirm失败绝不能触发cancel() {
736 let c = [Succeed, Failed];
739 let x = [Prepared, Prepared];
740 assert_eq!(
741 tcc_advance(GlobalStatus::Submitted, &c, &x),
742 Advance::Call {
743 index: 1,
744 op: BranchOp::Confirm
745 },
746 "confirm 失败要继续重试 confirm,不能转 cancel"
747 );
748 for a in [Prepared, Succeed, Failed] {
750 for b in [Prepared, Succeed, Failed] {
751 let r = tcc_advance(GlobalStatus::Submitted, &[a, b], &x);
752 assert_ne!(r, Advance::Finish(GlobalStatus::Aborting));
753 assert_ne!(r, Advance::Finish(GlobalStatus::Failed));
754 }
755 }
756 }
757
758 #[test]
759 fn tcc逆序cancel() {
760 let c = [Prepared, Prepared];
761 assert_eq!(
762 tcc_advance(GlobalStatus::Aborting, &c, &[Prepared, Prepared]),
763 Advance::Call {
764 index: 1,
765 op: BranchOp::Cancel
766 }
767 );
768 assert_eq!(
769 tcc_advance(GlobalStatus::Aborting, &c, &[Prepared, Succeed]),
770 Advance::Call {
771 index: 0,
772 op: BranchOp::Cancel
773 }
774 );
775 assert_eq!(
776 tcc_advance(GlobalStatus::Aborting, &c, &[Succeed, Succeed]),
777 Advance::Finish(GlobalStatus::Failed)
778 );
779 assert_eq!(
781 tcc_advance(GlobalStatus::Aborting, &c, &[Succeed, Failed]),
782 Advance::Call {
783 index: 1,
784 op: BranchOp::Cancel
785 }
786 );
787 }
788
789 #[test]
790 fn msg等回查而不是自己推() {
791 assert_eq!(
792 msg_advance(GlobalStatus::Prepared, &[Prepared]),
793 Advance::Wait
794 );
795 }
796
797 #[test]
798 fn msg只往前不补偿() {
799 assert_eq!(
800 msg_advance(GlobalStatus::Submitted, &[Prepared, Prepared]),
801 Advance::Call {
802 index: 0,
803 op: BranchOp::Action
804 }
805 );
806 assert_eq!(
808 msg_advance(GlobalStatus::Submitted, &[Failed]),
809 Advance::Call {
810 index: 0,
811 op: BranchOp::Action
812 }
813 );
814 assert_eq!(
815 msg_advance(GlobalStatus::Submitted, &[Succeed, Succeed]),
816 Advance::Finish(GlobalStatus::Succeed)
817 );
818 assert_eq!(
820 msg_advance(GlobalStatus::Aborting, &[Succeed]),
821 Advance::Finish(GlobalStatus::Failed)
822 );
823 }
824}
825
826pub fn xa_advance(
842 status: GlobalStatus,
843 commits: &[BranchStatus],
844 rollbacks: &[BranchStatus],
845) -> Advance {
846 debug_assert_eq!(commits.len(), rollbacks.len());
847 match status {
848 GlobalStatus::Prepared => Advance::Wait,
850 GlobalStatus::Submitted => {
851 for (i, st) in commits.iter().enumerate() {
852 if *st != BranchStatus::Succeed {
853 return Advance::Call {
854 index: i,
855 op: BranchOp::Commit,
856 };
857 }
858 }
859 Advance::Finish(GlobalStatus::Succeed)
860 }
861 GlobalStatus::Aborting => {
862 for i in (0..rollbacks.len()).rev() {
863 if rollbacks[i] != BranchStatus::Succeed {
864 return Advance::Call {
865 index: i,
866 op: BranchOp::Rollback,
867 };
868 }
869 }
870 Advance::Finish(GlobalStatus::Failed)
871 }
872 s => Advance::Finish(s),
873 }
874}
875
876pub fn workflow_advance(status: GlobalStatus, compensates: &[BranchStatus]) -> Advance {
905 match status {
906 GlobalStatus::Prepared => Advance::Wait,
908 GlobalStatus::Submitted => Advance::RunWorkflow,
909 GlobalStatus::Aborting => {
910 for i in (0..compensates.len()).rev() {
912 if compensates[i] != BranchStatus::Succeed {
913 return Advance::Call {
914 index: i,
915 op: BranchOp::Compensate,
916 };
917 }
918 }
919 Advance::Finish(GlobalStatus::Failed)
920 }
921 s => Advance::Finish(s),
922 }
923}
924
925#[cfg(test)]
926mod workflow_tests {
927 use super::*;
928 use BranchStatus::{Failed, Prepared, Succeed};
929
930 #[test]
931 fn submitted就是去跑函数() {
932 assert_eq!(
933 workflow_advance(GlobalStatus::Submitted, &[]),
934 Advance::RunWorkflow
935 );
936 assert_eq!(
939 workflow_advance(GlobalStatus::Submitted, &[Succeed, Prepared]),
940 Advance::RunWorkflow
941 );
942 }
943
944 #[test]
945 fn 回滚时逆序补偿() {
946 assert_eq!(
947 workflow_advance(GlobalStatus::Aborting, &[Prepared, Prepared]),
948 Advance::Call {
949 index: 1,
950 op: BranchOp::Compensate
951 },
952 "后执行的先回滚"
953 );
954 assert_eq!(
955 workflow_advance(GlobalStatus::Aborting, &[Prepared, Succeed]),
956 Advance::Call {
957 index: 0,
958 op: BranchOp::Compensate
959 }
960 );
961 assert_eq!(
962 workflow_advance(GlobalStatus::Aborting, &[Succeed, Succeed]),
963 Advance::Finish(GlobalStatus::Failed)
964 );
965 assert_eq!(
967 workflow_advance(GlobalStatus::Aborting, &[Succeed, Failed]),
968 Advance::Call {
969 index: 1,
970 op: BranchOp::Compensate
971 }
972 );
973 }
974
975 #[test]
976 fn 一个分支都没登记就回滚是直接失败() {
977 assert_eq!(
979 workflow_advance(GlobalStatus::Aborting, &[]),
980 Advance::Finish(GlobalStatus::Failed)
981 );
982 }
983
984 #[test]
985 fn 终态不再推进() {
986 for s in [GlobalStatus::Succeed, GlobalStatus::Failed] {
987 assert_eq!(workflow_advance(s, &[]), Advance::Finish(s));
988 }
989 }
990
991 #[test]
992 fn workflow是一种事务类型() {
993 assert_eq!(TransType::parse("workflow"), Some(TransType::Workflow));
994 assert_eq!(TransType::Workflow.to_string(), "workflow");
995 }
996}
997
998#[cfg(test)]
999mod xa_tests {
1000 use super::*;
1001 use BranchStatus::{Failed, Prepared, Succeed};
1002
1003 #[test]
1004 fn xa的prepare阶段tc不插手() {
1005 assert_eq!(
1007 xa_advance(GlobalStatus::Prepared, &[Prepared], &[Prepared]),
1008 Advance::Wait
1009 );
1010 }
1011
1012 #[test]
1013 fn xa按序commit() {
1014 let r = [Prepared, Prepared];
1015 assert_eq!(
1016 xa_advance(GlobalStatus::Submitted, &[Prepared, Prepared], &r),
1017 Advance::Call {
1018 index: 0,
1019 op: BranchOp::Commit
1020 }
1021 );
1022 assert_eq!(
1023 xa_advance(GlobalStatus::Submitted, &[Succeed, Prepared], &r),
1024 Advance::Call {
1025 index: 1,
1026 op: BranchOp::Commit
1027 }
1028 );
1029 assert_eq!(
1030 xa_advance(GlobalStatus::Submitted, &[Succeed, Succeed], &r),
1031 Advance::Finish(GlobalStatus::Succeed)
1032 );
1033 }
1034
1035 #[test]
1036 fn commit失败绝不能转rollback() {
1037 let r = [Prepared, Prepared];
1039 assert_eq!(
1040 xa_advance(GlobalStatus::Submitted, &[Succeed, Failed], &r),
1041 Advance::Call {
1042 index: 1,
1043 op: BranchOp::Commit
1044 },
1045 "commit 失败要继续重试 commit"
1046 );
1047 for a in [Prepared, Succeed, Failed] {
1049 for b in [Prepared, Succeed, Failed] {
1050 let got = xa_advance(GlobalStatus::Submitted, &[a, b], &r);
1051 assert_ne!(got, Advance::Finish(GlobalStatus::Aborting));
1052 assert_ne!(got, Advance::Finish(GlobalStatus::Failed));
1053 }
1054 }
1055 }
1056
1057 #[test]
1058 fn xa逆序rollback且失败也要重试() {
1059 let c = [Prepared, Prepared];
1060 assert_eq!(
1061 xa_advance(GlobalStatus::Aborting, &c, &[Prepared, Prepared]),
1062 Advance::Call {
1063 index: 1,
1064 op: BranchOp::Rollback
1065 }
1066 );
1067 assert_eq!(
1068 xa_advance(GlobalStatus::Aborting, &c, &[Succeed, Succeed]),
1069 Advance::Finish(GlobalStatus::Failed)
1070 );
1071 assert_eq!(
1073 xa_advance(GlobalStatus::Aborting, &c, &[Succeed, Failed]),
1074 Advance::Call {
1075 index: 1,
1076 op: BranchOp::Rollback
1077 }
1078 );
1079 }
1080
1081 #[test]
1082 fn commit操作没有反向映射() {
1083 assert_eq!(BranchOp::Commit.origin_op(), None);
1085 assert!(!BranchOp::Commit.is_compensating());
1086 assert_eq!(BranchOp::parse("commit"), Some(BranchOp::Commit));
1087 assert_eq!(BranchOp::Commit.as_str(), "commit");
1088 }
1089}