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