go.temporal.io/server@v1.23.0/common/persistence/operation_mode_validator.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package persistence 26 27 import ( 28 "fmt" 29 30 "go.temporal.io/api/serviceerror" 31 32 enumsspb "go.temporal.io/server/api/enums/v1" 33 ) 34 35 // NOTE: when modifying this file, plz make each case clear, 36 // do not combine cases together. 37 // The idea for this file is to test whether current record 38 // points to a zombie record. 39 40 // ValidateCreateWorkflowModeState validate workflow creation mode & workflow state 41 func ValidateCreateWorkflowModeState( 42 mode CreateWorkflowMode, 43 newWorkflowSnapshot WorkflowSnapshot, 44 ) error { 45 46 workflowState := newWorkflowSnapshot.ExecutionState.State 47 if err := checkWorkflowState(workflowState); err != nil { 48 return err 49 } 50 51 switch mode { 52 case CreateWorkflowModeBrandNew, 53 CreateWorkflowModeUpdateCurrent: 54 if workflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 55 return newInvalidCreateWorkflowMode( 56 mode, 57 workflowState, 58 ) 59 } 60 return nil 61 62 case CreateWorkflowModeBypassCurrent: 63 if workflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 64 workflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING { 65 return newInvalidCreateWorkflowMode( 66 mode, 67 workflowState, 68 ) 69 } 70 return nil 71 72 default: 73 return serviceerror.NewInternal(fmt.Sprintf("unknown mode: %v", mode)) 74 } 75 } 76 77 // ValidateUpdateWorkflowModeState validate workflow update mode & workflow state 78 func ValidateUpdateWorkflowModeState( 79 mode UpdateWorkflowMode, 80 currentWorkflowMutation WorkflowMutation, 81 newWorkflowSnapshot *WorkflowSnapshot, 82 ) error { 83 84 currentWorkflowState := currentWorkflowMutation.ExecutionState.State 85 if err := checkWorkflowState(currentWorkflowState); err != nil { 86 return err 87 } 88 var newWorkflowState *enumsspb.WorkflowExecutionState 89 if newWorkflowSnapshot != nil { 90 newWorkflowState = &newWorkflowSnapshot.ExecutionState.State 91 if err := checkWorkflowState(*newWorkflowState); err != nil { 92 return err 93 } 94 } 95 96 switch mode { 97 case UpdateWorkflowModeUpdateCurrent: 98 // update current record 99 // 1. current workflow only -> 100 // current workflow cannot be zombie 101 // 2. current workflow & new workflow -> 102 // current workflow cannot be created / running, 103 // new workflow cannot be zombie 104 105 // case 1 106 if newWorkflowState == nil { 107 if currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 108 return newInvalidUpdateWorkflowMode(mode, currentWorkflowState) 109 } 110 return nil 111 } 112 113 // case 2 114 if currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 115 currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 116 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 117 return newInvalidUpdateWorkflowWithNewMode(mode, currentWorkflowState, *newWorkflowState) 118 } 119 return nil 120 121 case UpdateWorkflowModeBypassCurrent: 122 // bypass current record 123 // 1. current workflow only -> 124 // current workflow cannot be created / running 125 // 2. current workflow & new workflow -> 126 // current workflow cannot be created / running, 127 // new workflow cannot be created / running 128 129 // case 1 130 if newWorkflowState == nil { 131 if currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 132 currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING { 133 return newInvalidUpdateWorkflowMode(mode, currentWorkflowState) 134 } 135 return nil 136 } 137 138 // case 2 139 if currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 140 currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 141 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 142 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING { 143 return newInvalidUpdateWorkflowWithNewMode( 144 mode, 145 currentWorkflowState, 146 *newWorkflowState, 147 ) 148 } 149 return nil 150 151 default: 152 return serviceerror.NewInternal(fmt.Sprintf("unknown mode: %v", mode)) 153 } 154 } 155 156 // ValidateConflictResolveWorkflowModeState validate workflow conflict resolve mode & workflow state 157 func ValidateConflictResolveWorkflowModeState( 158 mode ConflictResolveWorkflowMode, 159 resetWorkflowSnapshot WorkflowSnapshot, 160 newWorkflowSnapshot *WorkflowSnapshot, 161 currentWorkflowMutation *WorkflowMutation, 162 ) error { 163 164 resetWorkflowState := resetWorkflowSnapshot.ExecutionState.State 165 if err := checkWorkflowState(resetWorkflowState); err != nil { 166 return err 167 } 168 var newWorkflowState *enumsspb.WorkflowExecutionState 169 if newWorkflowSnapshot != nil { 170 newWorkflowState = &newWorkflowSnapshot.ExecutionState.State 171 if err := checkWorkflowState(*newWorkflowState); err != nil { 172 return err 173 } 174 } 175 var currentWorkflowState *enumsspb.WorkflowExecutionState 176 if currentWorkflowMutation != nil { 177 currentWorkflowState = ¤tWorkflowMutation.ExecutionState.State 178 if err := checkWorkflowState(*currentWorkflowState); err != nil { 179 return err 180 } 181 } 182 183 switch mode { 184 case ConflictResolveWorkflowModeUpdateCurrent: 185 // update current record 186 // 1. reset workflow only -> 187 // reset workflow cannot be zombie 188 // 2. reset workflow & new workflow -> 189 // reset workflow cannot be created / running / zombie, 190 // new workflow cannot be zombie 191 // 3. current workflow & reset workflow -> 192 // current workflow cannot be created / running, 193 // reset workflow cannot be zombie 194 // 4. current workflow & reset workflow & new workflow -> 195 // current workflow cannot be created / running, 196 // reset workflow cannot be created / running / zombie, 197 // new workflow cannot be zombie 198 199 // TODO remove case 1 & 2 support once 2DC is deprecated 200 // it is ok that currentWorkflowMutation is null, only for 2 DC case 201 // NDC should always require current workflow for CAS 202 // Note: current workflow mutation can be in zombie state, for the update 203 204 // case 1 & 2 205 if currentWorkflowState == nil { 206 // case 1 207 if newWorkflowState == nil { 208 if resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 209 return newInvalidConflictResolveWorkflowMode( 210 mode, 211 resetWorkflowState, 212 ) 213 } 214 return nil 215 } 216 217 // case 2 218 if resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 219 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 220 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE || 221 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 222 return newInvalidConflictResolveWorkflowWithNewMode( 223 mode, 224 resetWorkflowState, 225 *newWorkflowState, 226 ) 227 } 228 return nil 229 } 230 231 // case 3 & 4 232 // case 3 233 if newWorkflowState == nil { 234 if *currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 235 *currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 236 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 237 return newInvalidConflictResolveWorkflowWithCurrentMode( 238 mode, 239 resetWorkflowState, 240 *currentWorkflowState, 241 ) 242 } 243 return nil 244 } 245 246 // case 4 247 if *currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 248 *currentWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 249 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 250 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 251 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE || 252 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE { 253 return newInvalidConflictResolveWorkflowWithCurrentWithNewMode( 254 mode, 255 resetWorkflowState, 256 *newWorkflowState, 257 *currentWorkflowState, 258 ) 259 } 260 return nil 261 262 case ConflictResolveWorkflowModeBypassCurrent: 263 // bypass current record 264 // * current workflow cannot be set 265 // 1. reset workflow only -> 266 // reset workflow cannot be created / running 267 // 2. reset workflow & new workflow -> 268 // reset workflow cannot be created / running / zombie, 269 // new workflow cannot be created / running / completed 270 271 // precondition 272 if currentWorkflowMutation != nil { 273 return serviceerror.NewInternal(fmt.Sprintf("Invalid workflow conflict resolve mode %v, encountered current workflow", mode)) 274 } 275 276 // case 1 277 if newWorkflowState == nil { 278 if resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 279 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING { 280 return newInvalidConflictResolveWorkflowMode( 281 mode, 282 resetWorkflowState, 283 ) 284 } 285 return nil 286 } 287 288 // case 2 289 if resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 290 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 291 resetWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE || 292 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_CREATED || 293 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING || 294 *newWorkflowState == enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED { 295 return newInvalidConflictResolveWorkflowWithNewMode( 296 mode, 297 resetWorkflowState, 298 *newWorkflowState, 299 ) 300 } 301 return nil 302 303 default: 304 return serviceerror.NewInternal(fmt.Sprintf("unknown mode: %v", mode)) 305 } 306 } 307 308 func checkWorkflowState(state enumsspb.WorkflowExecutionState) error { 309 switch state { 310 case enumsspb.WORKFLOW_EXECUTION_STATE_CREATED, 311 enumsspb.WORKFLOW_EXECUTION_STATE_RUNNING, 312 enumsspb.WORKFLOW_EXECUTION_STATE_ZOMBIE, 313 enumsspb.WORKFLOW_EXECUTION_STATE_COMPLETED, 314 enumsspb.WORKFLOW_EXECUTION_STATE_CORRUPTED: 315 return nil 316 default: 317 return serviceerror.NewInternal(fmt.Sprintf("unknown workflow state: %v", state)) 318 } 319 } 320 321 func newInvalidCreateWorkflowMode( 322 mode CreateWorkflowMode, 323 workflowState enumsspb.WorkflowExecutionState, 324 ) error { 325 return serviceerror.NewInternal(fmt.Sprintf( 326 "Invalid workflow create mode %v, state: %v", 327 mode, 328 workflowState, 329 ), 330 ) 331 } 332 333 func newInvalidUpdateWorkflowMode( 334 mode UpdateWorkflowMode, 335 currentWorkflowState enumsspb.WorkflowExecutionState, 336 ) error { 337 return serviceerror.NewInternal(fmt.Sprintf( 338 "Invalid workflow update mode %v, state: %v", 339 mode, 340 currentWorkflowState, 341 ), 342 ) 343 } 344 345 func newInvalidUpdateWorkflowWithNewMode( 346 mode UpdateWorkflowMode, 347 currentWorkflowState enumsspb.WorkflowExecutionState, 348 newWorkflowState enumsspb.WorkflowExecutionState, 349 ) error { 350 return serviceerror.NewInternal(fmt.Sprintf( 351 "Invalid workflow update mode %v, current state: %v, new state: %v", 352 mode, 353 currentWorkflowState, 354 newWorkflowState, 355 ), 356 ) 357 } 358 359 func newInvalidConflictResolveWorkflowMode( 360 mode ConflictResolveWorkflowMode, 361 resetWorkflowState enumsspb.WorkflowExecutionState, 362 ) error { 363 return serviceerror.NewInternal(fmt.Sprintf( 364 "Invalid workflow conflict resolve mode %v, reset state: %v", 365 mode, 366 resetWorkflowState, 367 ), 368 ) 369 } 370 371 func newInvalidConflictResolveWorkflowWithNewMode( 372 mode ConflictResolveWorkflowMode, 373 resetWorkflowState enumsspb.WorkflowExecutionState, 374 newWorkflowState enumsspb.WorkflowExecutionState, 375 ) error { 376 return serviceerror.NewInternal(fmt.Sprintf( 377 "Invalid workflow conflict resolve mode %v, reset state: %v, new state: %v", 378 mode, 379 resetWorkflowState, 380 newWorkflowState, 381 ), 382 ) 383 } 384 385 func newInvalidConflictResolveWorkflowWithCurrentMode( 386 mode ConflictResolveWorkflowMode, 387 resetWorkflowState enumsspb.WorkflowExecutionState, 388 currentWorkflowState enumsspb.WorkflowExecutionState, 389 ) error { 390 return serviceerror.NewInternal(fmt.Sprintf( 391 "Invalid workflow conflict resolve mode %v, reset state: %v, current state: %v", 392 mode, 393 resetWorkflowState, 394 currentWorkflowState, 395 ), 396 ) 397 } 398 399 func newInvalidConflictResolveWorkflowWithCurrentWithNewMode( 400 mode ConflictResolveWorkflowMode, 401 resetWorkflowState enumsspb.WorkflowExecutionState, 402 newWorkflowState enumsspb.WorkflowExecutionState, 403 currentWorkflowState enumsspb.WorkflowExecutionState, 404 ) error { 405 return serviceerror.NewInternal(fmt.Sprintf( 406 "Invalid workflow conflict resolve mode %v, reset state: %v, new state: %v, current state: %v", 407 mode, 408 resetWorkflowState, 409 newWorkflowState, 410 currentWorkflowState, 411 ), 412 ) 413 }