open-nomad/nomad/deploymentwatcher/batcher.go

140 lines
3.4 KiB
Go
Raw Normal View History

2017-06-26 21:23:52 +00:00
package deploymentwatcher
import (
2017-06-28 04:36:16 +00:00
"context"
2017-06-26 21:23:52 +00:00
"time"
"github.com/hashicorp/nomad/nomad/structs"
)
2018-04-10 22:02:52 +00:00
// AllocUpdateBatcher is used to batch the updates to the desired transitions
// of allocations and the creation of evals.
type AllocUpdateBatcher struct {
2017-06-28 21:25:20 +00:00
// batch is the batching duration
batch time.Duration
// raft is used to actually commit the updates
2017-06-26 21:23:52 +00:00
raft DeploymentRaftEndpoints
// workCh is used to pass evaluations to the daemon process
workCh chan *updateWrapper
2017-06-26 21:23:52 +00:00
2017-06-28 04:36:16 +00:00
// ctx is used to exit the daemon batcher
ctx context.Context
2017-06-26 21:23:52 +00:00
}
// NewAllocUpdateBatcher returns an AllocUpdateBatcher that uses the passed raft endpoints to
2018-04-10 22:02:52 +00:00
// create the allocation desired transition updates and new evaluations and
// exits the batcher when the passed exit channel is closed.
func NewAllocUpdateBatcher(ctx context.Context, batchDuration time.Duration, raft DeploymentRaftEndpoints) *AllocUpdateBatcher {
b := &AllocUpdateBatcher{
batch: batchDuration,
raft: raft,
ctx: ctx,
workCh: make(chan *updateWrapper, 10),
2017-06-26 21:23:52 +00:00
}
go b.batcher()
return b
}
2018-04-10 22:02:52 +00:00
// CreateUpdate batches the allocation desired transition update and returns a
// future that tracks the completion of the request.
func (b *AllocUpdateBatcher) CreateUpdate(allocs map[string]*structs.DesiredTransition, eval *structs.Evaluation) *BatchFuture {
wrapper := &updateWrapper{
allocs: allocs,
e: eval,
f: make(chan *BatchFuture, 1),
2017-06-26 21:23:52 +00:00
}
b.workCh <- wrapper
return <-wrapper.f
}
type updateWrapper struct {
allocs map[string]*structs.DesiredTransition
e *structs.Evaluation
f chan *BatchFuture
2017-06-26 21:23:52 +00:00
}
// batcher is the long lived batcher goroutine
func (b *AllocUpdateBatcher) batcher() {
2017-07-03 18:51:58 +00:00
var timerCh <-chan time.Time
allocs := make(map[string]*structs.DesiredTransition)
2017-06-26 21:23:52 +00:00
evals := make(map[string]*structs.Evaluation)
future := NewBatchFuture()
2017-06-26 21:23:52 +00:00
for {
select {
2017-06-28 04:36:16 +00:00
case <-b.ctx.Done():
2017-06-26 21:23:52 +00:00
return
case w := <-b.workCh:
2017-07-03 18:51:58 +00:00
if timerCh == nil {
timerCh = time.After(b.batch)
2017-06-28 21:25:20 +00:00
}
// Store the eval and alloc updates, and attach the future
evals[w.e.DeploymentID] = w.e
for id, upd := range w.allocs {
allocs[id] = upd
}
w.f <- future
2017-07-03 18:51:58 +00:00
case <-timerCh:
// Capture the future and create a new one
f := future
future = NewBatchFuture()
2017-06-26 21:23:52 +00:00
2017-07-03 18:51:58 +00:00
// Shouldn't be possible
2017-06-26 21:23:52 +00:00
if f == nil {
2017-07-03 18:51:58 +00:00
panic("no future")
2017-06-26 21:23:52 +00:00
}
// Create the request
req := &structs.AllocUpdateDesiredTransitionRequest{
Allocs: allocs,
Evals: make([]*structs.Evaluation, 0, len(evals)),
}
2017-06-26 21:23:52 +00:00
for _, e := range evals {
req.Evals = append(req.Evals, e)
2017-06-26 21:23:52 +00:00
}
2017-07-03 18:51:58 +00:00
// Upsert the evals in a go routine
2018-04-10 22:02:52 +00:00
go f.Set(b.raft.UpdateAllocDesiredTransition(req))
2017-06-26 21:23:52 +00:00
2017-07-03 18:51:58 +00:00
// Reset the evals list and timer
2017-06-26 21:23:52 +00:00
evals = make(map[string]*structs.Evaluation)
allocs = make(map[string]*structs.DesiredTransition)
2017-07-03 18:51:58 +00:00
timerCh = nil
2017-06-26 21:23:52 +00:00
}
}
}
// BatchFuture is a future that can be used to retrieve the index the eval was
2017-06-26 21:23:52 +00:00
// created at or any error in the creation process
type BatchFuture struct {
2017-06-26 21:23:52 +00:00
index uint64
err error
waitCh chan struct{}
}
// NewBatchFuture returns a new BatchFuture
func NewBatchFuture() *BatchFuture {
return &BatchFuture{
2017-06-26 21:23:52 +00:00
waitCh: make(chan struct{}),
}
}
// Set sets the results of the future, unblocking any client.
func (f *BatchFuture) Set(index uint64, err error) {
2017-06-26 21:23:52 +00:00
f.index = index
f.err = err
close(f.waitCh)
}
// Results returns the creation index and any error.
func (f *BatchFuture) Results() (uint64, error) {
2017-06-26 21:23:52 +00:00
<-f.waitCh
return f.index, f.err
}