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  }