github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/nomad/eval_broker_test.go (about)

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