go.temporal.io/server@v1.23.0/common/util_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 common 26 27 import ( 28 "context" 29 "errors" 30 "testing" 31 "time" 32 33 "github.com/pborman/uuid" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 commonpb "go.temporal.io/api/common/v1" 37 enumspb "go.temporal.io/api/enums/v1" 38 "go.temporal.io/api/serviceerror" 39 "google.golang.org/protobuf/types/known/durationpb" 40 41 "go.temporal.io/server/common/dynamicconfig" 42 ) 43 44 func TestValidateRetryPolicy(t *testing.T) { 45 testCases := []struct { 46 name string 47 input *commonpb.RetryPolicy 48 wantErr bool 49 wantErrString string 50 }{ 51 { 52 name: "nil policy is okay", 53 input: nil, 54 wantErr: false, 55 wantErrString: "", 56 }, 57 { 58 name: "maxAttempts is 1, coefficient < 1", 59 input: &commonpb.RetryPolicy{ 60 BackoffCoefficient: 0.5, 61 MaximumAttempts: 1, 62 }, 63 wantErr: false, 64 wantErrString: "", 65 }, 66 { 67 name: "initial interval negative", 68 input: &commonpb.RetryPolicy{ 69 InitialInterval: durationpb.New(-22 * time.Second), 70 }, 71 wantErr: true, 72 wantErrString: "InitialInterval cannot be negative on retry policy.", 73 }, 74 { 75 name: "coefficient < 1", 76 input: &commonpb.RetryPolicy{ 77 BackoffCoefficient: 0.8, 78 }, 79 wantErr: true, 80 wantErrString: "BackoffCoefficient cannot be less than 1 on retry policy.", 81 }, 82 { 83 name: "maximum interval in seconds is negative", 84 input: &commonpb.RetryPolicy{ 85 BackoffCoefficient: 2.0, 86 MaximumInterval: durationpb.New(-2 * time.Second), 87 }, 88 wantErr: true, 89 wantErrString: "MaximumInterval cannot be negative on retry policy.", 90 }, 91 { 92 name: "maximum interval in less than initial interval", 93 input: &commonpb.RetryPolicy{ 94 BackoffCoefficient: 2.0, 95 MaximumInterval: durationpb.New(5 * time.Second), 96 InitialInterval: durationpb.New(10 * time.Second), 97 }, 98 wantErr: true, 99 wantErrString: "MaximumInterval cannot be less than InitialInterval on retry policy.", 100 }, 101 { 102 name: "maximum attempts negative", 103 input: &commonpb.RetryPolicy{ 104 BackoffCoefficient: 2.0, 105 MaximumAttempts: -3, 106 }, 107 wantErr: true, 108 wantErrString: "MaximumAttempts cannot be negative on retry policy.", 109 }, 110 { 111 name: "timeout nonretryable error - valid type", 112 input: &commonpb.RetryPolicy{ 113 BackoffCoefficient: 1, 114 NonRetryableErrorTypes: []string{ 115 TimeoutFailureTypePrefix + enumspb.TIMEOUT_TYPE_START_TO_CLOSE.String(), 116 TimeoutFailureTypePrefix + enumspb.TIMEOUT_TYPE_SCHEDULE_TO_START.String(), 117 TimeoutFailureTypePrefix + enumspb.TIMEOUT_TYPE_SCHEDULE_TO_CLOSE.String(), 118 TimeoutFailureTypePrefix + enumspb.TIMEOUT_TYPE_HEARTBEAT.String(), 119 }, 120 }, 121 wantErr: false, 122 wantErrString: "", 123 }, 124 { 125 name: "timeout nonretryable error - unspecified type", 126 input: &commonpb.RetryPolicy{ 127 BackoffCoefficient: 1, 128 NonRetryableErrorTypes: []string{ 129 TimeoutFailureTypePrefix + enumspb.TIMEOUT_TYPE_UNSPECIFIED.String(), 130 }, 131 }, 132 wantErr: true, 133 wantErrString: "Invalid timeout type value: Unspecified.", 134 }, 135 { 136 name: "timeout nonretryable error - unknown type", 137 input: &commonpb.RetryPolicy{ 138 BackoffCoefficient: 1, 139 NonRetryableErrorTypes: []string{ 140 TimeoutFailureTypePrefix + "unknown", 141 }, 142 }, 143 wantErr: true, 144 wantErrString: "Invalid timeout type value: unknown.", 145 }, 146 } 147 148 for _, tt := range testCases { 149 t.Run(tt.name, func(t *testing.T) { 150 err := ValidateRetryPolicy(tt.input) 151 if tt.wantErr { 152 assert.NotNil(t, err, "expected error - did not get one") 153 assert.Equal(t, err.Error(), tt.wantErrString, "unexpected error message") 154 } else { 155 assert.Nil(t, err, "unexpected error") 156 } 157 }) 158 } 159 } 160 161 func TestEnsureRetryPolicyDefaults(t *testing.T) { 162 defaultRetrySettings := DefaultRetrySettings{ 163 InitialInterval: time.Second, 164 MaximumIntervalCoefficient: 100, 165 BackoffCoefficient: 2.0, 166 MaximumAttempts: 120, 167 } 168 169 defaultRetryPolicy := &commonpb.RetryPolicy{ 170 InitialInterval: durationpb.New(1 * time.Second), 171 MaximumInterval: durationpb.New(100 * time.Second), 172 BackoffCoefficient: 2.0, 173 MaximumAttempts: 120, 174 } 175 176 testCases := []struct { 177 name string 178 input *commonpb.RetryPolicy 179 want *commonpb.RetryPolicy 180 }{ 181 { 182 name: "default fields are set ", 183 input: &commonpb.RetryPolicy{}, 184 want: defaultRetryPolicy, 185 }, 186 { 187 name: "non-default InitialIntervalInSeconds is not set", 188 input: &commonpb.RetryPolicy{ 189 InitialInterval: durationpb.New(2 * time.Second), 190 }, 191 want: &commonpb.RetryPolicy{ 192 InitialInterval: durationpb.New(2 * time.Second), 193 MaximumInterval: durationpb.New(200 * time.Second), 194 BackoffCoefficient: 2, 195 MaximumAttempts: 120, 196 }, 197 }, 198 { 199 name: "non-default MaximumIntervalInSeconds is not set", 200 input: &commonpb.RetryPolicy{ 201 MaximumInterval: durationpb.New(1000 * time.Second), 202 }, 203 want: &commonpb.RetryPolicy{ 204 InitialInterval: durationpb.New(1 * time.Second), 205 MaximumInterval: durationpb.New(1000 * time.Second), 206 BackoffCoefficient: 2, 207 MaximumAttempts: 120, 208 }, 209 }, 210 { 211 name: "non-default BackoffCoefficient is not set", 212 input: &commonpb.RetryPolicy{ 213 BackoffCoefficient: 1.5, 214 }, 215 want: &commonpb.RetryPolicy{ 216 InitialInterval: durationpb.New(1 * time.Second), 217 MaximumInterval: durationpb.New(100 * time.Second), 218 BackoffCoefficient: 1.5, 219 MaximumAttempts: 120, 220 }, 221 }, 222 { 223 name: "non-default Maximum attempts is not set", 224 input: &commonpb.RetryPolicy{ 225 MaximumAttempts: 49, 226 }, 227 want: &commonpb.RetryPolicy{ 228 InitialInterval: durationpb.New(1 * time.Second), 229 MaximumInterval: durationpb.New(100 * time.Second), 230 BackoffCoefficient: 2, 231 MaximumAttempts: 49, 232 }, 233 }, 234 { 235 name: "non-retryable errors are set", 236 input: &commonpb.RetryPolicy{ 237 NonRetryableErrorTypes: []string{"testFailureType"}, 238 }, 239 want: &commonpb.RetryPolicy{ 240 InitialInterval: durationpb.New(1 * time.Second), 241 MaximumInterval: durationpb.New(100 * time.Second), 242 BackoffCoefficient: 2.0, 243 MaximumAttempts: 120, 244 NonRetryableErrorTypes: []string{"testFailureType"}, 245 }, 246 }, 247 } 248 249 for _, tt := range testCases { 250 t.Run(tt.name, func(t *testing.T) { 251 EnsureRetryPolicyDefaults(tt.input, defaultRetrySettings) 252 assert.Equal(t, tt.want, tt.input) 253 }) 254 } 255 } 256 257 func Test_FromConfigToRetryPolicy(t *testing.T) { 258 options := map[string]interface{}{ 259 initialIntervalInSecondsConfigKey: 2, 260 maximumIntervalCoefficientConfigKey: 100.0, 261 backoffCoefficientConfigKey: 4.0, 262 maximumAttemptsConfigKey: 5, 263 } 264 265 defaultSettings := FromConfigToDefaultRetrySettings(options) 266 assert.Equal(t, 2*time.Second, defaultSettings.InitialInterval) 267 assert.Equal(t, 100.0, defaultSettings.MaximumIntervalCoefficient) 268 assert.Equal(t, 4.0, defaultSettings.BackoffCoefficient) 269 assert.Equal(t, int32(5), defaultSettings.MaximumAttempts) 270 } 271 272 func TestIsContextDeadlineExceededErr(t *testing.T) { 273 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Millisecond)) 274 defer cancel() 275 require.True(t, IsContextDeadlineExceededErr(ctx.Err())) 276 require.True(t, IsContextDeadlineExceededErr(serviceerror.NewDeadlineExceeded("something"))) 277 278 require.False(t, IsContextDeadlineExceededErr(errors.New("some random error"))) 279 280 ctx, cancel = context.WithCancel(context.Background()) 281 cancel() 282 require.False(t, IsContextDeadlineExceededErr(ctx.Err())) 283 } 284 285 func TestIsContextCanceledErr(t *testing.T) { 286 require.True(t, IsContextCanceledErr(serviceerror.NewCanceled("something"))) 287 require.False(t, IsContextCanceledErr(errors.New("some random error"))) 288 289 ctx, cancel := context.WithCancel(context.Background()) 290 cancel() 291 require.True(t, IsContextCanceledErr(ctx.Err())) 292 } 293 294 func TestOverrideWorkflowRunTimeout_InfiniteRunTimeout_InfiniteExecutionTimeout(t *testing.T) { 295 runTimeout := time.Duration(0) 296 executionTimeout := time.Duration(0) 297 require.Equal(t, time.Duration(0), OverrideWorkflowRunTimeout(runTimeout, executionTimeout)) 298 } 299 300 func TestOverrideWorkflowRunTimeout_FiniteRunTimeout_InfiniteExecutionTimeout(t *testing.T) { 301 runTimeout := time.Duration(10) 302 executionTimeout := time.Duration(0) 303 require.Equal(t, time.Duration(10), OverrideWorkflowRunTimeout(runTimeout, executionTimeout)) 304 } 305 306 func TestOverrideWorkflowRunTimeout_InfiniteRunTimeout_FiniteExecutionTimeout(t *testing.T) { 307 runTimeout := time.Duration(0) 308 executionTimeout := time.Duration(10) 309 require.Equal(t, time.Duration(10), OverrideWorkflowRunTimeout(runTimeout, executionTimeout)) 310 } 311 312 func TestOverrideWorkflowRunTimeout_FiniteRunTimeout_FiniteExecutionTimeout(t *testing.T) { 313 runTimeout := time.Duration(100) 314 executionTimeout := time.Duration(10) 315 require.Equal(t, time.Duration(10), OverrideWorkflowRunTimeout(runTimeout, executionTimeout)) 316 317 runTimeout = time.Duration(10) 318 executionTimeout = time.Duration(100) 319 require.Equal(t, time.Duration(10), OverrideWorkflowRunTimeout(runTimeout, executionTimeout)) 320 } 321 322 func TestOverrideWorkflowTaskTimeout_Infinite(t *testing.T) { 323 taskTimeout := time.Duration(0) 324 runTimeout := time.Duration(100) 325 defaultTimeout := time.Duration(20) 326 defaultTimeoutFn := dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 327 require.Equal(t, time.Duration(20), OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 328 329 taskTimeout = time.Duration(0) 330 runTimeout = time.Duration(10) 331 defaultTimeout = time.Duration(20) 332 defaultTimeoutFn = dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 333 require.Equal(t, time.Duration(10), OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 334 335 taskTimeout = time.Duration(0) 336 runTimeout = time.Duration(0) 337 defaultTimeout = time.Duration(30) 338 defaultTimeoutFn = dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 339 require.Equal(t, time.Duration(30), OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 340 341 taskTimeout = time.Duration(0) 342 runTimeout = time.Duration(0) 343 defaultTimeout = MaxWorkflowTaskStartToCloseTimeout + time.Duration(1) 344 defaultTimeoutFn = dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 345 require.Equal(t, MaxWorkflowTaskStartToCloseTimeout, OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 346 } 347 348 func TestOverrideWorkflowTaskTimeout_Finite(t *testing.T) { 349 taskTimeout := time.Duration(10) 350 runTimeout := MaxWorkflowTaskStartToCloseTimeout - time.Duration(1) 351 defaultTimeout := time.Duration(20) 352 defaultTimeoutFn := dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 353 require.Equal(t, time.Duration(10), OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 354 355 taskTimeout = MaxWorkflowTaskStartToCloseTimeout - time.Duration(1) 356 runTimeout = time.Duration(10) 357 defaultTimeout = time.Duration(20) 358 defaultTimeoutFn = dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 359 require.Equal(t, time.Duration(10), OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 360 361 taskTimeout = time.Duration(10) 362 runTimeout = MaxWorkflowTaskStartToCloseTimeout + time.Duration(1) 363 defaultTimeout = time.Duration(20) 364 defaultTimeoutFn = dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 365 require.Equal(t, time.Duration(10), OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 366 367 taskTimeout = MaxWorkflowTaskStartToCloseTimeout + time.Duration(1) 368 runTimeout = MaxWorkflowTaskStartToCloseTimeout + time.Duration(1) 369 defaultTimeout = time.Duration(20) 370 defaultTimeoutFn = dynamicconfig.GetDurationPropertyFnFilteredByNamespace(defaultTimeout) 371 require.Equal(t, MaxWorkflowTaskStartToCloseTimeout, OverrideWorkflowTaskTimeout("random domain", taskTimeout, runTimeout, defaultTimeoutFn)) 372 } 373 374 func TestMapShardID_ByNamespaceWorkflow_4And16(t *testing.T) { 375 namespaceID := uuid.New() 376 workflowID := uuid.New() 377 shardID4 := WorkflowIDToHistoryShard(namespaceID, workflowID, 4) 378 shardID16 := WorkflowIDToHistoryShard(namespaceID, workflowID, 16) 379 380 targetShardIDs := MapShardID(16, 4, shardID16) 381 require.Equal(t, []int32{ 382 shardID4, 383 }, targetShardIDs) 384 385 targetShardIDs = MapShardID(4, 16, shardID4) 386 found := false 387 for _, targetShardID := range targetShardIDs { 388 if shardID16 == targetShardID { 389 found = true 390 break 391 } 392 } 393 require.True(t, found) 394 } 395 396 func TestMapShardID_1To4(t *testing.T) { 397 sourceShardCount := int32(1) 398 targetShardCount := int32(4) 399 400 targetShards := MapShardID(sourceShardCount, targetShardCount, 1) 401 require.Equal(t, []int32{ 402 1, 2, 3, 4, 403 }, targetShards) 404 } 405 406 func TestMapShardID_4To1(t *testing.T) { 407 sourceShardCount := int32(4) 408 targetShardCount := int32(1) 409 410 targetShards := MapShardID(sourceShardCount, targetShardCount, 4) 411 require.Equal(t, []int32{1}, targetShards) 412 413 targetShards = MapShardID(sourceShardCount, targetShardCount, 3) 414 require.Equal(t, []int32{1}, targetShards) 415 416 targetShards = MapShardID(sourceShardCount, targetShardCount, 2) 417 require.Equal(t, []int32{1}, targetShards) 418 419 targetShards = MapShardID(sourceShardCount, targetShardCount, 1) 420 require.Equal(t, []int32{1}, targetShards) 421 } 422 423 func TestMapShardID_4To16(t *testing.T) { 424 sourceShardCount := int32(4) 425 targetShardCount := int32(16) 426 427 targetShards := MapShardID(sourceShardCount, targetShardCount, 1) 428 require.Equal(t, []int32{ 429 1, 5, 9, 13, 430 }, targetShards) 431 432 targetShards = MapShardID(sourceShardCount, targetShardCount, 2) 433 require.Equal(t, []int32{ 434 2, 6, 10, 14, 435 }, targetShards) 436 437 targetShards = MapShardID(sourceShardCount, targetShardCount, 3) 438 require.Equal(t, []int32{ 439 3, 7, 11, 15, 440 }, targetShards) 441 442 targetShards = MapShardID(sourceShardCount, targetShardCount, 4) 443 require.Equal(t, []int32{ 444 4, 8, 12, 16, 445 }, targetShards) 446 } 447 448 func TestMapShardID_16To4(t *testing.T) { 449 sourceShardCount := int32(16) 450 targetShardCount := int32(4) 451 452 targetShards := MapShardID(sourceShardCount, targetShardCount, 16) 453 require.Equal(t, []int32{4}, targetShards) 454 455 targetShards = MapShardID(sourceShardCount, targetShardCount, 15) 456 require.Equal(t, []int32{3}, targetShards) 457 458 targetShards = MapShardID(sourceShardCount, targetShardCount, 14) 459 require.Equal(t, []int32{2}, targetShards) 460 461 targetShards = MapShardID(sourceShardCount, targetShardCount, 13) 462 require.Equal(t, []int32{1}, targetShards) 463 464 targetShards = MapShardID(sourceShardCount, targetShardCount, 12) 465 require.Equal(t, []int32{4}, targetShards) 466 467 targetShards = MapShardID(sourceShardCount, targetShardCount, 11) 468 require.Equal(t, []int32{3}, targetShards) 469 470 targetShards = MapShardID(sourceShardCount, targetShardCount, 10) 471 require.Equal(t, []int32{2}, targetShards) 472 473 targetShards = MapShardID(sourceShardCount, targetShardCount, 9) 474 require.Equal(t, []int32{1}, targetShards) 475 476 targetShards = MapShardID(sourceShardCount, targetShardCount, 8) 477 require.Equal(t, []int32{4}, targetShards) 478 479 targetShards = MapShardID(sourceShardCount, targetShardCount, 7) 480 require.Equal(t, []int32{3}, targetShards) 481 482 targetShards = MapShardID(sourceShardCount, targetShardCount, 6) 483 require.Equal(t, []int32{2}, targetShards) 484 485 targetShards = MapShardID(sourceShardCount, targetShardCount, 5) 486 require.Equal(t, []int32{1}, targetShards) 487 488 targetShards = MapShardID(sourceShardCount, targetShardCount, 4) 489 require.Equal(t, []int32{4}, targetShards) 490 491 targetShards = MapShardID(sourceShardCount, targetShardCount, 3) 492 require.Equal(t, []int32{3}, targetShards) 493 494 targetShards = MapShardID(sourceShardCount, targetShardCount, 2) 495 require.Equal(t, []int32{2}, targetShards) 496 497 targetShards = MapShardID(sourceShardCount, targetShardCount, 1) 498 require.Equal(t, []int32{1}, targetShards) 499 } 500 501 func TestVerifyShardIDMapping_1VS4(t *testing.T) { 502 require.NoError(t, VerifyShardIDMapping(1, 4, 1, 1)) 503 require.NoError(t, VerifyShardIDMapping(1, 4, 1, 2)) 504 require.NoError(t, VerifyShardIDMapping(1, 4, 1, 3)) 505 require.NoError(t, VerifyShardIDMapping(1, 4, 1, 4)) 506 } 507 508 func TestVerifyShardIDMapping_2VS4(t *testing.T) { 509 require.NoError(t, VerifyShardIDMapping(2, 4, 1, 1)) 510 require.Error(t, VerifyShardIDMapping(2, 4, 1, 2)) 511 require.NoError(t, VerifyShardIDMapping(2, 4, 1, 3)) 512 require.Error(t, VerifyShardIDMapping(2, 4, 1, 4)) 513 514 require.Error(t, VerifyShardIDMapping(2, 4, 2, 1)) 515 require.NoError(t, VerifyShardIDMapping(2, 4, 2, 2)) 516 require.Error(t, VerifyShardIDMapping(2, 4, 2, 3)) 517 require.NoError(t, VerifyShardIDMapping(2, 4, 2, 4)) 518 }