github.com/emate/nomad@v0.8.2-wo-binpacking/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 // EvalBatcher is used to batch the creation of evaluations 11 type EvalBatcher struct { 12 // batch is the batching duration 13 batch time.Duration 14 15 // raft is used to actually commit the evaluations 16 raft DeploymentRaftEndpoints 17 18 // workCh is used to pass evaluations to the daemon process 19 workCh chan *evalWrapper 20 21 // ctx is used to exit the daemon batcher 22 ctx context.Context 23 } 24 25 // NewEvalBatcher returns an EvalBatcher that uses the passed raft endpoints to 26 // create the evaluations and exits the batcher when the passed exit channel is 27 // closed. 28 func NewEvalBatcher(batchDuration time.Duration, raft DeploymentRaftEndpoints, ctx context.Context) *EvalBatcher { 29 b := &EvalBatcher{ 30 batch: batchDuration, 31 raft: raft, 32 ctx: ctx, 33 workCh: make(chan *evalWrapper, 10), 34 } 35 36 go b.batcher() 37 return b 38 } 39 40 // CreateEval batches the creation of the evaluation and returns a future that 41 // tracks the evaluations creation. 42 func (b *EvalBatcher) CreateEval(e *structs.Evaluation) *EvalFuture { 43 wrapper := &evalWrapper{ 44 e: e, 45 f: make(chan *EvalFuture, 1), 46 } 47 48 b.workCh <- wrapper 49 return <-wrapper.f 50 } 51 52 type evalWrapper struct { 53 e *structs.Evaluation 54 f chan *EvalFuture 55 } 56 57 // batcher is the long lived batcher goroutine 58 func (b *EvalBatcher) batcher() { 59 var timerCh <-chan time.Time 60 evals := make(map[string]*structs.Evaluation) 61 future := NewEvalFuture() 62 for { 63 select { 64 case <-b.ctx.Done(): 65 return 66 case w := <-b.workCh: 67 if timerCh == nil { 68 timerCh = time.After(b.batch) 69 } 70 71 // Store the eval and attach the future 72 evals[w.e.DeploymentID] = w.e 73 w.f <- future 74 case <-timerCh: 75 // Capture the future and create a new one 76 f := future 77 future = NewEvalFuture() 78 79 // Shouldn't be possible 80 if f == nil { 81 panic("no future") 82 } 83 84 // Capture the evals 85 all := make([]*structs.Evaluation, 0, len(evals)) 86 for _, e := range evals { 87 all = append(all, e) 88 } 89 90 // Upsert the evals in a go routine 91 go f.Set(b.raft.UpsertEvals(all)) 92 93 // Reset the evals list and timer 94 evals = make(map[string]*structs.Evaluation) 95 timerCh = nil 96 } 97 } 98 } 99 100 // EvalFuture is a future that can be used to retrieve the index the eval was 101 // created at or any error in the creation process 102 type EvalFuture struct { 103 index uint64 104 err error 105 waitCh chan struct{} 106 } 107 108 // NewEvalFuture returns a new EvalFuture 109 func NewEvalFuture() *EvalFuture { 110 return &EvalFuture{ 111 waitCh: make(chan struct{}), 112 } 113 } 114 115 // Set sets the results of the future, unblocking any client. 116 func (f *EvalFuture) Set(index uint64, err error) { 117 f.index = index 118 f.err = err 119 close(f.waitCh) 120 } 121 122 // Results returns the creation index and any error. 123 func (f *EvalFuture) Results() (uint64, error) { 124 <-f.waitCh 125 return f.index, f.err 126 }