go.temporal.io/server@v1.23.0/common/persistence/cassandra/errors_test.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 cassandra
    26  
    27  import (
    28  	"errors"
    29  	"math/rand"
    30  	"testing"
    31  
    32  	"github.com/gocql/gocql"
    33  	"github.com/google/uuid"
    34  	"github.com/stretchr/testify/require"
    35  	"github.com/stretchr/testify/suite"
    36  
    37  	persistencespb "go.temporal.io/server/api/persistence/v1"
    38  	p "go.temporal.io/server/common/persistence"
    39  	"go.temporal.io/server/common/persistence/serialization"
    40  )
    41  
    42  type (
    43  	cassandraErrorsSuite struct {
    44  		suite.Suite
    45  		*require.Assertions
    46  	}
    47  )
    48  
    49  func TestCassandraErrorsSuite(t *testing.T) {
    50  	s := new(cassandraErrorsSuite)
    51  	suite.Run(t, s)
    52  }
    53  
    54  func (s *cassandraErrorsSuite) SetupSuite() {
    55  }
    56  
    57  func (s *cassandraErrorsSuite) TearDownSuite() {
    58  
    59  }
    60  
    61  func (s *cassandraErrorsSuite) SetupTest() {
    62  	s.Assertions = require.New(s.T())
    63  }
    64  
    65  func (s *cassandraErrorsSuite) TearDownTest() {
    66  
    67  }
    68  
    69  func (s *cassandraErrorsSuite) TestSortErrors_Sorted() {
    70  	shardOwnershipLostErr := &p.ShardOwnershipLostError{}
    71  	currentWorkflowErr := &p.CurrentWorkflowConditionFailedError{}
    72  	workflowErr := &p.WorkflowConditionFailedError{}
    73  	genericErr := &p.ConditionFailedError{}
    74  	randomErr := errors.New("random error")
    75  
    76  	expectedErrors := []error{
    77  		shardOwnershipLostErr,
    78  		currentWorkflowErr,
    79  		workflowErr,
    80  		genericErr,
    81  		randomErr,
    82  	}
    83  
    84  	errorsCaseSorted := []error{
    85  		shardOwnershipLostErr,
    86  		currentWorkflowErr,
    87  		workflowErr,
    88  		genericErr,
    89  		randomErr,
    90  	}
    91  	s.Equal(expectedErrors, sortErrors(errorsCaseSorted))
    92  }
    93  
    94  func (s *cassandraErrorsSuite) TestSortErrors_ReverseSorted() {
    95  	shardOwnershipLostErr := &p.ShardOwnershipLostError{}
    96  	currentWorkflowErr := &p.CurrentWorkflowConditionFailedError{}
    97  	workflowErr := &p.WorkflowConditionFailedError{}
    98  	genericErr := &p.ConditionFailedError{}
    99  	randomErr := errors.New("random error")
   100  
   101  	expectedErrors := []error{
   102  		shardOwnershipLostErr,
   103  		currentWorkflowErr,
   104  		workflowErr,
   105  		genericErr,
   106  		randomErr,
   107  	}
   108  
   109  	errorsCaseReverseSorted := []error{
   110  		randomErr,
   111  		genericErr,
   112  		workflowErr,
   113  		currentWorkflowErr,
   114  		shardOwnershipLostErr,
   115  	}
   116  	s.Equal(expectedErrors, sortErrors(errorsCaseReverseSorted))
   117  }
   118  
   119  func (s *cassandraErrorsSuite) TestSortErrors_Random() {
   120  	shardOwnershipLostErr := &p.ShardOwnershipLostError{}
   121  	currentWorkflowErr := &p.CurrentWorkflowConditionFailedError{}
   122  	workflowErr := &p.WorkflowConditionFailedError{}
   123  	genericErr := &p.ConditionFailedError{}
   124  	randomErr := errors.New("random error")
   125  
   126  	expectedErrors := []error{
   127  		shardOwnershipLostErr,
   128  		currentWorkflowErr,
   129  		workflowErr,
   130  		genericErr,
   131  		randomErr,
   132  	}
   133  
   134  	errorsCaseShuffled := []error{
   135  		randomErr,
   136  		genericErr,
   137  		workflowErr,
   138  		currentWorkflowErr,
   139  		shardOwnershipLostErr,
   140  	}
   141  	rand.Shuffle(len(errorsCaseShuffled), func(i int, j int) {
   142  		errorsCaseShuffled[i], errorsCaseShuffled[j] = errorsCaseShuffled[j], errorsCaseShuffled[i]
   143  	})
   144  	s.Equal(expectedErrors, sortErrors(errorsCaseShuffled))
   145  }
   146  
   147  func (s *cassandraErrorsSuite) TestSortErrors_One() {
   148  	shardOwnershipLostErr := &p.ShardOwnershipLostError{}
   149  	currentWorkflowErr := &p.CurrentWorkflowConditionFailedError{}
   150  	workflowErr := &p.WorkflowConditionFailedError{}
   151  	genericErr := &p.ConditionFailedError{}
   152  	randomErr := errors.New("random error")
   153  
   154  	s.Equal([]error{shardOwnershipLostErr}, sortErrors([]error{shardOwnershipLostErr}))
   155  	s.Equal([]error{currentWorkflowErr}, sortErrors([]error{currentWorkflowErr}))
   156  	s.Equal([]error{workflowErr}, sortErrors([]error{workflowErr}))
   157  	s.Equal([]error{genericErr}, sortErrors([]error{genericErr}))
   158  	s.Equal([]error{randomErr}, sortErrors([]error{randomErr}))
   159  }
   160  
   161  func (s *cassandraErrorsSuite) TestExtractShardOwnershipLostError_Failed() {
   162  	rangeID := int64(1234)
   163  
   164  	err := extractShardOwnershipLostError(map[string]interface{}{}, rand.Int31(), rangeID)
   165  	s.NoError(err)
   166  
   167  	t := rowTypeExecution
   168  	err = extractShardOwnershipLostError(map[string]interface{}{
   169  		"type":     &t,
   170  		"range_id": rangeID,
   171  	}, rand.Int31(), rangeID)
   172  	s.NoError(err)
   173  
   174  	t = rowTypeShard
   175  	err = extractShardOwnershipLostError(map[string]interface{}{
   176  		"type":     &t,
   177  		"range_id": rangeID,
   178  	}, rand.Int31(), rangeID)
   179  	s.NoError(err)
   180  }
   181  
   182  func (s *cassandraErrorsSuite) TestExtractShardOwnershipLostError_Success() {
   183  	rangeID := int64(1234)
   184  	t := rowTypeShard
   185  	record := map[string]interface{}{
   186  		"type":     &t,
   187  		"range_id": rangeID,
   188  	}
   189  
   190  	err := extractShardOwnershipLostError(record, rand.Int31(), rangeID+1)
   191  	s.IsType(&p.ShardOwnershipLostError{}, err)
   192  }
   193  
   194  func (s *cassandraErrorsSuite) TestExtractCurrentWorkflowConflictError_Failed() {
   195  	runID, _ := uuid.Parse(permanentRunID)
   196  	currentRunID := uuid.New()
   197  
   198  	err := extractCurrentWorkflowConflictError(map[string]interface{}{}, uuid.New().String())
   199  	s.NoError(err)
   200  
   201  	t := rowTypeShard
   202  	err = extractCurrentWorkflowConflictError(map[string]interface{}{
   203  		"type":           &t,
   204  		"run_id":         gocql.UUID(runID),
   205  		"current_run_id": gocql.UUID(currentRunID),
   206  	}, uuid.New().String())
   207  	s.NoError(err)
   208  
   209  	t = rowTypeExecution
   210  	err = extractCurrentWorkflowConflictError(map[string]interface{}{
   211  		"type":           &t,
   212  		"run_id":         gocql.UUID([16]byte{}),
   213  		"current_run_id": gocql.UUID(currentRunID),
   214  	}, uuid.New().String())
   215  	s.NoError(err)
   216  
   217  	t = rowTypeExecution
   218  	err = extractCurrentWorkflowConflictError(map[string]interface{}{
   219  		"type":           &t,
   220  		"run_id":         gocql.UUID(runID),
   221  		"current_run_id": gocql.UUID(currentRunID),
   222  	}, currentRunID.String())
   223  	s.NoError(err)
   224  }
   225  
   226  func (s *cassandraErrorsSuite) TestExtractCurrentWorkflowConflictError_Success() {
   227  	runID, _ := uuid.Parse(permanentRunID)
   228  	currentRunID := uuid.New()
   229  	workflowState := &persistencespb.WorkflowExecutionState{}
   230  	blob, err := serialization.WorkflowExecutionStateToBlob(workflowState)
   231  	s.NoError(err)
   232  	t := rowTypeExecution
   233  	record := map[string]interface{}{
   234  		"type":                        &t,
   235  		"run_id":                      gocql.UUID(runID),
   236  		"current_run_id":              gocql.UUID(currentRunID),
   237  		"execution_state":             blob.Data,
   238  		"execution_state_encoding":    blob.EncodingType.String(),
   239  		"workflow_last_write_version": rand.Int63(),
   240  	}
   241  
   242  	err = extractCurrentWorkflowConflictError(record, uuid.New().String())
   243  	s.IsType(&p.CurrentWorkflowConditionFailedError{}, err)
   244  }
   245  
   246  func (s *cassandraErrorsSuite) TestExtractWorkflowConflictError_Failed() {
   247  	runID := uuid.New()
   248  	dbVersion := rand.Int63() + 1
   249  
   250  	err := extractWorkflowConflictError(map[string]interface{}{}, runID.String(), dbVersion, rand.Int63())
   251  	s.NoError(err)
   252  
   253  	t := rowTypeShard
   254  	err = extractWorkflowConflictError(map[string]interface{}{
   255  		"type":              &t,
   256  		"run_id":            gocql.UUID(runID),
   257  		"db_record_version": dbVersion,
   258  	}, runID.String(), dbVersion+1, rand.Int63())
   259  	s.NoError(err)
   260  
   261  	t = rowTypeExecution
   262  	err = extractWorkflowConflictError(map[string]interface{}{
   263  		"type":              &t,
   264  		"run_id":            gocql.UUID([16]byte{}),
   265  		"db_record_version": dbVersion,
   266  	}, runID.String(), dbVersion+1, rand.Int63())
   267  	s.NoError(err)
   268  
   269  	t = rowTypeExecution
   270  	err = extractWorkflowConflictError(map[string]interface{}{
   271  		"type":              &t,
   272  		"run_id":            gocql.UUID(runID),
   273  		"db_record_version": dbVersion,
   274  	}, runID.String(), dbVersion, rand.Int63())
   275  	s.NoError(err)
   276  }
   277  
   278  func (s *cassandraErrorsSuite) TestExtractWorkflowConflictError_Success() {
   279  	runID := uuid.New()
   280  	dbVersion := rand.Int63() + 1
   281  	t := rowTypeExecution
   282  	record := map[string]interface{}{
   283  		"type":              &t,
   284  		"run_id":            gocql.UUID(runID),
   285  		"db_record_version": dbVersion,
   286  	}
   287  
   288  	err := extractWorkflowConflictError(record, runID.String(), dbVersion+1, rand.Int63())
   289  	s.IsType(&p.WorkflowConditionFailedError{}, err)
   290  }
   291  
   292  // TODO remove this block once DB version comparison is the default
   293  func (s *cassandraErrorsSuite) TestExtractWorkflowConflictError_Failed_NextEventID() {
   294  	runID := uuid.New()
   295  	nextEventID := rand.Int63()
   296  
   297  	err := extractWorkflowConflictError(map[string]interface{}{}, runID.String(), 0, nextEventID)
   298  	s.NoError(err)
   299  
   300  	t := rowTypeShard
   301  	err = extractWorkflowConflictError(map[string]interface{}{
   302  		"type":          &t,
   303  		"run_id":        gocql.UUID(runID),
   304  		"next_event_id": nextEventID + 1,
   305  	}, runID.String(), 0, nextEventID)
   306  	s.NoError(err)
   307  
   308  	t = rowTypeExecution
   309  	err = extractWorkflowConflictError(map[string]interface{}{
   310  		"type":          &t,
   311  		"run_id":        gocql.UUID([16]byte{}),
   312  		"next_event_id": nextEventID + 1,
   313  	}, runID.String(), 0, nextEventID)
   314  	s.NoError(err)
   315  
   316  	t = rowTypeExecution
   317  	err = extractWorkflowConflictError(map[string]interface{}{
   318  		"type":          &t,
   319  		"run_id":        gocql.UUID(runID),
   320  		"next_event_id": nextEventID,
   321  	}, runID.String(), 0, nextEventID)
   322  	s.NoError(err)
   323  }
   324  
   325  // TODO remove this block once DB version comparison is the default
   326  func (s *cassandraErrorsSuite) TestExtractWorkflowConflictError_Success_NextEventID() {
   327  	runID := uuid.New()
   328  	nextEventID := int64(1234)
   329  	t := rowTypeExecution
   330  	record := map[string]interface{}{
   331  		"type":          &t,
   332  		"run_id":        gocql.UUID(runID),
   333  		"next_event_id": nextEventID,
   334  	}
   335  
   336  	err := extractWorkflowConflictError(record, runID.String(), 0, nextEventID+1)
   337  	s.IsType(&p.WorkflowConditionFailedError{}, err)
   338  }