in pkg/scheduler/objects/application.go [1441:1539]
func (sa *Application) tryNodes(ask *Allocation, iterator NodeIterator) *AllocationResult {
var nodeToReserve *Node
scoreReserved := math.Inf(1)
// check if the alloc is reserved or not
allocKey := ask.GetAllocationKey()
reserved := sa.reservations[allocKey]
var allocResult *AllocationResult
var predicateErrors map[string]int
iterator.ForEachNode(func(node *Node) bool {
// skip the node if the node is not schedulable
if !node.IsSchedulable() {
log.Log(log.SchedApplication).Debug("skipping node for ask as state is unschedulable",
zap.String("allocationKey", allocKey),
zap.String("node", node.NodeID))
return true
}
// skip over the node if the resource does not fit the node at all.
if !node.FitInNode(ask.GetAllocatedResource()) {
return true
}
tryNodeStart := time.Now()
result, err := sa.tryNode(node, ask)
if err != nil {
if predicateErrors == nil {
predicateErrors = make(map[string]int)
}
predicateErrors[err.Error()]++
}
// allocation worked so return
if result != nil {
metrics.GetSchedulerMetrics().ObserveTryNodeLatency(tryNodeStart)
// check if the alloc had a reservation: if it has set the resultType and return
if reserved != nil {
if reserved.nodeID != node.NodeID {
// we have a different node reserved for this alloc
log.Log(log.SchedApplication).Debug("allocate picking reserved alloc during non reserved allocate",
zap.String("appID", sa.ApplicationID),
zap.String("reserved nodeID", reserved.nodeID),
zap.String("allocationKey", allocKey))
result.ReservedNodeID = reserved.nodeID
} else {
// NOTE: this is a safeguard as reserved nodes should never be part of the iterator
log.Log(log.SchedApplication).Debug("allocate found reserved alloc during non reserved allocate",
zap.String("appID", sa.ApplicationID),
zap.String("nodeID", node.NodeID),
zap.String("allocationKey", allocKey))
}
result.ResultType = AllocatedReserved
allocResult = result
return false
}
// nothing reserved just return this as a normal alloc
allocResult = result
return false
}
// nothing allocated should we look at a reservation?
askAge := time.Since(ask.GetCreateTime())
if reserved == nil && askAge > reservationDelay {
log.Log(log.SchedApplication).Debug("app reservation check",
zap.String("allocationKey", allocKey),
zap.Time("createTime", ask.GetCreateTime()),
zap.Duration("askAge", askAge),
zap.Duration("reservationDelay", reservationDelay))
score := node.GetFitInScoreForAvailableResource(ask.GetAllocatedResource())
// Record the best node so-far to reserve
if score < scoreReserved {
scoreReserved = score
nodeToReserve = node
}
}
return true
})
if allocResult != nil {
return allocResult
}
if predicateErrors != nil {
ask.SendPredicatesFailedEvent(predicateErrors)
}
// we have not allocated yet, check if we should reserve
// NOTE: the node should not be reserved as the iterator filters them but we do not lock the nodes
if nodeToReserve != nil && !nodeToReserve.IsReserved() {
log.Log(log.SchedApplication).Debug("found candidate node for app reservation",
zap.String("appID", sa.ApplicationID),
zap.String("nodeID", nodeToReserve.NodeID),
zap.String("allocationKey", allocKey),
zap.Int("reservations", len(sa.reservations)))
// skip the node if conditions can not be satisfied
if nodeToReserve.preReserveConditions(ask) != nil {
return nil
}
// return reservation allocation and mark it as a reservation
return newReservedAllocationResult(nodeToReserve.NodeID, ask)
}
// ask does not fit, skip to next ask
return nil
}