github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/fsm/example_test.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package fsm_test
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"fmt"
    17  	"os"
    18  
    19  	. "github.com/cockroachdb/cockroach/pkg/util/fsm"
    20  )
    21  
    22  /// States.
    23  
    24  type stateNoTxn struct{}
    25  type stateOpen struct {
    26  	RetryIntent Bool
    27  }
    28  type stateAborted struct {
    29  	RetryIntent Bool
    30  }
    31  type stateRestartWait struct{}
    32  
    33  func (stateNoTxn) State()       {}
    34  func (stateOpen) State()        {}
    35  func (stateAborted) State()     {}
    36  func (stateRestartWait) State() {}
    37  
    38  /// Events.
    39  
    40  type eventNoTopLevelTransition struct {
    41  	RetryIntent Bool
    42  }
    43  type eventTxnStart struct{}
    44  type eventTxnFinish struct{}
    45  type eventTxnRestart struct{}
    46  type eventNonRetriableErr struct {
    47  	IsCommit Bool
    48  }
    49  type eventRetriableErr struct {
    50  	CanAutoRetry Bool
    51  	IsCommit     Bool
    52  }
    53  
    54  func (eventNoTopLevelTransition) Event() {}
    55  func (eventTxnStart) Event()             {}
    56  func (eventTxnFinish) Event()            {}
    57  func (eventTxnRestart) Event()           {}
    58  func (eventNonRetriableErr) Event()      {}
    59  func (eventRetriableErr) Event()         {}
    60  
    61  /// Transitions.
    62  
    63  var txnStateTransitions = Compile(Pattern{
    64  	stateNoTxn{}: {
    65  		eventNoTopLevelTransition{False}: {
    66  			Description: "my test transition",
    67  			Next:        stateNoTxn{},
    68  			Action:      writeAction("Identity"),
    69  		},
    70  		eventTxnStart{}: {
    71  			Next:   stateOpen{False},
    72  			Action: writeAction("Open..."),
    73  		},
    74  	},
    75  	stateOpen{Var("x")}: {
    76  		eventNoTopLevelTransition{False}: {
    77  			Next:   stateOpen{Var("x")},
    78  			Action: writeAction("Identity"),
    79  		},
    80  		eventTxnFinish{}: {
    81  			Next:   stateNoTxn{},
    82  			Action: writeAction("Finish..."),
    83  		},
    84  		eventNonRetriableErr{True}: {
    85  			Next:   stateNoTxn{},
    86  			Action: writeAction("Error"),
    87  		},
    88  		eventRetriableErr{True, Any}: {
    89  			Next:   stateOpen{Var("x")},
    90  			Action: writeAction("Transition 6"),
    91  		},
    92  		eventNonRetriableErr{False}: {
    93  			Next:   stateAborted{Var("x")},
    94  			Action: writeAction("Abort"),
    95  		},
    96  	},
    97  	stateOpen{False}: {
    98  		eventNoTopLevelTransition{True}: {
    99  			Next:   stateOpen{True},
   100  			Action: writeAction("Make Open"),
   101  		},
   102  		eventRetriableErr{False, Any}: {
   103  			Next:   stateAborted{False},
   104  			Action: writeAction("Abort"),
   105  		},
   106  	},
   107  	stateOpen{True}: {
   108  		eventRetriableErr{False, False}: {
   109  			Next:   stateRestartWait{},
   110  			Action: writeAction("Wait for restart"),
   111  		},
   112  		eventRetriableErr{False, True}: {
   113  			Next:   stateNoTxn{},
   114  			Action: writeAction("No more"),
   115  		},
   116  	},
   117  	stateAborted{Var("x")}: {
   118  		eventNoTopLevelTransition{False}: {
   119  			Next:   stateAborted{Var("x")},
   120  			Action: writeAction("Identity"),
   121  		},
   122  		eventTxnFinish{}: {
   123  			Next:   stateNoTxn{},
   124  			Action: writeAction("Abort finished"),
   125  		},
   126  		eventTxnStart{}: {
   127  			Next:   stateOpen{Var("x")},
   128  			Action: writeAction("Open from abort"),
   129  		},
   130  		eventNonRetriableErr{Any}: {
   131  			Next:   stateAborted{Var("x")},
   132  			Action: writeAction("Abort"),
   133  		},
   134  	},
   135  	stateRestartWait{}: {
   136  		eventNoTopLevelTransition{False}: {
   137  			Next:   stateRestartWait{},
   138  			Action: writeAction("Identity"),
   139  		},
   140  		eventTxnFinish{}: {
   141  			Next:   stateNoTxn{},
   142  			Action: writeAction("No more"),
   143  		},
   144  		eventTxnRestart{}: {
   145  			Next:   stateOpen{True},
   146  			Action: writeAction("Restarting"),
   147  		},
   148  		eventNonRetriableErr{Any}: {
   149  			Next:   stateAborted{True},
   150  			Action: writeAction("Abort"),
   151  		},
   152  	},
   153  })
   154  
   155  func writeAction(s string) func(Args) error {
   156  	return func(a Args) error {
   157  		a.Extended.(*executor).write(s)
   158  		return nil
   159  	}
   160  }
   161  
   162  type executor struct {
   163  	m   Machine
   164  	log bytes.Buffer
   165  }
   166  
   167  func (e *executor) write(s string) {
   168  	e.log.WriteString(s)
   169  	e.log.WriteString("\n")
   170  }
   171  
   172  func ExampleMachine() {
   173  	ctx := context.Background()
   174  
   175  	var e executor
   176  	e.m = MakeMachine(txnStateTransitions, stateNoTxn{}, &e)
   177  	_ = e.m.Apply(ctx, eventTxnStart{})
   178  	_ = e.m.Apply(ctx, eventNoTopLevelTransition{True})
   179  	_ = e.m.Apply(ctx, eventRetriableErr{False, False})
   180  	_ = e.m.Apply(ctx, eventTxnRestart{})
   181  	_ = e.m.Apply(ctx, eventTxnFinish{})
   182  	fmt.Print(e.log.String())
   183  
   184  	// Output:
   185  	// Open...
   186  	// Make Open
   187  	// Wait for restart
   188  	// Restarting
   189  	// Finish...
   190  }
   191  
   192  func ExampleTransitions_WriteReport() {
   193  	txnStateTransitions.WriteReport(os.Stdout)
   194  
   195  	// Output:
   196  	// Aborted{RetryIntent:false}
   197  	// 	handled events:
   198  	// 		NoTopLevelTransition{RetryIntent:false}
   199  	// 		NonRetriableErr{IsCommit:false}
   200  	// 		NonRetriableErr{IsCommit:true}
   201  	// 		TxnFinish{}
   202  	// 		TxnStart{}
   203  	// 	missing events:
   204  	// 		NoTopLevelTransition{RetryIntent:true}
   205  	// 		RetriableErr{CanAutoRetry:false, IsCommit:false}
   206  	// 		RetriableErr{CanAutoRetry:false, IsCommit:true}
   207  	// 		RetriableErr{CanAutoRetry:true, IsCommit:false}
   208  	// 		RetriableErr{CanAutoRetry:true, IsCommit:true}
   209  	// 		TxnRestart{}
   210  	// Aborted{RetryIntent:true}
   211  	// 	handled events:
   212  	// 		NoTopLevelTransition{RetryIntent:false}
   213  	// 		NonRetriableErr{IsCommit:false}
   214  	// 		NonRetriableErr{IsCommit:true}
   215  	// 		TxnFinish{}
   216  	// 		TxnStart{}
   217  	// 	missing events:
   218  	// 		NoTopLevelTransition{RetryIntent:true}
   219  	// 		RetriableErr{CanAutoRetry:false, IsCommit:false}
   220  	// 		RetriableErr{CanAutoRetry:false, IsCommit:true}
   221  	// 		RetriableErr{CanAutoRetry:true, IsCommit:false}
   222  	// 		RetriableErr{CanAutoRetry:true, IsCommit:true}
   223  	// 		TxnRestart{}
   224  	// NoTxn{}
   225  	// 	handled events:
   226  	// 		NoTopLevelTransition{RetryIntent:false}
   227  	// 		TxnStart{}
   228  	// 	missing events:
   229  	// 		NoTopLevelTransition{RetryIntent:true}
   230  	// 		NonRetriableErr{IsCommit:false}
   231  	// 		NonRetriableErr{IsCommit:true}
   232  	// 		RetriableErr{CanAutoRetry:false, IsCommit:false}
   233  	// 		RetriableErr{CanAutoRetry:false, IsCommit:true}
   234  	// 		RetriableErr{CanAutoRetry:true, IsCommit:false}
   235  	// 		RetriableErr{CanAutoRetry:true, IsCommit:true}
   236  	// 		TxnFinish{}
   237  	// 		TxnRestart{}
   238  	// Open{RetryIntent:false}
   239  	// 	handled events:
   240  	// 		NoTopLevelTransition{RetryIntent:false}
   241  	// 		NoTopLevelTransition{RetryIntent:true}
   242  	// 		NonRetriableErr{IsCommit:false}
   243  	// 		NonRetriableErr{IsCommit:true}
   244  	// 		RetriableErr{CanAutoRetry:false, IsCommit:false}
   245  	// 		RetriableErr{CanAutoRetry:false, IsCommit:true}
   246  	// 		RetriableErr{CanAutoRetry:true, IsCommit:false}
   247  	// 		RetriableErr{CanAutoRetry:true, IsCommit:true}
   248  	// 		TxnFinish{}
   249  	// 	missing events:
   250  	// 		TxnRestart{}
   251  	// 		TxnStart{}
   252  	// Open{RetryIntent:true}
   253  	// 	handled events:
   254  	// 		NoTopLevelTransition{RetryIntent:false}
   255  	// 		NonRetriableErr{IsCommit:false}
   256  	// 		NonRetriableErr{IsCommit:true}
   257  	// 		RetriableErr{CanAutoRetry:false, IsCommit:false}
   258  	// 		RetriableErr{CanAutoRetry:false, IsCommit:true}
   259  	// 		RetriableErr{CanAutoRetry:true, IsCommit:false}
   260  	// 		RetriableErr{CanAutoRetry:true, IsCommit:true}
   261  	// 		TxnFinish{}
   262  	// 	missing events:
   263  	// 		NoTopLevelTransition{RetryIntent:true}
   264  	// 		TxnRestart{}
   265  	// 		TxnStart{}
   266  	// RestartWait{}
   267  	// 	handled events:
   268  	// 		NoTopLevelTransition{RetryIntent:false}
   269  	// 		NonRetriableErr{IsCommit:false}
   270  	// 		NonRetriableErr{IsCommit:true}
   271  	// 		TxnFinish{}
   272  	// 		TxnRestart{}
   273  	// 	missing events:
   274  	// 		NoTopLevelTransition{RetryIntent:true}
   275  	// 		RetriableErr{CanAutoRetry:false, IsCommit:false}
   276  	// 		RetriableErr{CanAutoRetry:false, IsCommit:true}
   277  	// 		RetriableErr{CanAutoRetry:true, IsCommit:false}
   278  	// 		RetriableErr{CanAutoRetry:true, IsCommit:true}
   279  	// 		TxnStart{}
   280  }
   281  
   282  func ExampleTransitions_WriteDotGraph() {
   283  	txnStateTransitions.WriteDotGraph(os.Stdout, stateNoTxn{})
   284  
   285  	// Output:
   286  	// digraph finite_state_machine {
   287  	// 	rankdir=LR;
   288  	//
   289  	// 	node [shape = doublecircle]; "NoTxn{}";
   290  	// 	node [shape = point ]; qi
   291  	// 	qi -> "NoTxn{}";
   292  	//
   293  	// 	node [shape = circle];
   294  	// 	"Aborted{RetryIntent:false}" -> "Aborted{RetryIntent:false}" [label = "NoTopLevelTransition{RetryIntent:false}"]
   295  	// 	"Aborted{RetryIntent:false}" -> "Aborted{RetryIntent:false}" [label = "NonRetriableErr{IsCommit:false}"]
   296  	// 	"Aborted{RetryIntent:false}" -> "Aborted{RetryIntent:false}" [label = "NonRetriableErr{IsCommit:true}"]
   297  	// 	"Aborted{RetryIntent:false}" -> "NoTxn{}" [label = "TxnFinish{}"]
   298  	// 	"Aborted{RetryIntent:false}" -> "Open{RetryIntent:false}" [label = "TxnStart{}"]
   299  	// 	"Aborted{RetryIntent:true}" -> "Aborted{RetryIntent:true}" [label = "NoTopLevelTransition{RetryIntent:false}"]
   300  	// 	"Aborted{RetryIntent:true}" -> "Aborted{RetryIntent:true}" [label = "NonRetriableErr{IsCommit:false}"]
   301  	// 	"Aborted{RetryIntent:true}" -> "Aborted{RetryIntent:true}" [label = "NonRetriableErr{IsCommit:true}"]
   302  	// 	"Aborted{RetryIntent:true}" -> "NoTxn{}" [label = "TxnFinish{}"]
   303  	// 	"Aborted{RetryIntent:true}" -> "Open{RetryIntent:true}" [label = "TxnStart{}"]
   304  	// 	"NoTxn{}" -> "NoTxn{}" [label = <NoTopLevelTransition{RetryIntent:false}<BR/><I>my test transition</I>>]
   305  	// 	"NoTxn{}" -> "Open{RetryIntent:false}" [label = "TxnStart{}"]
   306  	// 	"Open{RetryIntent:false}" -> "Open{RetryIntent:false}" [label = "NoTopLevelTransition{RetryIntent:false}"]
   307  	// 	"Open{RetryIntent:false}" -> "Open{RetryIntent:true}" [label = "NoTopLevelTransition{RetryIntent:true}"]
   308  	// 	"Open{RetryIntent:false}" -> "Aborted{RetryIntent:false}" [label = "NonRetriableErr{IsCommit:false}"]
   309  	// 	"Open{RetryIntent:false}" -> "NoTxn{}" [label = "NonRetriableErr{IsCommit:true}"]
   310  	// 	"Open{RetryIntent:false}" -> "Aborted{RetryIntent:false}" [label = "RetriableErr{CanAutoRetry:false, IsCommit:false}"]
   311  	// 	"Open{RetryIntent:false}" -> "Aborted{RetryIntent:false}" [label = "RetriableErr{CanAutoRetry:false, IsCommit:true}"]
   312  	// 	"Open{RetryIntent:false}" -> "Open{RetryIntent:false}" [label = "RetriableErr{CanAutoRetry:true, IsCommit:false}"]
   313  	// 	"Open{RetryIntent:false}" -> "Open{RetryIntent:false}" [label = "RetriableErr{CanAutoRetry:true, IsCommit:true}"]
   314  	// 	"Open{RetryIntent:false}" -> "NoTxn{}" [label = "TxnFinish{}"]
   315  	// 	"Open{RetryIntent:true}" -> "Open{RetryIntent:true}" [label = "NoTopLevelTransition{RetryIntent:false}"]
   316  	// 	"Open{RetryIntent:true}" -> "Aborted{RetryIntent:true}" [label = "NonRetriableErr{IsCommit:false}"]
   317  	// 	"Open{RetryIntent:true}" -> "NoTxn{}" [label = "NonRetriableErr{IsCommit:true}"]
   318  	// 	"Open{RetryIntent:true}" -> "RestartWait{}" [label = "RetriableErr{CanAutoRetry:false, IsCommit:false}"]
   319  	// 	"Open{RetryIntent:true}" -> "NoTxn{}" [label = "RetriableErr{CanAutoRetry:false, IsCommit:true}"]
   320  	// 	"Open{RetryIntent:true}" -> "Open{RetryIntent:true}" [label = "RetriableErr{CanAutoRetry:true, IsCommit:false}"]
   321  	// 	"Open{RetryIntent:true}" -> "Open{RetryIntent:true}" [label = "RetriableErr{CanAutoRetry:true, IsCommit:true}"]
   322  	// 	"Open{RetryIntent:true}" -> "NoTxn{}" [label = "TxnFinish{}"]
   323  	// 	"RestartWait{}" -> "RestartWait{}" [label = "NoTopLevelTransition{RetryIntent:false}"]
   324  	// 	"RestartWait{}" -> "Aborted{RetryIntent:true}" [label = "NonRetriableErr{IsCommit:false}"]
   325  	// 	"RestartWait{}" -> "Aborted{RetryIntent:true}" [label = "NonRetriableErr{IsCommit:true}"]
   326  	// 	"RestartWait{}" -> "NoTxn{}" [label = "TxnFinish{}"]
   327  	// 	"RestartWait{}" -> "Open{RetryIntent:true}" [label = "TxnRestart{}"]
   328  	// }
   329  }