github.com/uber/kraken@v0.1.4/lib/persistedretry/manager_test.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package persistedretry_test 15 16 import ( 17 "errors" 18 "runtime" 19 "testing" 20 "time" 21 22 "github.com/golang/mock/gomock" 23 "github.com/stretchr/testify/require" 24 "github.com/uber-go/tally" 25 26 . "github.com/uber/kraken/lib/persistedretry" 27 "github.com/uber/kraken/mocks/lib/persistedretry" 28 ) 29 30 func waitForWorkers() { 31 runtime.Gosched() 32 time.Sleep(10 * time.Millisecond) 33 } 34 35 type managerMocks struct { 36 ctrl *gomock.Controller 37 config Config 38 store *mockpersistedretry.MockStore 39 executor *mockpersistedretry.MockExecutor 40 } 41 42 func newManagerMocks(t *testing.T) (*managerMocks, func()) { 43 ctrl := gomock.NewController(t) 44 return &managerMocks{ 45 ctrl: ctrl, 46 config: Config{ 47 IncomingBuffer: 0, 48 RetryBuffer: 0, 49 NumIncomingWorkers: 1, 50 NumRetryWorkers: 1, 51 MaxTaskThroughput: 5 * time.Millisecond, 52 RetryInterval: 100 * time.Millisecond, 53 PollRetriesInterval: 5 * time.Millisecond, 54 Testing: true, 55 }, 56 store: mockpersistedretry.NewMockStore(ctrl), 57 executor: mockpersistedretry.NewMockExecutor(ctrl), 58 }, ctrl.Finish 59 } 60 61 func (m *managerMocks) new() (Manager, error) { 62 m.executor.EXPECT().Name().Return("mock executor") 63 return NewManager(m.config, tally.NoopScope, m.store, m.executor) 64 } 65 66 func (m *managerMocks) task() *mockpersistedretry.MockTask { 67 return mockpersistedretry.NewMockTask(m.ctrl) 68 } 69 70 func TestNewManagerMarksAllPendingTasksAsFailed(t *testing.T) { 71 require := require.New(t) 72 73 mocks, cleanup := newManagerMocks(t) 74 defer cleanup() 75 76 tasks := []Task{mocks.task(), mocks.task()} 77 78 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 79 80 gomock.InOrder( 81 mocks.store.EXPECT().GetPending().Return(tasks, nil), 82 mocks.store.EXPECT().MarkFailed(tasks[0]).Return(nil), 83 mocks.store.EXPECT().MarkFailed(tasks[1]).Return(nil), 84 ) 85 86 m, err := mocks.new() 87 require.NoError(err) 88 defer m.Close() 89 90 time.Sleep(50 * time.Millisecond) 91 } 92 93 func TestManagerAddTaskSuccess(t *testing.T) { 94 require := require.New(t) 95 96 mocks, cleanup := newManagerMocks(t) 97 defer cleanup() 98 99 task := mocks.task() 100 101 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 102 103 gomock.InOrder( 104 mocks.store.EXPECT().GetPending().Return(nil, nil), 105 task.EXPECT().Ready().Return(true), 106 mocks.store.EXPECT().AddPending(task).Return(nil), 107 mocks.executor.EXPECT().Exec(task).Return(nil), 108 mocks.store.EXPECT().Remove(task).Return(nil), 109 ) 110 111 m, err := mocks.new() 112 require.NoError(err) 113 defer m.Close() 114 115 waitForWorkers() 116 117 require.NoError(m.Add(task)) 118 119 time.Sleep(50 * time.Millisecond) 120 } 121 122 func TestManagerAddTaskClosed(t *testing.T) { 123 require := require.New(t) 124 125 mocks, cleanup := newManagerMocks(t) 126 defer cleanup() 127 128 mocks.store.EXPECT().GetPending().Return(nil, nil) 129 130 m, err := mocks.new() 131 require.NoError(err) 132 133 m.Close() 134 135 require.Equal(ErrManagerClosed, m.Add(mocks.task())) 136 } 137 138 func TestManagerAddTaskFail(t *testing.T) { 139 require := require.New(t) 140 141 mocks, cleanup := newManagerMocks(t) 142 defer cleanup() 143 144 task := mocks.task() 145 146 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 147 148 gomock.InOrder( 149 mocks.store.EXPECT().GetPending().Return(nil, nil), 150 task.EXPECT().Ready().Return(true), 151 mocks.store.EXPECT().AddPending(task).Return(nil), 152 mocks.executor.EXPECT().Exec(task).Return(errors.New("task failed")), 153 mocks.store.EXPECT().MarkFailed(task).Return(nil), 154 task.EXPECT().GetFailures().Return(1), 155 task.EXPECT().Tags().Return(nil), 156 ) 157 158 m, err := mocks.new() 159 require.NoError(err) 160 defer m.Close() 161 162 waitForWorkers() 163 164 require.NoError(m.Add(task)) 165 166 time.Sleep(50 * time.Millisecond) 167 } 168 169 func TestManagerAddTaskFallbackWhenWorkersBusy(t *testing.T) { 170 require := require.New(t) 171 172 mocks, cleanup := newManagerMocks(t) 173 defer cleanup() 174 175 task1 := mocks.task() 176 task2 := mocks.task() 177 178 task1Done := make(chan bool) 179 180 mocks.store.EXPECT().GetPending().Return(nil, nil) 181 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 182 183 gomock.InOrder( 184 task1.EXPECT().Ready().Return(true), 185 mocks.store.EXPECT().AddPending(task1).Return(nil), 186 mocks.executor.EXPECT().Exec(task1).DoAndReturn(func(Task) error { 187 <-task1Done 188 return nil 189 }), 190 mocks.store.EXPECT().Remove(task1).Return(nil), 191 ) 192 193 gomock.InOrder( 194 task2.EXPECT().Ready().Return(true), 195 mocks.store.EXPECT().AddPending(task2).Return(nil), 196 mocks.store.EXPECT().MarkFailed(task2).Return(nil), 197 ) 198 199 m, err := mocks.new() 200 require.NoError(err) 201 defer m.Close() 202 203 waitForWorkers() 204 205 // First task blocks, so the only worker is busy when we add second task, which 206 // should then fallback to failed. 207 require.NoError(m.Add(task1)) 208 require.NoError(m.Add(task2)) 209 210 task1Done <- true 211 212 time.Sleep(50 * time.Millisecond) 213 } 214 215 func TestManagerRetriesFailedTasks(t *testing.T) { 216 require := require.New(t) 217 218 mocks, cleanup := newManagerMocks(t) 219 defer cleanup() 220 221 task := mocks.task() 222 223 gomock.InOrder( 224 mocks.store.EXPECT().GetPending().Return(nil, nil).MinTimes(1), 225 mocks.store.EXPECT().GetFailed().Return([]Task{task}, nil), 226 task.EXPECT().Ready().Return(true), 227 task.EXPECT().GetLastAttempt().Return(time.Time{}), 228 mocks.store.EXPECT().MarkPending(task), 229 mocks.executor.EXPECT().Exec(task).Return(nil), 230 mocks.store.EXPECT().Remove(task).Return(nil), 231 ) 232 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 233 234 m, err := mocks.new() 235 require.NoError(err) 236 defer m.Close() 237 238 time.Sleep(50 * time.Millisecond) 239 } 240 241 func TestManagerRetriesSkipsNotReadyTasks(t *testing.T) { 242 require := require.New(t) 243 244 mocks, cleanup := newManagerMocks(t) 245 defer cleanup() 246 247 task := mocks.task() 248 249 gomock.InOrder( 250 mocks.store.EXPECT().GetPending().Return(nil, nil).MinTimes(1), 251 mocks.store.EXPECT().GetFailed().Return([]Task{task}, nil), 252 task.EXPECT().Ready().Return(false), 253 ) 254 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 255 256 m, err := mocks.new() 257 require.NoError(err) 258 defer m.Close() 259 260 time.Sleep(50 * time.Millisecond) 261 } 262 263 func TestManagerRetriesSkipsRecentlyAttemptedTasks(t *testing.T) { 264 require := require.New(t) 265 266 mocks, cleanup := newManagerMocks(t) 267 defer cleanup() 268 269 task := mocks.task() 270 271 gomock.InOrder( 272 mocks.store.EXPECT().GetPending().Return(nil, nil).MinTimes(1), 273 mocks.store.EXPECT().GetFailed().Return([]Task{task}, nil), 274 task.EXPECT().Ready().Return(true), 275 task.EXPECT().GetLastAttempt().Return(time.Now()), 276 ) 277 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 278 279 m, err := mocks.new() 280 require.NoError(err) 281 defer m.Close() 282 283 time.Sleep(50 * time.Millisecond) 284 } 285 286 func TestManagerAddNotReadyTaskMarksAsFailed(t *testing.T) { 287 require := require.New(t) 288 289 mocks, cleanup := newManagerMocks(t) 290 defer cleanup() 291 292 task := mocks.task() 293 294 mocks.store.EXPECT().GetPending().Return(nil, nil) 295 296 gomock.InOrder( 297 task.EXPECT().Ready().Return(false), 298 mocks.store.EXPECT().AddFailed(task).Return(nil), 299 ) 300 301 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 302 303 m, err := mocks.new() 304 require.NoError(err) 305 defer m.Close() 306 307 waitForWorkers() 308 309 require.NoError(m.Add(task)) 310 } 311 312 func TestManagerNoopsOnFailedTaskConflicts(t *testing.T) { 313 require := require.New(t) 314 315 mocks, cleanup := newManagerMocks(t) 316 defer cleanup() 317 318 task := mocks.task() 319 320 mocks.store.EXPECT().GetPending().Return(nil, nil) 321 322 gomock.InOrder( 323 task.EXPECT().Ready().Return(false), 324 mocks.store.EXPECT().AddFailed(task).Return(ErrTaskExists), 325 ) 326 327 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 328 329 m, err := mocks.new() 330 require.NoError(err) 331 defer m.Close() 332 333 waitForWorkers() 334 335 require.NoError(m.Add(task)) 336 } 337 338 func TestManagerNoopsOnPendingTaskConflicts(t *testing.T) { 339 require := require.New(t) 340 341 mocks, cleanup := newManagerMocks(t) 342 defer cleanup() 343 344 task := mocks.task() 345 346 mocks.store.EXPECT().GetPending().Return(nil, nil) 347 348 gomock.InOrder( 349 task.EXPECT().Ready().Return(true), 350 mocks.store.EXPECT().AddPending(task).Return(ErrTaskExists), 351 ) 352 353 mocks.store.EXPECT().GetFailed().Return(nil, nil).AnyTimes() 354 355 m, err := mocks.new() 356 require.NoError(err) 357 defer m.Close() 358 359 waitForWorkers() 360 361 require.NoError(m.Add(task)) 362 } 363 364 func TestManagerExec(t *testing.T) { 365 require := require.New(t) 366 367 mocks, cleanup := newManagerMocks(t) 368 defer cleanup() 369 370 task := mocks.task() 371 372 mocks.store.EXPECT().GetPending().Return(nil, nil) 373 374 m, err := mocks.new() 375 require.NoError(err) 376 defer m.Close() 377 378 mocks.executor.EXPECT().Exec(task).Return(nil) 379 380 require.NoError(m.SyncExec(task)) 381 }