github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/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  	err := b.Enqueue(eval)
    35  	if err != nil {
    36  		t.Fatalf("err: %v", err)
    37  	}
    38  
    39  	// Verify nothing was done
    40  	stats := b.Stats()
    41  	if stats.TotalReady != 0 {
    42  		t.Fatalf("bad: %#v", stats)
    43  	}
    44  
    45  	if b.Enabled() {
    46  		t.Fatalf("should not be enabled")
    47  	}
    48  
    49  	// Enable the broker, and enqueue
    50  	b.SetEnabled(true)
    51  	err = b.Enqueue(eval)
    52  	if err != nil {
    53  		t.Fatalf("err: %v", err)
    54  	}
    55  
    56  	// Double enqueue is a no-op
    57  	err = b.Enqueue(eval)
    58  	if err != nil {
    59  		t.Fatalf("err: %v", err)
    60  	}
    61  
    62  	if !b.Enabled() {
    63  		t.Fatalf("should be enabled")
    64  	}
    65  
    66  	// Verify enqueue is done
    67  	stats = b.Stats()
    68  	if stats.TotalReady != 1 {
    69  		t.Fatalf("bad: %#v", stats)
    70  	}
    71  	if stats.ByScheduler[eval.Type].Ready != 1 {
    72  		t.Fatalf("bad: %#v", stats)
    73  	}
    74  
    75  	// Dequeue should work
    76  	out, token, err := b.Dequeue(defaultSched, time.Second)
    77  	if err != nil {
    78  		t.Fatalf("err: %v", err)
    79  	}
    80  	if out != eval {
    81  		t.Fatalf("bad : %#v", out)
    82  	}
    83  
    84  	tokenOut, ok := b.Outstanding(out.ID)
    85  	if !ok {
    86  		t.Fatalf("should be outstanding")
    87  	}
    88  	if tokenOut != token {
    89  		t.Fatalf("Bad: %#v %#v", token, tokenOut)
    90  	}
    91  
    92  	// OutstandingReset should verify the token
    93  	err = b.OutstandingReset("nope", "foo")
    94  	if err != ErrNotOutstanding {
    95  		t.Fatalf("err: %v", err)
    96  	}
    97  	err = b.OutstandingReset(out.ID, "foo")
    98  	if err != ErrTokenMismatch {
    99  		t.Fatalf("err: %v", err)
   100  	}
   101  	err = b.OutstandingReset(out.ID, tokenOut)
   102  	if err != nil {
   103  		t.Fatalf("err: %v", err)
   104  	}
   105  
   106  	// Check the stats
   107  	stats = b.Stats()
   108  	if stats.TotalReady != 0 {
   109  		t.Fatalf("bad: %#v", stats)
   110  	}
   111  	if stats.TotalUnacked != 1 {
   112  		t.Fatalf("bad: %#v", stats)
   113  	}
   114  	if stats.ByScheduler[eval.Type].Ready != 0 {
   115  		t.Fatalf("bad: %#v", stats)
   116  	}
   117  	if stats.ByScheduler[eval.Type].Unacked != 1 {
   118  		t.Fatalf("bad: %#v", stats)
   119  	}
   120  
   121  	// Nack with wrong token should fail
   122  	err = b.Nack(eval.ID, "foobarbaz")
   123  	if err == nil {
   124  		t.Fatalf("should fail to nack")
   125  	}
   126  
   127  	// Nack back into the queue
   128  	err = b.Nack(eval.ID, token)
   129  	if err != nil {
   130  		t.Fatalf("err: %v", err)
   131  	}
   132  
   133  	if _, ok := b.Outstanding(out.ID); ok {
   134  		t.Fatalf("should not be outstanding")
   135  	}
   136  
   137  	// Check the stats
   138  	stats = b.Stats()
   139  	if stats.TotalReady != 1 {
   140  		t.Fatalf("bad: %#v", stats)
   141  	}
   142  	if stats.TotalUnacked != 0 {
   143  		t.Fatalf("bad: %#v", stats)
   144  	}
   145  	if stats.ByScheduler[eval.Type].Ready != 1 {
   146  		t.Fatalf("bad: %#v", stats)
   147  	}
   148  	if stats.ByScheduler[eval.Type].Unacked != 0 {
   149  		t.Fatalf("bad: %#v", stats)
   150  	}
   151  
   152  	// Dequeue should work again
   153  	out2, token2, err := b.Dequeue(defaultSched, time.Second)
   154  	if err != nil {
   155  		t.Fatalf("err: %v", err)
   156  	}
   157  	if out2 != eval {
   158  		t.Fatalf("bad : %#v", out2)
   159  	}
   160  	if token2 == token {
   161  		t.Fatalf("should get a new token")
   162  	}
   163  
   164  	tokenOut2, ok := b.Outstanding(out.ID)
   165  	if !ok {
   166  		t.Fatalf("should be outstanding")
   167  	}
   168  	if tokenOut2 != token2 {
   169  		t.Fatalf("Bad: %#v %#v", token2, tokenOut2)
   170  	}
   171  
   172  	// Ack with wrong token
   173  	err = b.Ack(eval.ID, "zip")
   174  	if err == nil {
   175  		t.Fatalf("should fail to ack")
   176  	}
   177  
   178  	// Ack finally
   179  	err = b.Ack(eval.ID, token2)
   180  	if err != nil {
   181  		t.Fatalf("err: %v", err)
   182  	}
   183  
   184  	if _, ok := b.Outstanding(out.ID); ok {
   185  		t.Fatalf("should not be outstanding")
   186  	}
   187  
   188  	// Check the stats
   189  	stats = b.Stats()
   190  	if stats.TotalReady != 0 {
   191  		t.Fatalf("bad: %#v", stats)
   192  	}
   193  	if stats.TotalUnacked != 0 {
   194  		t.Fatalf("bad: %#v", stats)
   195  	}
   196  	if stats.ByScheduler[eval.Type].Ready != 0 {
   197  		t.Fatalf("bad: %#v", stats)
   198  	}
   199  	if stats.ByScheduler[eval.Type].Unacked != 0 {
   200  		t.Fatalf("bad: %#v", stats)
   201  	}
   202  }
   203  
   204  func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
   205  	b := testBroker(t, 0)
   206  	b.SetEnabled(true)
   207  
   208  	eval := mock.Eval()
   209  	err := b.Enqueue(eval)
   210  	if err != nil {
   211  		t.Fatalf("err: %v", err)
   212  	}
   213  
   214  	eval2 := mock.Eval()
   215  	eval2.JobID = eval.JobID
   216  	eval2.CreateIndex = eval.CreateIndex + 1
   217  	err = b.Enqueue(eval2)
   218  	if err != nil {
   219  		t.Fatalf("err: %v", err)
   220  	}
   221  
   222  	eval3 := mock.Eval()
   223  	eval3.JobID = eval.JobID
   224  	eval3.CreateIndex = eval.CreateIndex + 2
   225  	err = b.Enqueue(eval3)
   226  	if err != nil {
   227  		t.Fatalf("err: %v", err)
   228  	}
   229  
   230  	stats := b.Stats()
   231  	if stats.TotalReady != 1 {
   232  		t.Fatalf("bad: %#v", stats)
   233  	}
   234  	if stats.TotalBlocked != 2 {
   235  		t.Fatalf("bad: %#v", stats)
   236  	}
   237  
   238  	// Dequeue should work
   239  	out, token, err := b.Dequeue(defaultSched, time.Second)
   240  	if err != nil {
   241  		t.Fatalf("err: %v", err)
   242  	}
   243  	if out != eval {
   244  		t.Fatalf("bad : %#v", out)
   245  	}
   246  
   247  	// Check the stats
   248  	stats = b.Stats()
   249  	if stats.TotalReady != 0 {
   250  		t.Fatalf("bad: %#v", stats)
   251  	}
   252  	if stats.TotalUnacked != 1 {
   253  		t.Fatalf("bad: %#v", stats)
   254  	}
   255  	if stats.TotalBlocked != 2 {
   256  		t.Fatalf("bad: %#v", stats)
   257  	}
   258  
   259  	// Ack out
   260  	err = b.Ack(eval.ID, token)
   261  	if err != nil {
   262  		t.Fatalf("err: %v", err)
   263  	}
   264  
   265  	// Check the stats
   266  	stats = b.Stats()
   267  	if stats.TotalReady != 1 {
   268  		t.Fatalf("bad: %#v", stats)
   269  	}
   270  	if stats.TotalUnacked != 0 {
   271  		t.Fatalf("bad: %#v", stats)
   272  	}
   273  	if stats.TotalBlocked != 1 {
   274  		t.Fatalf("bad: %#v", stats)
   275  	}
   276  
   277  	// Dequeue should work
   278  	out, token, err = b.Dequeue(defaultSched, time.Second)
   279  	if err != nil {
   280  		t.Fatalf("err: %v", err)
   281  	}
   282  	if out != eval2 {
   283  		t.Fatalf("bad : %#v", out)
   284  	}
   285  
   286  	// Check the stats
   287  	stats = b.Stats()
   288  	if stats.TotalReady != 0 {
   289  		t.Fatalf("bad: %#v", stats)
   290  	}
   291  	if stats.TotalUnacked != 1 {
   292  		t.Fatalf("bad: %#v", stats)
   293  	}
   294  	if stats.TotalBlocked != 1 {
   295  		t.Fatalf("bad: %#v", stats)
   296  	}
   297  
   298  	// Ack out
   299  	err = b.Ack(eval2.ID, token)
   300  	if err != nil {
   301  		t.Fatalf("err: %v", err)
   302  	}
   303  
   304  	// Check the stats
   305  	stats = b.Stats()
   306  	if stats.TotalReady != 1 {
   307  		t.Fatalf("bad: %#v", stats)
   308  	}
   309  	if stats.TotalUnacked != 0 {
   310  		t.Fatalf("bad: %#v", stats)
   311  	}
   312  	if stats.TotalBlocked != 0 {
   313  		t.Fatalf("bad: %#v", stats)
   314  	}
   315  
   316  	// Dequeue should work
   317  	out, token, err = b.Dequeue(defaultSched, time.Second)
   318  	if err != nil {
   319  		t.Fatalf("err: %v", err)
   320  	}
   321  	if out != eval3 {
   322  		t.Fatalf("bad : %#v", out)
   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 != 1 {
   331  		t.Fatalf("bad: %#v", stats)
   332  	}
   333  	if stats.TotalBlocked != 0 {
   334  		t.Fatalf("bad: %#v", stats)
   335  	}
   336  
   337  	// Ack out
   338  	err = b.Ack(eval3.ID, token)
   339  	if err != nil {
   340  		t.Fatalf("err: %v", err)
   341  	}
   342  
   343  	// Check the stats
   344  	stats = b.Stats()
   345  	if stats.TotalReady != 0 {
   346  		t.Fatalf("bad: %#v", stats)
   347  	}
   348  	if stats.TotalUnacked != 0 {
   349  		t.Fatalf("bad: %#v", stats)
   350  	}
   351  	if stats.TotalBlocked != 0 {
   352  		t.Fatalf("bad: %#v", stats)
   353  	}
   354  }
   355  
   356  func TestEvalBroker_Enqueue_Disable(t *testing.T) {
   357  	b := testBroker(t, 0)
   358  
   359  	// Enqueue
   360  	eval := mock.Eval()
   361  	b.SetEnabled(true)
   362  	err := b.Enqueue(eval)
   363  	if err != nil {
   364  		t.Fatalf("err: %v", err)
   365  	}
   366  
   367  	// Flush via SetEnabled
   368  	b.SetEnabled(false)
   369  
   370  	// Check the stats
   371  	stats := b.Stats()
   372  	if stats.TotalReady != 0 {
   373  		t.Fatalf("bad: %#v", stats)
   374  	}
   375  	if stats.TotalUnacked != 0 {
   376  		t.Fatalf("bad: %#v", stats)
   377  	}
   378  	if _, ok := stats.ByScheduler[eval.Type]; ok {
   379  		t.Fatalf("bad: %#v", stats)
   380  	}
   381  }
   382  
   383  func TestEvalBroker_Dequeue_Timeout(t *testing.T) {
   384  	b := testBroker(t, 0)
   385  	b.SetEnabled(true)
   386  
   387  	start := time.Now()
   388  	out, _, err := b.Dequeue(defaultSched, 5*time.Millisecond)
   389  	end := time.Now()
   390  
   391  	if err != nil {
   392  		t.Fatalf("err: %v", err)
   393  	}
   394  	if out != nil {
   395  		t.Fatalf("unexpected: %#v", out)
   396  	}
   397  
   398  	if diff := end.Sub(start); diff < 5*time.Millisecond {
   399  		t.Fatalf("bad: %#v", diff)
   400  	}
   401  }
   402  
   403  func TestEvalBroker_Dequeue_Empty_Timeout(t *testing.T) {
   404  	b := testBroker(t, 0)
   405  	b.SetEnabled(true)
   406  	doneCh := make(chan struct{}, 1)
   407  
   408  	go func() {
   409  		out, _, err := b.Dequeue(defaultSched, 0)
   410  		if err != nil {
   411  			t.Fatalf("err: %v", err)
   412  		}
   413  		if out == nil {
   414  			t.Fatal("Expect an eval")
   415  		}
   416  		doneCh <- struct{}{}
   417  	}()
   418  
   419  	// Sleep for a little bit
   420  	select {
   421  	case <-time.After(5 * time.Millisecond):
   422  	case <-doneCh:
   423  		t.Fatalf("Dequeue(0) should block")
   424  	}
   425  
   426  	// Enqueue to unblock the dequeue.
   427  	eval := mock.Eval()
   428  	err := b.Enqueue(eval)
   429  	if err != nil {
   430  		t.Fatalf("err: %v", err)
   431  	}
   432  
   433  	select {
   434  	case <-doneCh:
   435  		return
   436  	case <-time.After(5 * time.Millisecond):
   437  		t.Fatal("timeout: Dequeue(0) should return after enqueue")
   438  	}
   439  }
   440  
   441  // Ensure higher priority dequeued first
   442  func TestEvalBroker_Dequeue_Priority(t *testing.T) {
   443  	b := testBroker(t, 0)
   444  	b.SetEnabled(true)
   445  
   446  	eval1 := mock.Eval()
   447  	eval1.Priority = 10
   448  	b.Enqueue(eval1)
   449  
   450  	eval2 := mock.Eval()
   451  	eval2.Priority = 30
   452  	b.Enqueue(eval2)
   453  
   454  	eval3 := mock.Eval()
   455  	eval3.Priority = 20
   456  	b.Enqueue(eval3)
   457  
   458  	out1, _, _ := b.Dequeue(defaultSched, time.Second)
   459  	if out1 != eval2 {
   460  		t.Fatalf("bad: %#v", out1)
   461  	}
   462  
   463  	out2, _, _ := b.Dequeue(defaultSched, time.Second)
   464  	if out2 != eval3 {
   465  		t.Fatalf("bad: %#v", out2)
   466  	}
   467  
   468  	out3, _, _ := b.Dequeue(defaultSched, time.Second)
   469  	if out3 != eval1 {
   470  		t.Fatalf("bad: %#v", out3)
   471  	}
   472  }
   473  
   474  // Ensure FIFO at fixed priority
   475  func TestEvalBroker_Dequeue_FIFO(t *testing.T) {
   476  	b := testBroker(t, 0)
   477  	b.SetEnabled(true)
   478  	NUM := 100
   479  
   480  	for i := 0; i < NUM; i++ {
   481  		eval1 := mock.Eval()
   482  		eval1.CreateIndex = uint64(i)
   483  		eval1.ModifyIndex = uint64(i)
   484  		b.Enqueue(eval1)
   485  	}
   486  
   487  	for i := 0; i < NUM; i++ {
   488  		out1, _, _ := b.Dequeue(defaultSched, time.Second)
   489  		if out1.CreateIndex != uint64(i) {
   490  			t.Fatalf("bad: %d %#v", i, out1)
   491  		}
   492  	}
   493  }
   494  
   495  // Ensure fairness between schedulers
   496  func TestEvalBroker_Dequeue_Fairness(t *testing.T) {
   497  	b := testBroker(t, 0)
   498  	b.SetEnabled(true)
   499  	NUM := 100
   500  
   501  	for i := 0; i < NUM; i++ {
   502  		eval1 := mock.Eval()
   503  		if i < (NUM / 2) {
   504  			eval1.Type = structs.JobTypeService
   505  		} else {
   506  			eval1.Type = structs.JobTypeBatch
   507  		}
   508  		b.Enqueue(eval1)
   509  	}
   510  
   511  	counter := 0
   512  	for i := 0; i < NUM; i++ {
   513  		out1, _, _ := b.Dequeue(defaultSched, time.Second)
   514  
   515  		switch out1.Type {
   516  		case structs.JobTypeService:
   517  			if counter < 0 {
   518  				counter = 0
   519  			}
   520  			counter += 1
   521  		case structs.JobTypeBatch:
   522  			if counter > 0 {
   523  				counter = 0
   524  			}
   525  			counter -= 1
   526  		}
   527  
   528  		// This will fail randomly at times. It is very hard to
   529  		// test deterministically that its acting randomly.
   530  		if counter >= 25 || counter <= -25 {
   531  			t.Fatalf("unlikely sequence: %d", counter)
   532  		}
   533  	}
   534  }
   535  
   536  // Ensure we get unblocked
   537  func TestEvalBroker_Dequeue_Blocked(t *testing.T) {
   538  	b := testBroker(t, 0)
   539  	b.SetEnabled(true)
   540  
   541  	// Start with a blocked dequeue
   542  	outCh := make(chan *structs.Evaluation, 1)
   543  	go func() {
   544  		start := time.Now()
   545  		out, _, err := b.Dequeue(defaultSched, time.Second)
   546  		end := time.Now()
   547  		outCh <- out
   548  		if err != nil {
   549  			t.Fatalf("err: %v", err)
   550  		}
   551  		if d := end.Sub(start); d < 5*time.Millisecond {
   552  			t.Fatalf("bad: %v", d)
   553  		}
   554  	}()
   555  
   556  	// Wait for a bit
   557  	time.Sleep(5 * time.Millisecond)
   558  
   559  	// Enqueue
   560  	eval := mock.Eval()
   561  	err := b.Enqueue(eval)
   562  	if err != nil {
   563  		t.Fatalf("err: %v", err)
   564  	}
   565  
   566  	// Ensure dequeue
   567  	select {
   568  	case out := <-outCh:
   569  		if out != eval {
   570  			t.Fatalf("bad: %v", out)
   571  		}
   572  	case <-time.After(time.Second):
   573  		t.Fatalf("timeout")
   574  	}
   575  }
   576  
   577  // Ensure we nack in a timely manner
   578  func TestEvalBroker_Nack_Timeout(t *testing.T) {
   579  	b := testBroker(t, 5*time.Millisecond)
   580  	b.SetEnabled(true)
   581  
   582  	// Enqueue
   583  	eval := mock.Eval()
   584  	err := b.Enqueue(eval)
   585  	if err != nil {
   586  		t.Fatalf("err: %v", err)
   587  	}
   588  
   589  	// Dequeue
   590  	out, _, err := b.Dequeue(defaultSched, time.Second)
   591  	start := time.Now()
   592  	if err != nil {
   593  		t.Fatalf("err: %v", err)
   594  	}
   595  	if out != eval {
   596  		t.Fatalf("bad: %v", out)
   597  	}
   598  
   599  	// Dequeue, should block on Nack timer
   600  	out, _, err = b.Dequeue(defaultSched, time.Second)
   601  	end := time.Now()
   602  	if err != nil {
   603  		t.Fatalf("err: %v", err)
   604  	}
   605  	if out != eval {
   606  		t.Fatalf("bad: %v", out)
   607  	}
   608  
   609  	// Check the nack timer
   610  	if diff := end.Sub(start); diff < 5*time.Millisecond {
   611  		t.Fatalf("bad: %#v", diff)
   612  	}
   613  }
   614  
   615  // Ensure we nack in a timely manner
   616  func TestEvalBroker_Nack_TimeoutReset(t *testing.T) {
   617  	b := testBroker(t, 5*time.Millisecond)
   618  	b.SetEnabled(true)
   619  
   620  	// Enqueue
   621  	eval := mock.Eval()
   622  	err := b.Enqueue(eval)
   623  	if err != nil {
   624  		t.Fatalf("err: %v", err)
   625  	}
   626  
   627  	// Dequeue
   628  	out, token, err := b.Dequeue(defaultSched, time.Second)
   629  	start := time.Now()
   630  	if err != nil {
   631  		t.Fatalf("err: %v", err)
   632  	}
   633  	if out != eval {
   634  		t.Fatalf("bad: %v", out)
   635  	}
   636  
   637  	// Reset in 2 milliseconds
   638  	time.Sleep(2 * time.Millisecond)
   639  	if err := b.OutstandingReset(out.ID, token); err != nil {
   640  		t.Fatalf("err: %v", err)
   641  	}
   642  
   643  	// Dequeue, should block on Nack timer
   644  	out, _, err = b.Dequeue(defaultSched, time.Second)
   645  	end := time.Now()
   646  	if err != nil {
   647  		t.Fatalf("err: %v", err)
   648  	}
   649  	if out != eval {
   650  		t.Fatalf("bad: %v", out)
   651  	}
   652  
   653  	// Check the nack timer
   654  	if diff := end.Sub(start); diff < 7*time.Millisecond {
   655  		t.Fatalf("bad: %#v", diff)
   656  	}
   657  }
   658  
   659  func TestEvalBroker_DeliveryLimit(t *testing.T) {
   660  	b := testBroker(t, 0)
   661  	b.SetEnabled(true)
   662  
   663  	eval := mock.Eval()
   664  	err := b.Enqueue(eval)
   665  	if err != nil {
   666  		t.Fatalf("err: %v", err)
   667  	}
   668  
   669  	for i := 0; i < 3; i++ {
   670  		// Dequeue should work
   671  		out, token, err := b.Dequeue(defaultSched, time.Second)
   672  		if err != nil {
   673  			t.Fatalf("err: %v", err)
   674  		}
   675  		if out != eval {
   676  			t.Fatalf("bad : %#v", out)
   677  		}
   678  
   679  		// Nack with wrong token should fail
   680  		err = b.Nack(eval.ID, token)
   681  		if err != nil {
   682  			t.Fatalf("err: %v", err)
   683  		}
   684  	}
   685  
   686  	// Check the stats
   687  	stats := b.Stats()
   688  	if stats.TotalReady != 1 {
   689  		t.Fatalf("bad: %#v", stats)
   690  	}
   691  	if stats.TotalUnacked != 0 {
   692  		t.Fatalf("bad: %#v", stats)
   693  	}
   694  	if stats.ByScheduler[failedQueue].Ready != 1 {
   695  		t.Fatalf("bad: %#v", stats)
   696  	}
   697  	if stats.ByScheduler[failedQueue].Unacked != 0 {
   698  		t.Fatalf("bad: %#v", stats)
   699  	}
   700  
   701  	// Dequeue from failed queue
   702  	out, token, err := b.Dequeue([]string{failedQueue}, time.Second)
   703  	if err != nil {
   704  		t.Fatalf("err: %v", err)
   705  	}
   706  	if out != eval {
   707  		t.Fatalf("bad : %#v", out)
   708  	}
   709  
   710  	// Check the stats
   711  	stats = b.Stats()
   712  	if stats.TotalReady != 0 {
   713  		t.Fatalf("bad: %#v", stats)
   714  	}
   715  	if stats.TotalUnacked != 1 {
   716  		t.Fatalf("bad: %#v", stats)
   717  	}
   718  	if stats.ByScheduler[failedQueue].Ready != 0 {
   719  		t.Fatalf("bad: %#v", stats)
   720  	}
   721  	if stats.ByScheduler[failedQueue].Unacked != 1 {
   722  		t.Fatalf("bad: %#v", stats)
   723  	}
   724  
   725  	// Ack finally
   726  	err = b.Ack(out.ID, token)
   727  	if err != nil {
   728  		t.Fatalf("err: %v", err)
   729  	}
   730  
   731  	if _, ok := b.Outstanding(out.ID); ok {
   732  		t.Fatalf("should not be outstanding")
   733  	}
   734  
   735  	// Check the stats
   736  	stats = b.Stats()
   737  	if stats.TotalReady != 0 {
   738  		t.Fatalf("bad: %#v", stats)
   739  	}
   740  	if stats.TotalUnacked != 0 {
   741  		t.Fatalf("bad: %#v", stats)
   742  	}
   743  	if stats.ByScheduler[failedQueue].Ready != 0 {
   744  		t.Fatalf("bad: %#v", stats.ByScheduler[failedQueue])
   745  	}
   746  	if stats.ByScheduler[failedQueue].Unacked != 0 {
   747  		t.Fatalf("bad: %#v", stats.ByScheduler[failedQueue])
   748  	}
   749  }
   750  
   751  // Ensure fairness between schedulers
   752  func TestEvalBroker_Wait(t *testing.T) {
   753  	b := testBroker(t, 0)
   754  	b.SetEnabled(true)
   755  
   756  	// Create an eval that should wait
   757  	eval := mock.Eval()
   758  	eval.Wait = 10 * time.Millisecond
   759  	err := b.Enqueue(eval)
   760  	if err != nil {
   761  		t.Fatalf("err: %v", err)
   762  	}
   763  
   764  	// Verify waiting
   765  	stats := b.Stats()
   766  	if stats.TotalReady != 0 {
   767  		t.Fatalf("bad: %#v", stats)
   768  	}
   769  	if stats.TotalWaiting != 1 {
   770  		t.Fatalf("bad: %#v", stats)
   771  	}
   772  
   773  	// Let the wait elapse
   774  	time.Sleep(15 * time.Millisecond)
   775  
   776  	// Verify ready
   777  	stats = b.Stats()
   778  	if stats.TotalReady != 1 {
   779  		t.Fatalf("bad: %#v", stats)
   780  	}
   781  	if stats.TotalWaiting != 0 {
   782  		t.Fatalf("bad: %#v", stats)
   783  	}
   784  
   785  	// Dequeue should work
   786  	out, _, err := b.Dequeue(defaultSched, time.Second)
   787  	if err != nil {
   788  		t.Fatalf("err: %v", err)
   789  	}
   790  	if out != eval {
   791  		t.Fatalf("bad : %#v", out)
   792  	}
   793  }