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 }