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 = &currentWorkflowMutation.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  }