github.com/maier/nomad@v0.4.1-0.20161110003312-a9e3d0b8549d/nomad/eval_broker_test.go (about) 1 package nomad 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/hashicorp/nomad/nomad/mock" 8 "github.com/hashicorp/nomad/nomad/structs" 9 ) 10 11 var ( 12 defaultSched = []string{ 13 structs.JobTypeService, 14 structs.JobTypeBatch, 15 } 16 ) 17 18 func testBroker(t *testing.T, timeout time.Duration) *EvalBroker { 19 if timeout == 0 { 20 timeout = 5 * time.Second 21 } 22 b, err := NewEvalBroker(timeout, 3) 23 if err != nil { 24 t.Fatalf("err: %v", err) 25 } 26 return b 27 } 28 29 func TestEvalBroker_Enqueue_Dequeue_Nack_Ack(t *testing.T) { 30 b := testBroker(t, 0) 31 32 // Enqueue, but broker is disabled! 33 eval := mock.Eval() 34 b.Enqueue(eval) 35 36 // Verify nothing was done 37 stats := b.Stats() 38 if stats.TotalReady != 0 { 39 t.Fatalf("bad: %#v", stats) 40 } 41 42 if b.Enabled() { 43 t.Fatalf("should not be enabled") 44 } 45 46 // Enable the broker, and enqueue 47 b.SetEnabled(true) 48 b.Enqueue(eval) 49 50 // Double enqueue is a no-op 51 b.Enqueue(eval) 52 53 if !b.Enabled() { 54 t.Fatalf("should be enabled") 55 } 56 57 // Verify enqueue is done 58 stats = b.Stats() 59 if stats.TotalReady != 1 { 60 t.Fatalf("bad: %#v", stats) 61 } 62 if stats.ByScheduler[eval.Type].Ready != 1 { 63 t.Fatalf("bad: %#v", stats) 64 } 65 66 // Dequeue should work 67 out, token, err := b.Dequeue(defaultSched, time.Second) 68 if err != nil { 69 t.Fatalf("err: %v", err) 70 } 71 if out != eval { 72 t.Fatalf("bad : %#v", out) 73 } 74 75 tokenOut, ok := b.Outstanding(out.ID) 76 if !ok { 77 t.Fatalf("should be outstanding") 78 } 79 if tokenOut != token { 80 t.Fatalf("Bad: %#v %#v", token, tokenOut) 81 } 82 83 // OutstandingReset should verify the token 84 err = b.OutstandingReset("nope", "foo") 85 if err != ErrNotOutstanding { 86 t.Fatalf("err: %v", err) 87 } 88 err = b.OutstandingReset(out.ID, "foo") 89 if err != ErrTokenMismatch { 90 t.Fatalf("err: %v", err) 91 } 92 err = b.OutstandingReset(out.ID, tokenOut) 93 if err != nil { 94 t.Fatalf("err: %v", err) 95 } 96 97 // Check the stats 98 stats = b.Stats() 99 if stats.TotalReady != 0 { 100 t.Fatalf("bad: %#v", stats) 101 } 102 if stats.TotalUnacked != 1 { 103 t.Fatalf("bad: %#v", stats) 104 } 105 if stats.ByScheduler[eval.Type].Ready != 0 { 106 t.Fatalf("bad: %#v", stats) 107 } 108 if stats.ByScheduler[eval.Type].Unacked != 1 { 109 t.Fatalf("bad: %#v", stats) 110 } 111 112 // Nack with wrong token should fail 113 err = b.Nack(eval.ID, "foobarbaz") 114 if err == nil { 115 t.Fatalf("should fail to nack") 116 } 117 118 // Nack back into the queue 119 err = b.Nack(eval.ID, token) 120 if err != nil { 121 t.Fatalf("err: %v", err) 122 } 123 124 if _, ok := b.Outstanding(out.ID); ok { 125 t.Fatalf("should not be outstanding") 126 } 127 128 // Check the stats 129 stats = b.Stats() 130 if stats.TotalReady != 1 { 131 t.Fatalf("bad: %#v", stats) 132 } 133 if stats.TotalUnacked != 0 { 134 t.Fatalf("bad: %#v", stats) 135 } 136 if stats.ByScheduler[eval.Type].Ready != 1 { 137 t.Fatalf("bad: %#v", stats) 138 } 139 if stats.ByScheduler[eval.Type].Unacked != 0 { 140 t.Fatalf("bad: %#v", stats) 141 } 142 143 // Dequeue should work again 144 out2, token2, err := b.Dequeue(defaultSched, time.Second) 145 if err != nil { 146 t.Fatalf("err: %v", err) 147 } 148 if out2 != eval { 149 t.Fatalf("bad : %#v", out2) 150 } 151 if token2 == token { 152 t.Fatalf("should get a new token") 153 } 154 155 tokenOut2, ok := b.Outstanding(out.ID) 156 if !ok { 157 t.Fatalf("should be outstanding") 158 } 159 if tokenOut2 != token2 { 160 t.Fatalf("Bad: %#v %#v", token2, tokenOut2) 161 } 162 163 // Ack with wrong token 164 err = b.Ack(eval.ID, "zip") 165 if err == nil { 166 t.Fatalf("should fail to ack") 167 } 168 169 // Ack finally 170 err = b.Ack(eval.ID, token2) 171 if err != nil { 172 t.Fatalf("err: %v", err) 173 } 174 175 if _, ok := b.Outstanding(out.ID); ok { 176 t.Fatalf("should not be outstanding") 177 } 178 179 // Check the stats 180 stats = b.Stats() 181 if stats.TotalReady != 0 { 182 t.Fatalf("bad: %#v", stats) 183 } 184 if stats.TotalUnacked != 0 { 185 t.Fatalf("bad: %#v", stats) 186 } 187 if stats.ByScheduler[eval.Type].Ready != 0 { 188 t.Fatalf("bad: %#v", stats) 189 } 190 if stats.ByScheduler[eval.Type].Unacked != 0 { 191 t.Fatalf("bad: %#v", stats) 192 } 193 } 194 195 func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) { 196 b := testBroker(t, 0) 197 b.SetEnabled(true) 198 199 eval := mock.Eval() 200 b.Enqueue(eval) 201 202 eval2 := mock.Eval() 203 eval2.JobID = eval.JobID 204 eval2.CreateIndex = eval.CreateIndex + 1 205 b.Enqueue(eval2) 206 207 eval3 := mock.Eval() 208 eval3.JobID = eval.JobID 209 eval3.CreateIndex = eval.CreateIndex + 2 210 b.Enqueue(eval3) 211 212 stats := b.Stats() 213 if stats.TotalReady != 1 { 214 t.Fatalf("bad: %#v", stats) 215 } 216 if stats.TotalBlocked != 2 { 217 t.Fatalf("bad: %#v", stats) 218 } 219 220 // Dequeue should work 221 out, token, err := b.Dequeue(defaultSched, time.Second) 222 if err != nil { 223 t.Fatalf("err: %v", err) 224 } 225 if out != eval { 226 t.Fatalf("bad : %#v", out) 227 } 228 229 // Check the stats 230 stats = b.Stats() 231 if stats.TotalReady != 0 { 232 t.Fatalf("bad: %#v", stats) 233 } 234 if stats.TotalUnacked != 1 { 235 t.Fatalf("bad: %#v", stats) 236 } 237 if stats.TotalBlocked != 2 { 238 t.Fatalf("bad: %#v", stats) 239 } 240 241 // Ack out 242 err = b.Ack(eval.ID, token) 243 if err != nil { 244 t.Fatalf("err: %v", err) 245 } 246 247 // Check the stats 248 stats = b.Stats() 249 if stats.TotalReady != 1 { 250 t.Fatalf("bad: %#v", stats) 251 } 252 if stats.TotalUnacked != 0 { 253 t.Fatalf("bad: %#v", stats) 254 } 255 if stats.TotalBlocked != 1 { 256 t.Fatalf("bad: %#v", stats) 257 } 258 259 // Dequeue should work 260 out, token, err = b.Dequeue(defaultSched, time.Second) 261 if err != nil { 262 t.Fatalf("err: %v", err) 263 } 264 if out != eval2 { 265 t.Fatalf("bad : %#v", out) 266 } 267 268 // Check the stats 269 stats = b.Stats() 270 if stats.TotalReady != 0 { 271 t.Fatalf("bad: %#v", stats) 272 } 273 if stats.TotalUnacked != 1 { 274 t.Fatalf("bad: %#v", stats) 275 } 276 if stats.TotalBlocked != 1 { 277 t.Fatalf("bad: %#v", stats) 278 } 279 280 // Ack out 281 err = b.Ack(eval2.ID, token) 282 if err != nil { 283 t.Fatalf("err: %v", err) 284 } 285 286 // Check the stats 287 stats = b.Stats() 288 if stats.TotalReady != 1 { 289 t.Fatalf("bad: %#v", stats) 290 } 291 if stats.TotalUnacked != 0 { 292 t.Fatalf("bad: %#v", stats) 293 } 294 if stats.TotalBlocked != 0 { 295 t.Fatalf("bad: %#v", stats) 296 } 297 298 // Dequeue should work 299 out, token, err = b.Dequeue(defaultSched, time.Second) 300 if err != nil { 301 t.Fatalf("err: %v", err) 302 } 303 if out != eval3 { 304 t.Fatalf("bad : %#v", out) 305 } 306 307 // Check the stats 308 stats = b.Stats() 309 if stats.TotalReady != 0 { 310 t.Fatalf("bad: %#v", stats) 311 } 312 if stats.TotalUnacked != 1 { 313 t.Fatalf("bad: %#v", stats) 314 } 315 if stats.TotalBlocked != 0 { 316 t.Fatalf("bad: %#v", stats) 317 } 318 319 // Ack out 320 err = b.Ack(eval3.ID, token) 321 if err != nil { 322 t.Fatalf("err: %v", err) 323 } 324 325 // Check the stats 326 stats = b.Stats() 327 if stats.TotalReady != 0 { 328 t.Fatalf("bad: %#v", stats) 329 } 330 if stats.TotalUnacked != 0 { 331 t.Fatalf("bad: %#v", stats) 332 } 333 if stats.TotalBlocked != 0 { 334 t.Fatalf("bad: %#v", stats) 335 } 336 } 337 338 func TestEvalBroker_Enqueue_Disable(t *testing.T) { 339 b := testBroker(t, 0) 340 341 // Enqueue 342 eval := mock.Eval() 343 b.SetEnabled(true) 344 b.Enqueue(eval) 345 346 // Flush via SetEnabled 347 b.SetEnabled(false) 348 349 // Check the stats 350 stats := b.Stats() 351 if stats.TotalReady != 0 { 352 t.Fatalf("bad: %#v", stats) 353 } 354 if stats.TotalUnacked != 0 { 355 t.Fatalf("bad: %#v", stats) 356 } 357 if _, ok := stats.ByScheduler[eval.Type]; ok { 358 t.Fatalf("bad: %#v", stats) 359 } 360 } 361 362 func TestEvalBroker_Dequeue_Timeout(t *testing.T) { 363 b := testBroker(t, 0) 364 b.SetEnabled(true) 365 366 start := time.Now() 367 out, _, err := b.Dequeue(defaultSched, 5*time.Millisecond) 368 end := time.Now() 369 370 if err != nil { 371 t.Fatalf("err: %v", err) 372 } 373 if out != nil { 374 t.Fatalf("unexpected: %#v", out) 375 } 376 377 if diff := end.Sub(start); diff < 5*time.Millisecond { 378 t.Fatalf("bad: %#v", diff) 379 } 380 } 381 382 func TestEvalBroker_Dequeue_Empty_Timeout(t *testing.T) { 383 b := testBroker(t, 0) 384 b.SetEnabled(true) 385 doneCh := make(chan struct{}, 1) 386 387 go func() { 388 out, _, err := b.Dequeue(defaultSched, 0) 389 if err != nil { 390 t.Fatalf("err: %v", err) 391 } 392 if out == nil { 393 t.Fatal("Expect an eval") 394 } 395 doneCh <- struct{}{} 396 }() 397 398 // Sleep for a little bit 399 select { 400 case <-time.After(5 * time.Millisecond): 401 case <-doneCh: 402 t.Fatalf("Dequeue(0) should block") 403 } 404 405 // Enqueue to unblock the dequeue. 406 eval := mock.Eval() 407 b.Enqueue(eval) 408 409 select { 410 case <-doneCh: 411 return 412 case <-time.After(5 * time.Millisecond): 413 t.Fatal("timeout: Dequeue(0) should return after enqueue") 414 } 415 } 416 417 // Ensure higher priority dequeued first 418 func TestEvalBroker_Dequeue_Priority(t *testing.T) { 419 b := testBroker(t, 0) 420 b.SetEnabled(true) 421 422 eval1 := mock.Eval() 423 eval1.Priority = 10 424 b.Enqueue(eval1) 425 426 eval2 := mock.Eval() 427 eval2.Priority = 30 428 b.Enqueue(eval2) 429 430 eval3 := mock.Eval() 431 eval3.Priority = 20 432 b.Enqueue(eval3) 433 434 out1, _, _ := b.Dequeue(defaultSched, time.Second) 435 if out1 != eval2 { 436 t.Fatalf("bad: %#v", out1) 437 } 438 439 out2, _, _ := b.Dequeue(defaultSched, time.Second) 440 if out2 != eval3 { 441 t.Fatalf("bad: %#v", out2) 442 } 443 444 out3, _, _ := b.Dequeue(defaultSched, time.Second) 445 if out3 != eval1 { 446 t.Fatalf("bad: %#v", out3) 447 } 448 } 449 450 // Ensure FIFO at fixed priority 451 func TestEvalBroker_Dequeue_FIFO(t *testing.T) { 452 b := testBroker(t, 0) 453 b.SetEnabled(true) 454 NUM := 100 455 456 for i := 0; i < NUM; i++ { 457 eval1 := mock.Eval() 458 eval1.CreateIndex = uint64(i) 459 eval1.ModifyIndex = uint64(i) 460 b.Enqueue(eval1) 461 } 462 463 for i := 0; i < NUM; i++ { 464 out1, _, _ := b.Dequeue(defaultSched, time.Second) 465 if out1.CreateIndex != uint64(i) { 466 t.Fatalf("bad: %d %#v", i, out1) 467 } 468 } 469 } 470 471 // Ensure fairness between schedulers 472 func TestEvalBroker_Dequeue_Fairness(t *testing.T) { 473 b := testBroker(t, 0) 474 b.SetEnabled(true) 475 NUM := 100 476 477 for i := 0; i < NUM; i++ { 478 eval1 := mock.Eval() 479 if i < (NUM / 2) { 480 eval1.Type = structs.JobTypeService 481 } else { 482 eval1.Type = structs.JobTypeBatch 483 } 484 b.Enqueue(eval1) 485 } 486 487 counter := 0 488 for i := 0; i < NUM; i++ { 489 out1, _, _ := b.Dequeue(defaultSched, time.Second) 490 491 switch out1.Type { 492 case structs.JobTypeService: 493 if counter < 0 { 494 counter = 0 495 } 496 counter += 1 497 case structs.JobTypeBatch: 498 if counter > 0 { 499 counter = 0 500 } 501 counter -= 1 502 } 503 504 // This will fail randomly at times. It is very hard to 505 // test deterministically that its acting randomly. 506 if counter >= 25 || counter <= -25 { 507 t.Fatalf("unlikely sequence: %d", counter) 508 } 509 } 510 } 511 512 // Ensure we get unblocked 513 func TestEvalBroker_Dequeue_Blocked(t *testing.T) { 514 b := testBroker(t, 0) 515 b.SetEnabled(true) 516 517 // Start with a blocked dequeue 518 outCh := make(chan *structs.Evaluation, 1) 519 go func() { 520 start := time.Now() 521 out, _, err := b.Dequeue(defaultSched, time.Second) 522 end := time.Now() 523 outCh <- out 524 if err != nil { 525 t.Fatalf("err: %v", err) 526 } 527 if d := end.Sub(start); d < 5*time.Millisecond { 528 t.Fatalf("bad: %v", d) 529 } 530 }() 531 532 // Wait for a bit 533 time.Sleep(5 * time.Millisecond) 534 535 // Enqueue 536 eval := mock.Eval() 537 b.Enqueue(eval) 538 539 // Ensure dequeue 540 select { 541 case out := <-outCh: 542 if out != eval { 543 t.Fatalf("bad: %v", out) 544 } 545 case <-time.After(time.Second): 546 t.Fatalf("timeout") 547 } 548 } 549 550 // Ensure we nack in a timely manner 551 func TestEvalBroker_Nack_Timeout(t *testing.T) { 552 b := testBroker(t, 5*time.Millisecond) 553 b.SetEnabled(true) 554 555 // Enqueue 556 eval := mock.Eval() 557 b.Enqueue(eval) 558 559 // Dequeue 560 out, _, err := b.Dequeue(defaultSched, time.Second) 561 start := time.Now() 562 if err != nil { 563 t.Fatalf("err: %v", err) 564 } 565 if out != eval { 566 t.Fatalf("bad: %v", out) 567 } 568 569 // Dequeue, should block on Nack timer 570 out, _, err = b.Dequeue(defaultSched, time.Second) 571 end := time.Now() 572 if err != nil { 573 t.Fatalf("err: %v", err) 574 } 575 if out != eval { 576 t.Fatalf("bad: %v", out) 577 } 578 579 // Check the nack timer 580 if diff := end.Sub(start); diff < 5*time.Millisecond { 581 t.Fatalf("bad: %#v", diff) 582 } 583 } 584 585 // Ensure we nack in a timely manner 586 func TestEvalBroker_Nack_TimeoutReset(t *testing.T) { 587 b := testBroker(t, 5*time.Millisecond) 588 b.SetEnabled(true) 589 590 // Enqueue 591 eval := mock.Eval() 592 b.Enqueue(eval) 593 594 // Dequeue 595 out, token, err := b.Dequeue(defaultSched, time.Second) 596 start := time.Now() 597 if err != nil { 598 t.Fatalf("err: %v", err) 599 } 600 if out != eval { 601 t.Fatalf("bad: %v", out) 602 } 603 604 // Reset in 2 milliseconds 605 time.Sleep(2 * time.Millisecond) 606 if err := b.OutstandingReset(out.ID, token); err != nil { 607 t.Fatalf("err: %v", err) 608 } 609 610 // Dequeue, should block on Nack timer 611 out, _, err = b.Dequeue(defaultSched, time.Second) 612 end := time.Now() 613 if err != nil { 614 t.Fatalf("err: %v", err) 615 } 616 if out != eval { 617 t.Fatalf("bad: %v", out) 618 } 619 620 // Check the nack timer 621 if diff := end.Sub(start); diff < 7*time.Millisecond { 622 t.Fatalf("bad: %#v", diff) 623 } 624 } 625 626 func TestEvalBroker_PauseResumeNackTimeout(t *testing.T) { 627 b := testBroker(t, 5*time.Millisecond) 628 b.SetEnabled(true) 629 630 // Enqueue 631 eval := mock.Eval() 632 b.Enqueue(eval) 633 634 // Dequeue 635 out, token, err := b.Dequeue(defaultSched, time.Second) 636 start := time.Now() 637 if err != nil { 638 t.Fatalf("err: %v", err) 639 } 640 if out != eval { 641 t.Fatalf("bad: %v", out) 642 } 643 644 // Pause in 2 milliseconds 645 time.Sleep(2 * time.Millisecond) 646 if err := b.PauseNackTimeout(out.ID, token); err != nil { 647 t.Fatalf("err: %v", err) 648 } 649 650 go func() { 651 time.Sleep(2 * time.Millisecond) 652 if err := b.ResumeNackTimeout(out.ID, token); err != nil { 653 t.Fatalf("err: %v", err) 654 } 655 }() 656 657 // Dequeue, should block until the timer is resumed 658 out, _, err = b.Dequeue(defaultSched, time.Second) 659 end := time.Now() 660 if err != nil { 661 t.Fatalf("err: %v", err) 662 } 663 if out != eval { 664 t.Fatalf("bad: %v", out) 665 } 666 667 // Check the nack timer 668 if diff := end.Sub(start); diff < 9*time.Millisecond { 669 t.Fatalf("bad: %#v", diff) 670 } 671 } 672 673 func TestEvalBroker_DeliveryLimit(t *testing.T) { 674 b := testBroker(t, 0) 675 b.SetEnabled(true) 676 677 eval := mock.Eval() 678 b.Enqueue(eval) 679 680 for i := 0; i < 3; i++ { 681 // Dequeue should work 682 out, token, err := b.Dequeue(defaultSched, time.Second) 683 if err != nil { 684 t.Fatalf("err: %v", err) 685 } 686 if out != eval { 687 t.Fatalf("bad : %#v", out) 688 } 689 690 // Nack with wrong token should fail 691 err = b.Nack(eval.ID, token) 692 if err != nil { 693 t.Fatalf("err: %v", err) 694 } 695 } 696 697 // Check the stats 698 stats := b.Stats() 699 if stats.TotalReady != 1 { 700 t.Fatalf("bad: %#v", stats) 701 } 702 if stats.TotalUnacked != 0 { 703 t.Fatalf("bad: %#v", stats) 704 } 705 if stats.ByScheduler[failedQueue].Ready != 1 { 706 t.Fatalf("bad: %#v", stats) 707 } 708 if stats.ByScheduler[failedQueue].Unacked != 0 { 709 t.Fatalf("bad: %#v", stats) 710 } 711 712 // Dequeue from failed queue 713 out, token, err := b.Dequeue([]string{failedQueue}, time.Second) 714 if err != nil { 715 t.Fatalf("err: %v", err) 716 } 717 if out != eval { 718 t.Fatalf("bad : %#v", out) 719 } 720 721 // Check the stats 722 stats = b.Stats() 723 if stats.TotalReady != 0 { 724 t.Fatalf("bad: %#v", stats) 725 } 726 if stats.TotalUnacked != 1 { 727 t.Fatalf("bad: %#v", stats) 728 } 729 if stats.ByScheduler[failedQueue].Ready != 0 { 730 t.Fatalf("bad: %#v", stats) 731 } 732 if stats.ByScheduler[failedQueue].Unacked != 1 { 733 t.Fatalf("bad: %#v", stats) 734 } 735 736 // Ack finally 737 err = b.Ack(out.ID, token) 738 if err != nil { 739 t.Fatalf("err: %v", err) 740 } 741 742 if _, ok := b.Outstanding(out.ID); ok { 743 t.Fatalf("should not be outstanding") 744 } 745 746 // Check the stats 747 stats = b.Stats() 748 if stats.TotalReady != 0 { 749 t.Fatalf("bad: %#v", stats) 750 } 751 if stats.TotalUnacked != 0 { 752 t.Fatalf("bad: %#v", stats) 753 } 754 if stats.ByScheduler[failedQueue].Ready != 0 { 755 t.Fatalf("bad: %#v", stats.ByScheduler[failedQueue]) 756 } 757 if stats.ByScheduler[failedQueue].Unacked != 0 { 758 t.Fatalf("bad: %#v", stats.ByScheduler[failedQueue]) 759 } 760 } 761 762 func TestEvalBroker_AckAtDeliveryLimit(t *testing.T) { 763 b := testBroker(t, 0) 764 b.SetEnabled(true) 765 766 eval := mock.Eval() 767 b.Enqueue(eval) 768 769 for i := 0; i < 3; i++ { 770 // Dequeue should work 771 out, token, err := b.Dequeue(defaultSched, time.Second) 772 if err != nil { 773 t.Fatalf("err: %v", err) 774 } 775 if out != eval { 776 t.Fatalf("bad : %#v", out) 777 } 778 779 if i == 2 { 780 b.Ack(eval.ID, token) 781 } else { 782 // Nack with wrong token should fail 783 err = b.Nack(eval.ID, token) 784 if err != nil { 785 t.Fatalf("err: %v", err) 786 } 787 } 788 } 789 790 // Check the stats 791 stats := b.Stats() 792 if stats.TotalReady != 0 { 793 t.Fatalf("bad: %#v", stats) 794 } 795 if stats.TotalUnacked != 0 { 796 t.Fatalf("bad: %#v", stats) 797 } 798 if _, ok := stats.ByScheduler[failedQueue]; ok { 799 t.Fatalf("bad: %#v", stats) 800 } 801 } 802 803 // Ensure fairness between schedulers 804 func TestEvalBroker_Wait(t *testing.T) { 805 b := testBroker(t, 0) 806 b.SetEnabled(true) 807 808 // Create an eval that should wait 809 eval := mock.Eval() 810 eval.Wait = 10 * time.Millisecond 811 b.Enqueue(eval) 812 813 // Verify waiting 814 stats := b.Stats() 815 if stats.TotalReady != 0 { 816 t.Fatalf("bad: %#v", stats) 817 } 818 if stats.TotalWaiting != 1 { 819 t.Fatalf("bad: %#v", stats) 820 } 821 822 // Let the wait elapse 823 time.Sleep(15 * time.Millisecond) 824 825 // Verify ready 826 stats = b.Stats() 827 if stats.TotalReady != 1 { 828 t.Fatalf("bad: %#v", stats) 829 } 830 if stats.TotalWaiting != 0 { 831 t.Fatalf("bad: %#v", stats) 832 } 833 834 // Dequeue should work 835 out, _, err := b.Dequeue(defaultSched, time.Second) 836 if err != nil { 837 t.Fatalf("err: %v", err) 838 } 839 if out != eval { 840 t.Fatalf("bad : %#v", out) 841 } 842 } 843 844 // Ensure that priority is taken into account when enqueueing many evaluations. 845 func TestEvalBroker_EnqueueAll_Dequeue_Fair(t *testing.T) { 846 b := testBroker(t, 0) 847 b.SetEnabled(true) 848 849 // Start with a blocked dequeue 850 outCh := make(chan *structs.Evaluation, 1) 851 go func() { 852 start := time.Now() 853 out, _, err := b.Dequeue(defaultSched, time.Second) 854 end := time.Now() 855 outCh <- out 856 if err != nil { 857 t.Fatalf("err: %v", err) 858 } 859 if d := end.Sub(start); d < 5*time.Millisecond { 860 t.Fatalf("bad: %v", d) 861 } 862 }() 863 864 // Wait for a bit 865 time.Sleep(5 * time.Millisecond) 866 867 // Enqueue 868 evals := make(map[*structs.Evaluation]string, 8) 869 expectedPriority := 90 870 for i := 10; i <= expectedPriority; i += 10 { 871 eval := mock.Eval() 872 eval.Priority = i 873 evals[eval] = "" 874 875 } 876 b.EnqueueAll(evals) 877 878 // Ensure dequeue 879 select { 880 case out := <-outCh: 881 if out.Priority != expectedPriority { 882 t.Fatalf("bad: %v", out) 883 } 884 case <-time.After(time.Second): 885 t.Fatalf("timeout") 886 } 887 } 888 889 func TestEvalBroker_EnqueueAll_Requeue_Ack(t *testing.T) { 890 b := testBroker(t, 0) 891 b.SetEnabled(true) 892 893 // Create the evaluation, enqueue and dequeue 894 eval := mock.Eval() 895 b.Enqueue(eval) 896 897 out, token, err := b.Dequeue(defaultSched, time.Second) 898 if err != nil { 899 t.Fatalf("err: %v", err) 900 } 901 if out != eval { 902 t.Fatalf("bad : %#v", out) 903 } 904 905 // Requeue the same evaluation. 906 b.EnqueueAll(map[*structs.Evaluation]string{eval: token}) 907 908 // The stats should show one unacked 909 stats := b.Stats() 910 if stats.TotalReady != 0 { 911 t.Fatalf("bad: %#v", stats) 912 } 913 if stats.TotalUnacked != 1 { 914 t.Fatalf("bad: %#v", stats) 915 } 916 917 // Ack the evaluation. 918 if err := b.Ack(eval.ID, token); err != nil { 919 t.Fatalf("err: %v", err) 920 } 921 922 // Check stats again as this should cause the re-enqueued one to transition 923 // into the ready state 924 stats = b.Stats() 925 if stats.TotalReady != 1 { 926 t.Fatalf("bad: %#v", stats) 927 } 928 if stats.TotalUnacked != 0 { 929 t.Fatalf("bad: %#v", stats) 930 } 931 932 // Another dequeue should be successful 933 out2, token2, err := b.Dequeue(defaultSched, time.Second) 934 if err != nil { 935 t.Fatalf("err: %v", err) 936 } 937 if out2 != eval { 938 t.Fatalf("bad : %#v", out) 939 } 940 if token == token2 { 941 t.Fatalf("bad : %s and %s", token, token2) 942 } 943 } 944 945 func TestEvalBroker_EnqueueAll_Requeue_Nack(t *testing.T) { 946 b := testBroker(t, 0) 947 b.SetEnabled(true) 948 949 // Create the evaluation, enqueue and dequeue 950 eval := mock.Eval() 951 b.Enqueue(eval) 952 953 out, token, err := b.Dequeue(defaultSched, time.Second) 954 if err != nil { 955 t.Fatalf("err: %v", err) 956 } 957 if out != eval { 958 t.Fatalf("bad : %#v", out) 959 } 960 961 // Requeue the same evaluation. 962 b.EnqueueAll(map[*structs.Evaluation]string{eval: token}) 963 964 // The stats should show one unacked 965 stats := b.Stats() 966 if stats.TotalReady != 0 { 967 t.Fatalf("bad: %#v", stats) 968 } 969 if stats.TotalUnacked != 1 { 970 t.Fatalf("bad: %#v", stats) 971 } 972 973 // Nack the evaluation. 974 if err := b.Nack(eval.ID, token); err != nil { 975 t.Fatalf("err: %v", err) 976 } 977 978 // Check stats again as this should cause the re-enqueued one to be dropped 979 stats = b.Stats() 980 if stats.TotalReady != 1 { 981 t.Fatalf("bad: %#v", stats) 982 } 983 if stats.TotalUnacked != 0 { 984 t.Fatalf("bad: %#v", stats) 985 } 986 if len(b.requeue) != 0 { 987 t.Fatalf("bad: %#v", b.requeue) 988 } 989 }