go.temporal.io/server@v1.23.0/common/persistence/history_task_queue_manager_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 persistence_test 26 27 import ( 28 "context" 29 "strconv" 30 "testing" 31 "time" 32 33 "github.com/stretchr/testify/assert" 34 commonpb "go.temporal.io/api/common/v1" 35 enumspb "go.temporal.io/api/enums/v1" 36 37 "go.temporal.io/server/common/definition" 38 "go.temporal.io/server/common/persistence" 39 "go.temporal.io/server/service/history/tasks" 40 ) 41 42 // TestQueueKey_Determinism tests that the queue name generated from a QueueKey is deterministic. This is important to 43 // test for because we don't want to accidentally change the queue name generation algorithm and break the mapping of 44 // queue keys to queue names. 45 func TestQueueKey_Determinism(t *testing.T) { 46 name := persistence.QueueKey{ 47 Category: tasks.CategoryTransfer, 48 SourceCluster: "a", 49 TargetCluster: "b", 50 }.GetQueueName() 51 assert.Equal(t, name, "1_a_b_5aAf7hTg") 52 } 53 54 // TestQueueKey_Conflicts tests that unique tuples of cluster names containing the delimiter character will not produce 55 // names with conflicts when used to form queue names. 56 func TestQueueKey_Conflicts(t *testing.T) { 57 t.Parallel() 58 59 for _, tc := range []struct { 60 name string 61 explanation string 62 source1 string 63 target1 string 64 source2 string 65 target2 string 66 }{ 67 { 68 name: "(a,b_c) and (a_b,c)", 69 explanation: "If we just concatenate the cluster names with the queue name delimiter, both of these would" + 70 " produce the same queue name: 1_a_b_c", 71 source1: "a", 72 target1: "b_c", 73 source2: "a_b", 74 target2: "c", 75 }, 76 { 77 name: "(x_,x) and (x,_x)", 78 explanation: "If we concatenate the cluster names with the queue name delimiter and a hash of the" + 79 " concatenated cluster names, both of these would produce the same queue name: 1_x__x_<hash(x_x)>", 80 source1: "x_", 81 target1: "x", 82 source2: "x", 83 target2: "_x", 84 }, 85 } { 86 t.Run(tc.name, func(t *testing.T) { 87 k1 := persistence.QueueKey{ 88 Category: tasks.CategoryTransfer, 89 SourceCluster: tc.source1, 90 TargetCluster: tc.target1, 91 }.GetQueueName() 92 k2 := persistence.QueueKey{ 93 Category: tasks.CategoryTransfer, 94 SourceCluster: tc.source2, 95 TargetCluster: tc.target2, 96 }.GetQueueName() 97 98 // This test would fail if we did something naive to form the queue key like <category>_<source>_<target>. 99 assert.NotEqual(t, k1, k2, 100 "Two pairs of cluster names which are the same when concatenated with the queue name "+ 101 "delimiter should not have the same queue name.", tc.explanation) 102 }) 103 } 104 } 105 106 func TestHistoryTaskQueueManager_ErrSerializeTaskToEnqueue(t *testing.T) { 107 t.Parallel() 108 109 task := tasks.NewFakeTask(definition.WorkflowKey{}, tasks.Category{}, time.Time{}) 110 m := persistence.NewHistoryTaskQueueManager(nil) 111 _, err := m.EnqueueTask(context.Background(), &persistence.EnqueueTaskRequest{ 112 Task: task, 113 SourceShardID: 1, 114 }) 115 assert.ErrorContains(t, err, persistence.ErrMsgSerializeTaskToEnqueue, "EnqueueTask should return "+ 116 "ErrMsgSerializeTaskToEnqueue when the task cannot be serialized due to an invalid task category") 117 } 118 119 func TestHistoryTaskQueueManager_InvalidShardID(t *testing.T) { 120 t.Parallel() 121 122 task := &tasks.WorkflowTask{} 123 m := persistence.NewHistoryTaskQueueManager(nil) 124 _, err := m.EnqueueTask(context.Background(), &persistence.EnqueueTaskRequest{ 125 Task: task, 126 SourceShardID: 0, 127 }) 128 assert.ErrorIs(t, err, persistence.ErrShardIDInvalid) 129 } 130 131 // corruptQueue is a QueueV2 implementation that returns a single message that cannot be deserialized into a task. 132 type corruptQueue struct { 133 persistence.QueueV2 134 } 135 136 func (f corruptQueue) ReadMessages( 137 context.Context, 138 *persistence.InternalReadMessagesRequest, 139 ) (*persistence.InternalReadMessagesResponse, error) { 140 return &persistence.InternalReadMessagesResponse{ 141 Messages: []persistence.QueueV2Message{ 142 { 143 Data: &commonpb.DataBlob{ 144 EncodingType: enumspb.ENCODING_TYPE_PROTO3, 145 Data: []byte("some bytes that cannot be deserialized into a task"), 146 }, 147 }, 148 }, 149 NextPageToken: nil, 150 }, nil 151 } 152 153 func TestHistoryTaskQueueManager_ReadTasks_ErrDeserializeRawHistoryTask(t *testing.T) { 154 t.Parallel() 155 156 m := persistence.NewHistoryTaskQueueManager(corruptQueue{}) 157 _, err := m.ReadTasks(context.Background(), &persistence.ReadTasksRequest{ 158 QueueKey: persistence.QueueKey{ 159 Category: tasks.CategoryTransfer, 160 }, 161 PageSize: 1, 162 }) 163 assert.ErrorContains(t, err, persistence.ErrMsgDeserializeRawHistoryTask, 164 "ReadTasks should return ErrMsgDeserializeRawHistoryTask when the raw task cannot be deserialized"+ 165 " due to an error in the persistence layer") 166 } 167 168 func TestHistoryTaskQueueManager_ReadTasks_NonPositivePageSize(t *testing.T) { 169 t.Parallel() 170 171 m := persistence.NewHistoryTaskQueueManager(corruptQueue{}) 172 for _, pageSize := range []int{0, -1} { 173 _, err := m.ReadTasks(context.Background(), &persistence.ReadTasksRequest{ 174 QueueKey: persistence.QueueKey{ 175 Category: tasks.Category{}, 176 }, 177 PageSize: pageSize, 178 }) 179 assert.ErrorIs(t, err, persistence.ErrReadTasksNonPositivePageSize, "ReadTasks should return "+ 180 "ErrReadTasksNonPositivePageSize when the request's page size is: "+strconv.Itoa(pageSize)) 181 } 182 }