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 }