From abf7e52689617dd9a3510cf77eb337945a9e282f Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sat, 20 Feb 2016 11:24:06 -0800 Subject: [PATCH 1/3] nomad: adding compound index on alloc terminal status --- nomad/state/schema.go | 24 ++++++++++++++--- nomad/state/state_store.go | 24 ++++++++++++++++- nomad/state/state_store_test.go | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 4 deletions(-) diff --git a/nomad/state/schema.go b/nomad/state/schema.go index 2d49ca46e..10553856d 100644 --- a/nomad/state/schema.go +++ b/nomad/state/schema.go @@ -222,9 +222,27 @@ func allocTableSchema() *memdb.TableSchema { Name: "node", AllowMissing: true, // Missing is allow for failed allocations Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "NodeID", - Lowercase: true, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "NodeID", + Lowercase: true, + }, + + // Conditional indexer on if allocation is terminal + &memdb.ConditionalIndex{ + Conditional: func(obj interface{}) (bool, error) { + // Cast to allocation + alloc, ok := obj.(*structs.Allocation) + if !ok { + return false, fmt.Errorf("wrong type, got %t should be Allocation", obj) + } + + // Check if the allocation is terminal + return alloc.TerminalStatus(), nil + }, + }, + }, }, }, diff --git a/nomad/state/state_store.go b/nomad/state/state_store.go index d097bb0d7..07341fbd8 100644 --- a/nomad/state/state_store.go +++ b/nomad/state/state_store.go @@ -865,8 +865,30 @@ func (s *StateStore) AllocsByIDPrefix(id string) (memdb.ResultIterator, error) { func (s *StateStore) AllocsByNode(node string) ([]*structs.Allocation, error) { txn := s.db.Txn(false) + // Get an iterator over the node allocations, using only the + // node prefix which ignores the terminal status + iter, err := txn.Get("allocs", "node_prefix", node) + if err != nil { + return nil, err + } + + var out []*structs.Allocation + for { + raw := iter.Next() + if raw == nil { + break + } + out = append(out, raw.(*structs.Allocation)) + } + return out, nil +} + +// AllocsByNode returns all the allocations by node and terminal status +func (s *StateStore) AllocsByNodeTerminal(node string, terminal bool) ([]*structs.Allocation, error) { + txn := s.db.Txn(false) + // Get an iterator over the node allocations - iter, err := txn.Get("allocs", "node", node) + iter, err := txn.Get("allocs", "node", node, terminal) if err != nil { return nil, err } diff --git a/nomad/state/state_store_test.go b/nomad/state/state_store_test.go index 2260738fa..c2a9cac8f 100644 --- a/nomad/state/state_store_test.go +++ b/nomad/state/state_store_test.go @@ -1581,6 +1581,54 @@ func TestStateStore_AllocsByNode(t *testing.T) { } } +func TestStateStore_AllocsByNodeTerminal(t *testing.T) { + state := testStateStore(t) + var allocs, term, nonterm []*structs.Allocation + + for i := 0; i < 10; i++ { + alloc := mock.Alloc() + alloc.NodeID = "foo" + if i%2 == 0 { + alloc.DesiredStatus = structs.AllocDesiredStatusStop + term = append(term, alloc) + } else { + nonterm = append(nonterm, alloc) + } + allocs = append(allocs, alloc) + } + + err := state.UpsertAllocs(1000, allocs) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Verify the terminal allocs + out, err := state.AllocsByNodeTerminal("foo", true) + if err != nil { + t.Fatalf("err: %v", err) + } + + sort.Sort(AllocIDSort(term)) + sort.Sort(AllocIDSort(out)) + + if !reflect.DeepEqual(term, out) { + t.Fatalf("bad: %#v %#v", term, out) + } + + // Verify the non-terminal allocs + out, err = state.AllocsByNodeTerminal("foo", false) + if err != nil { + t.Fatalf("err: %v", err) + } + + sort.Sort(AllocIDSort(nonterm)) + sort.Sort(AllocIDSort(out)) + + if !reflect.DeepEqual(nonterm, out) { + t.Fatalf("bad: %#v %#v", nonterm, out) + } +} + func TestStateStore_AllocsByJob(t *testing.T) { state := testStateStore(t) var allocs []*structs.Allocation From 2d7236eb956099b1b1533425f6752b2e1ad768bd Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sat, 20 Feb 2016 11:26:38 -0800 Subject: [PATCH 2/3] nomad: Use AllocsByNodeTerminal in plan apply --- nomad/plan_apply.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/nomad/plan_apply.go b/nomad/plan_apply.go index 23ac96c1b..0a2f15955 100644 --- a/nomad/plan_apply.go +++ b/nomad/plan_apply.go @@ -256,15 +256,12 @@ func evaluateNodePlan(snap *state.StateSnapshot, plan *structs.Plan, nodeID stri return false, nil } - // Get the existing allocations - existingAlloc, err := snap.AllocsByNode(nodeID) + // Get the existing allocations that are non-terminal + existingAlloc, err := snap.AllocsByNodeTerminal(nodeID, false) if err != nil { return false, fmt.Errorf("failed to get existing allocations for '%s': %v", nodeID, err) } - // Filter on alloc state - existingAlloc = structs.FilterTerminalAllocs(existingAlloc) - // Determine the proposed allocation by first removing allocations // that are planned evictions and adding the new allocations. proposed := existingAlloc From 35741fceddba6d444a40d705373967b531081bb9 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sat, 20 Feb 2016 11:29:15 -0800 Subject: [PATCH 3/3] scheduler: Use AllocsByNodeTerminal to avoid filtering --- scheduler/context.go | 7 ++----- scheduler/scheduler.go | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scheduler/context.go b/scheduler/context.go index 0a765944f..01f61eed9 100644 --- a/scheduler/context.go +++ b/scheduler/context.go @@ -107,15 +107,12 @@ func (e *EvalContext) Reset() { } func (e *EvalContext) ProposedAllocs(nodeID string) ([]*structs.Allocation, error) { - // Get the existing allocations - existingAlloc, err := e.state.AllocsByNode(nodeID) + // Get the existing allocations that are non-terminal + existingAlloc, err := e.state.AllocsByNodeTerminal(nodeID, false) if err != nil { return nil, err } - // Filter on alloc state - existingAlloc = structs.FilterTerminalAllocs(existingAlloc) - // Determine the proposed allocation by first removing allocations // that are planned evictions and adding the new allocations. proposed := existingAlloc diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index baed71e73..518280d07 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -63,6 +63,9 @@ type State interface { // AllocsByNode returns all the allocations by node AllocsByNode(node string) ([]*structs.Allocation, error) + // AllocsByNodeTerminal returns all the allocations by node filtering by terminal status + AllocsByNodeTerminal(node string, terminal bool) ([]*structs.Allocation, error) + // GetNodeByID is used to lookup a node by ID NodeByID(nodeID string) (*structs.Node, error)