github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/queue/queue_disk_channel_test.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package queue 7 8 import ( 9 "os" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/gitbundle/modules/log" 15 "github.com/gitbundle/modules/util" 16 17 "github.com/stretchr/testify/assert" 18 ) 19 20 func TestPersistableChannelQueue(t *testing.T) { 21 handleChan := make(chan *testData) 22 handle := func(data ...Data) []Data { 23 for _, datum := range data { 24 if datum == nil { 25 continue 26 } 27 testDatum := datum.(*testData) 28 handleChan <- testDatum 29 } 30 return nil 31 } 32 33 lock := sync.Mutex{} 34 queueShutdown := []func(){} 35 queueTerminate := []func(){} 36 37 tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-test-data") 38 assert.NoError(t, err) 39 defer util.RemoveAll(tmpDir) 40 41 queue, err := NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{ 42 DataDir: tmpDir, 43 BatchLength: 2, 44 QueueLength: 20, 45 Workers: 1, 46 BoostWorkers: 0, 47 MaxWorkers: 10, 48 Name: "first", 49 }, &testData{}) 50 assert.NoError(t, err) 51 52 readyForShutdown := make(chan struct{}) 53 readyForTerminate := make(chan struct{}) 54 55 go queue.Run(func(shutdown func()) { 56 lock.Lock() 57 defer lock.Unlock() 58 select { 59 case <-readyForShutdown: 60 default: 61 close(readyForShutdown) 62 } 63 queueShutdown = append(queueShutdown, shutdown) 64 }, func(terminate func()) { 65 lock.Lock() 66 defer lock.Unlock() 67 select { 68 case <-readyForTerminate: 69 default: 70 close(readyForTerminate) 71 } 72 queueTerminate = append(queueTerminate, terminate) 73 }) 74 75 test1 := testData{"A", 1} 76 test2 := testData{"B", 2} 77 78 err = queue.Push(&test1) 79 assert.NoError(t, err) 80 go func() { 81 err := queue.Push(&test2) 82 assert.NoError(t, err) 83 }() 84 85 result1 := <-handleChan 86 assert.Equal(t, test1.TestString, result1.TestString) 87 assert.Equal(t, test1.TestInt, result1.TestInt) 88 89 result2 := <-handleChan 90 assert.Equal(t, test2.TestString, result2.TestString) 91 assert.Equal(t, test2.TestInt, result2.TestInt) 92 93 // test1 is a testData not a *testData so will be rejected 94 err = queue.Push(test1) 95 assert.Error(t, err) 96 97 <-readyForShutdown 98 // Now shutdown the queue 99 lock.Lock() 100 callbacks := make([]func(), len(queueShutdown)) 101 copy(callbacks, queueShutdown) 102 lock.Unlock() 103 for _, callback := range callbacks { 104 callback() 105 } 106 107 // Wait til it is closed 108 <-queue.(*PersistableChannelQueue).closed 109 110 err = queue.Push(&test1) 111 assert.NoError(t, err) 112 err = queue.Push(&test2) 113 assert.NoError(t, err) 114 select { 115 case <-handleChan: 116 assert.Fail(t, "Handler processing should have stopped") 117 default: 118 } 119 120 // terminate the queue 121 <-readyForTerminate 122 lock.Lock() 123 callbacks = make([]func(), len(queueTerminate)) 124 copy(callbacks, queueTerminate) 125 lock.Unlock() 126 for _, callback := range callbacks { 127 callback() 128 } 129 130 select { 131 case <-handleChan: 132 assert.Fail(t, "Handler processing should have stopped") 133 default: 134 } 135 136 // Reopen queue 137 queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{ 138 DataDir: tmpDir, 139 BatchLength: 2, 140 QueueLength: 20, 141 Workers: 1, 142 BoostWorkers: 0, 143 MaxWorkers: 10, 144 Name: "second", 145 }, &testData{}) 146 assert.NoError(t, err) 147 148 readyForShutdown = make(chan struct{}) 149 readyForTerminate = make(chan struct{}) 150 151 go queue.Run(func(shutdown func()) { 152 lock.Lock() 153 defer lock.Unlock() 154 select { 155 case <-readyForShutdown: 156 default: 157 close(readyForShutdown) 158 } 159 queueShutdown = append(queueShutdown, shutdown) 160 }, func(terminate func()) { 161 lock.Lock() 162 defer lock.Unlock() 163 select { 164 case <-readyForTerminate: 165 default: 166 close(readyForTerminate) 167 } 168 queueTerminate = append(queueTerminate, terminate) 169 }) 170 171 result3 := <-handleChan 172 assert.Equal(t, test1.TestString, result3.TestString) 173 assert.Equal(t, test1.TestInt, result3.TestInt) 174 175 result4 := <-handleChan 176 assert.Equal(t, test2.TestString, result4.TestString) 177 assert.Equal(t, test2.TestInt, result4.TestInt) 178 179 <-readyForShutdown 180 lock.Lock() 181 callbacks = make([]func(), len(queueShutdown)) 182 copy(callbacks, queueShutdown) 183 lock.Unlock() 184 for _, callback := range callbacks { 185 callback() 186 } 187 <-readyForTerminate 188 lock.Lock() 189 callbacks = make([]func(), len(queueTerminate)) 190 copy(callbacks, queueTerminate) 191 lock.Unlock() 192 for _, callback := range callbacks { 193 callback() 194 } 195 } 196 197 func TestPersistableChannelQueue_Pause(t *testing.T) { 198 lock := sync.Mutex{} 199 var queue Queue 200 var err error 201 pushBack := false 202 203 handleChan := make(chan *testData) 204 handle := func(data ...Data) []Data { 205 lock.Lock() 206 if pushBack { 207 if pausable, ok := queue.(Pausable); ok { 208 log.Info("pausing") 209 pausable.Pause() 210 } 211 lock.Unlock() 212 return data 213 } 214 lock.Unlock() 215 216 for _, datum := range data { 217 testDatum := datum.(*testData) 218 handleChan <- testDatum 219 } 220 return nil 221 } 222 223 queueShutdown := []func(){} 224 queueTerminate := []func(){} 225 terminated := make(chan struct{}) 226 227 tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-pause-test-data") 228 assert.NoError(t, err) 229 defer util.RemoveAll(tmpDir) 230 231 queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{ 232 DataDir: tmpDir, 233 BatchLength: 2, 234 QueueLength: 20, 235 Workers: 1, 236 BoostWorkers: 0, 237 MaxWorkers: 10, 238 Name: "first", 239 }, &testData{}) 240 assert.NoError(t, err) 241 242 go func() { 243 queue.Run(func(shutdown func()) { 244 lock.Lock() 245 defer lock.Unlock() 246 queueShutdown = append(queueShutdown, shutdown) 247 }, func(terminate func()) { 248 lock.Lock() 249 defer lock.Unlock() 250 queueTerminate = append(queueTerminate, terminate) 251 }) 252 close(terminated) 253 }() 254 255 // Shutdown and Terminate in defer 256 defer func() { 257 lock.Lock() 258 callbacks := make([]func(), len(queueShutdown)) 259 copy(callbacks, queueShutdown) 260 lock.Unlock() 261 for _, callback := range callbacks { 262 callback() 263 } 264 lock.Lock() 265 log.Info("Finally terminating") 266 callbacks = make([]func(), len(queueTerminate)) 267 copy(callbacks, queueTerminate) 268 lock.Unlock() 269 for _, callback := range callbacks { 270 callback() 271 } 272 }() 273 274 test1 := testData{"A", 1} 275 test2 := testData{"B", 2} 276 277 err = queue.Push(&test1) 278 assert.NoError(t, err) 279 280 pausable, ok := queue.(Pausable) 281 if !assert.True(t, ok) { 282 return 283 } 284 result1 := <-handleChan 285 assert.Equal(t, test1.TestString, result1.TestString) 286 assert.Equal(t, test1.TestInt, result1.TestInt) 287 288 pausable.Pause() 289 paused, _ := pausable.IsPausedIsResumed() 290 291 select { 292 case <-paused: 293 case <-time.After(100 * time.Millisecond): 294 assert.Fail(t, "Queue is not paused") 295 return 296 } 297 298 queue.Push(&test2) 299 300 var result2 *testData 301 select { 302 case result2 = <-handleChan: 303 assert.Fail(t, "handler chan should be empty") 304 case <-time.After(100 * time.Millisecond): 305 } 306 307 assert.Nil(t, result2) 308 309 pausable.Resume() 310 _, resumed := pausable.IsPausedIsResumed() 311 312 select { 313 case <-resumed: 314 case <-time.After(100 * time.Millisecond): 315 assert.Fail(t, "Queue should be resumed") 316 return 317 } 318 319 select { 320 case result2 = <-handleChan: 321 case <-time.After(500 * time.Millisecond): 322 assert.Fail(t, "handler chan should contain test2") 323 } 324 325 assert.Equal(t, test2.TestString, result2.TestString) 326 assert.Equal(t, test2.TestInt, result2.TestInt) 327 328 // Set pushBack to so that the next handle will result in a Pause 329 lock.Lock() 330 pushBack = true 331 lock.Unlock() 332 333 // Ensure that we're still resumed 334 _, resumed = pausable.IsPausedIsResumed() 335 336 select { 337 case <-resumed: 338 case <-time.After(100 * time.Millisecond): 339 assert.Fail(t, "Queue is not resumed") 340 return 341 } 342 343 // push test1 344 queue.Push(&test1) 345 346 // Now as this is handled it should pause 347 paused, _ = pausable.IsPausedIsResumed() 348 349 select { 350 case <-paused: 351 case <-handleChan: 352 assert.Fail(t, "handler chan should not contain test1") 353 return 354 case <-time.After(500 * time.Millisecond): 355 assert.Fail(t, "queue should be paused") 356 return 357 } 358 359 lock.Lock() 360 pushBack = false 361 lock.Unlock() 362 363 pausable.Resume() 364 365 _, resumed = pausable.IsPausedIsResumed() 366 select { 367 case <-resumed: 368 case <-time.After(500 * time.Millisecond): 369 assert.Fail(t, "Queue should be resumed") 370 return 371 } 372 373 select { 374 case result1 = <-handleChan: 375 case <-time.After(500 * time.Millisecond): 376 assert.Fail(t, "handler chan should contain test1") 377 return 378 } 379 assert.Equal(t, test1.TestString, result1.TestString) 380 assert.Equal(t, test1.TestInt, result1.TestInt) 381 382 lock.Lock() 383 callbacks := make([]func(), len(queueShutdown)) 384 copy(callbacks, queueShutdown) 385 queueShutdown = queueShutdown[:0] 386 lock.Unlock() 387 // Now shutdown the queue 388 for _, callback := range callbacks { 389 callback() 390 } 391 392 // Wait til it is closed 393 select { 394 case <-queue.(*PersistableChannelQueue).closed: 395 case <-time.After(5 * time.Second): 396 assert.Fail(t, "queue should close") 397 return 398 } 399 400 err = queue.Push(&test1) 401 assert.NoError(t, err) 402 err = queue.Push(&test2) 403 assert.NoError(t, err) 404 select { 405 case <-handleChan: 406 assert.Fail(t, "Handler processing should have stopped") 407 return 408 default: 409 } 410 411 // terminate the queue 412 lock.Lock() 413 callbacks = make([]func(), len(queueTerminate)) 414 copy(callbacks, queueTerminate) 415 queueShutdown = queueTerminate[:0] 416 lock.Unlock() 417 for _, callback := range callbacks { 418 callback() 419 } 420 421 select { 422 case <-handleChan: 423 assert.Fail(t, "Handler processing should have stopped") 424 return 425 case <-terminated: 426 case <-time.After(10 * time.Second): 427 assert.Fail(t, "Queue should have terminated") 428 return 429 } 430 431 lock.Lock() 432 pushBack = true 433 lock.Unlock() 434 435 // Reopen queue 436 terminated = make(chan struct{}) 437 queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{ 438 DataDir: tmpDir, 439 BatchLength: 1, 440 QueueLength: 20, 441 Workers: 1, 442 BoostWorkers: 0, 443 MaxWorkers: 10, 444 Name: "second", 445 }, &testData{}) 446 assert.NoError(t, err) 447 pausable, ok = queue.(Pausable) 448 if !assert.True(t, ok) { 449 return 450 } 451 452 paused, _ = pausable.IsPausedIsResumed() 453 454 go func() { 455 queue.Run(func(shutdown func()) { 456 lock.Lock() 457 defer lock.Unlock() 458 queueShutdown = append(queueShutdown, shutdown) 459 }, func(terminate func()) { 460 lock.Lock() 461 defer lock.Unlock() 462 queueTerminate = append(queueTerminate, terminate) 463 }) 464 close(terminated) 465 }() 466 467 select { 468 case <-handleChan: 469 assert.Fail(t, "Handler processing should have stopped") 470 return 471 case <-paused: 472 } 473 474 paused, _ = pausable.IsPausedIsResumed() 475 476 select { 477 case <-paused: 478 case <-time.After(500 * time.Millisecond): 479 assert.Fail(t, "Queue is not paused") 480 return 481 } 482 483 select { 484 case <-handleChan: 485 assert.Fail(t, "Handler processing should have stopped") 486 return 487 default: 488 } 489 490 lock.Lock() 491 pushBack = false 492 lock.Unlock() 493 494 pausable.Resume() 495 _, resumed = pausable.IsPausedIsResumed() 496 select { 497 case <-resumed: 498 case <-time.After(500 * time.Millisecond): 499 assert.Fail(t, "Queue should be resumed") 500 return 501 } 502 503 var result3, result4 *testData 504 505 select { 506 case result3 = <-handleChan: 507 case <-time.After(1 * time.Second): 508 assert.Fail(t, "Handler processing should have resumed") 509 return 510 } 511 select { 512 case result4 = <-handleChan: 513 case <-time.After(1 * time.Second): 514 assert.Fail(t, "Handler processing should have resumed") 515 return 516 } 517 if result4.TestString == test1.TestString { 518 result3, result4 = result4, result3 519 } 520 assert.Equal(t, test1.TestString, result3.TestString) 521 assert.Equal(t, test1.TestInt, result3.TestInt) 522 523 assert.Equal(t, test2.TestString, result4.TestString) 524 assert.Equal(t, test2.TestInt, result4.TestInt) 525 526 lock.Lock() 527 callbacks = make([]func(), len(queueShutdown)) 528 copy(callbacks, queueShutdown) 529 queueShutdown = queueShutdown[:0] 530 lock.Unlock() 531 // Now shutdown the queue 532 for _, callback := range callbacks { 533 callback() 534 } 535 536 // terminate the queue 537 lock.Lock() 538 callbacks = make([]func(), len(queueTerminate)) 539 copy(callbacks, queueTerminate) 540 queueShutdown = queueTerminate[:0] 541 lock.Unlock() 542 for _, callback := range callbacks { 543 callback() 544 } 545 546 select { 547 case <-time.After(10 * time.Second): 548 assert.Fail(t, "Queue should have terminated") 549 return 550 case <-terminated: 551 } 552 }