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 }