github.com/uchennaokeke444/nomad@v0.11.8/nomad/deploymentwatcher/batcher.go (about)

     1  package deploymentwatcher
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/hashicorp/nomad/nomad/structs"
     8  )
     9  
    10  // AllocUpdateBatcher is used to batch the updates to the desired transitions
    11  // of allocations and the creation of evals.
    12  type AllocUpdateBatcher struct {
    13  	// batch is the batching duration
    14  	batch time.Duration
    15  
    16  	// raft is used to actually commit the updates
    17  	raft DeploymentRaftEndpoints
    18  
    19  	// workCh is used to pass evaluations to the daemon process
    20  	workCh chan *updateWrapper
    21  
    22  	// ctx is used to exit the daemon batcher
    23  	ctx context.Context
    24  }
    25  
    26  // NewAllocUpdateBatcher returns an AllocUpdateBatcher that uses the passed raft endpoints to
    27  // create the allocation desired transition updates and new evaluations and
    28  // exits the batcher when the passed exit channel is closed.
    29  func NewAllocUpdateBatcher(ctx context.Context, batchDuration time.Duration, raft DeploymentRaftEndpoints) *AllocUpdateBatcher {
    30  	b := &AllocUpdateBatcher{
    31  		batch:  batchDuration,
    32  		raft:   raft,
    33  		ctx:    ctx,
    34  		workCh: make(chan *updateWrapper, 10),
    35  	}
    36  
    37  	go b.batcher()
    38  	return b
    39  }
    40  
    41  // CreateUpdate batches the allocation desired transition update and returns a
    42  // future that tracks the completion of the request.
    43  func (b *AllocUpdateBatcher) CreateUpdate(allocs map[string]*structs.DesiredTransition, eval *structs.Evaluation) *BatchFuture {
    44  	wrapper := &updateWrapper{
    45  		allocs: allocs,
    46  		e:      eval,
    47  		f:      make(chan *BatchFuture, 1),
    48  	}
    49  
    50  	b.workCh <- wrapper
    51  	return <-wrapper.f
    52  }
    53  
    54  type updateWrapper struct {
    55  	allocs map[string]*structs.DesiredTransition
    56  	e      *structs.Evaluation
    57  	f      chan *BatchFuture
    58  }
    59  
    60  // batcher is the long lived batcher goroutine
    61  func (b *AllocUpdateBatcher) batcher() {
    62  	var timerCh <-chan time.Time
    63  	allocs := make(map[string]*structs.DesiredTransition)
    64  	evals := make(map[string]*structs.Evaluation)
    65  	future := NewBatchFuture()
    66  	for {
    67  		select {
    68  		case <-b.ctx.Done():
    69  			return
    70  		case w := <-b.workCh:
    71  			if timerCh == nil {
    72  				timerCh = time.After(b.batch)
    73  			}
    74  
    75  			// Store the eval and alloc updates, and attach the future
    76  			evals[w.e.DeploymentID] = w.e
    77  			for id, upd := range w.allocs {
    78  				allocs[id] = upd
    79  			}
    80  
    81  			w.f <- future
    82  		case <-timerCh:
    83  			// Capture the future and create a new one
    84  			f := future
    85  			future = NewBatchFuture()
    86  
    87  			// Shouldn't be possible
    88  			if f == nil {
    89  				panic("no future")
    90  			}
    91  
    92  			// Create the request
    93  			req := &structs.AllocUpdateDesiredTransitionRequest{
    94  				Allocs: allocs,
    95  				Evals:  make([]*structs.Evaluation, 0, len(evals)),
    96  			}
    97  
    98  			for _, e := range evals {
    99  				req.Evals = append(req.Evals, e)
   100  			}
   101  
   102  			// Upsert the evals in a go routine
   103  			go f.Set(b.raft.UpdateAllocDesiredTransition(req))
   104  
   105  			// Reset the evals list and timer
   106  			evals = make(map[string]*structs.Evaluation)
   107  			allocs = make(map[string]*structs.DesiredTransition)
   108  			timerCh = nil
   109  		}
   110  	}
   111  }
   112  
   113  // BatchFuture is a future that can be used to retrieve the index the eval was
   114  // created at or any error in the creation process
   115  type BatchFuture struct {
   116  	index  uint64
   117  	err    error
   118  	waitCh chan struct{}
   119  }
   120  
   121  // NewBatchFuture returns a new BatchFuture
   122  func NewBatchFuture() *BatchFuture {
   123  	return &BatchFuture{
   124  		waitCh: make(chan struct{}),
   125  	}
   126  }
   127  
   128  // Set sets the results of the future, unblocking any client.
   129  func (f *BatchFuture) Set(index uint64, err error) {
   130  	f.index = index
   131  	f.err = err
   132  	close(f.waitCh)
   133  }
   134  
   135  // Results returns the creation index and any error.
   136  func (f *BatchFuture) Results() (uint64, error) {
   137  	<-f.waitCh
   138  	return f.index, f.err
   139  }