Skip inplace update on terminal batch allocation

This PR skips adding an inplace update to a successfully terminal batch
job to the plan. This avoids extra data in the plan and avoids
triggering updates on all clients that have the terminal allocation.
This is matching behavior of the service scheduler.

/cc @armon for review
This commit is contained in:
Alex Dadgar 2017-03-11 17:19:22 -08:00
parent 3f68aae7ab
commit 2c31d4036b
2 changed files with 75 additions and 4 deletions

View File

@ -2490,6 +2490,61 @@ func TestBatchSched_ReRun_SuccessfullyFinishedAlloc(t *testing.T) {
h.AssertEvalStatus(t, structs.EvalStatusComplete)
}
// This test checks that terminal allocations that receive an in-place updated
// are not added to the plan
func TestBatchSched_JobModify_InPlace_Terminal(t *testing.T) {
h := NewHarness(t)
// Create some nodes
var nodes []*structs.Node
for i := 0; i < 10; i++ {
node := mock.Node()
nodes = append(nodes, node)
noErr(t, h.State.UpsertNode(h.NextIndex(), node))
}
// Generate a fake job with allocations
job := mock.Job()
job.Type = structs.JobTypeBatch
noErr(t, h.State.UpsertJob(h.NextIndex(), job))
var allocs []*structs.Allocation
for i := 0; i < 10; i++ {
alloc := mock.Alloc()
alloc.Job = job
alloc.JobID = job.ID
alloc.NodeID = nodes[i].ID
alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
alloc.ClientStatus = structs.AllocClientStatusComplete
allocs = append(allocs, alloc)
}
noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
// Update the job
job2 := mock.Job()
job2.ID = job.ID
noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
// Create a mock evaluation to deal with drain
eval := &structs.Evaluation{
ID: structs.GenerateUUID(),
Priority: 50,
TriggeredBy: structs.EvalTriggerJobRegister,
JobID: job.ID,
}
// Process the evaluation
err := h.Process(NewBatchScheduler, eval)
if err != nil {
t.Fatalf("err: %v", err)
}
// Ensure no plan
if len(h.Plans) != 0 {
t.Fatalf("bad: %#v", h.Plans)
}
}
func TestGenericSched_FilterCompleteAllocs(t *testing.T) {
running := mock.Alloc()
desiredStop := mock.Alloc()

View File

@ -455,6 +455,15 @@ func setStatus(logger *log.Logger, planner Planner,
func inplaceUpdate(ctx Context, eval *structs.Evaluation, job *structs.Job,
stack Stack, updates []allocTuple) (destructive, inplace []allocTuple) {
// doInplace manipulates the updates map to make the current allocation
// an inplace update.
doInplace := func(cur, last, inplaceCount *int) {
updates[*cur], updates[*last-1] = updates[*last-1], updates[*cur]
*cur--
*last--
*inplaceCount++
}
ws := memdb.NewWatchSet()
n := len(updates)
inplaceCount := 0
@ -469,6 +478,15 @@ func inplaceUpdate(ctx Context, eval *structs.Evaluation, job *structs.Job,
continue
}
// Terminal batch allocations are not filtered when they are completed
// successfully. We should avoid adding the allocation to the plan in
// the case that it is an in-place update to avoid both additional data
// in the plan and work for the clients.
if update.Alloc.TerminalStatus() {
doInplace(&i, &n, &inplaceCount)
continue
}
// Get the existing node
node, err := ctx.State().NodeByID(ws, update.Alloc.NodeID)
if err != nil {
@ -523,11 +541,9 @@ func inplaceUpdate(ctx Context, eval *structs.Evaluation, job *structs.Job,
ctx.Plan().AppendAlloc(newAlloc)
// Remove this allocation from the slice
updates[i], updates[n-1] = updates[n-1], updates[i]
i--
n--
inplaceCount++
doInplace(&i, &n, &inplaceCount)
}
if len(updates) > 0 {
ctx.Logger().Printf("[DEBUG] sched: %#v: %d in-place updates of %d", eval, inplaceCount, len(updates))
}