open-nomad/scheduler/select.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

120 lines
3.2 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2015-08-12 01:27:54 +00:00
package scheduler
// LimitIterator is a RankIterator used to limit the number of options
2016-05-15 16:41:34 +00:00
// that are returned before we artificially end the stream.
2015-08-12 01:27:54 +00:00
type LimitIterator struct {
ctx Context
source RankIterator
limit int
maxSkip int
scoreThreshold float64
seen int
skippedNodes []*RankedNode
skippedNodeIndex int
2015-08-12 01:27:54 +00:00
}
2018-01-29 18:35:11 +00:00
// NewLimitIterator returns a LimitIterator with a fixed limit of returned options.
// Up to maxSkip options whose score is below scoreThreshold are skipped
// if there are additional options available in the source iterator
func NewLimitIterator(ctx Context, source RankIterator, limit int, scoreThreshold float64, maxSkip int) *LimitIterator {
2015-08-12 01:27:54 +00:00
iter := &LimitIterator{
ctx: ctx,
source: source,
limit: limit,
maxSkip: maxSkip,
scoreThreshold: scoreThreshold,
2018-01-29 18:35:11 +00:00
skippedNodes: make([]*RankedNode, 0, maxSkip),
2015-08-12 01:27:54 +00:00
}
return iter
}
func (iter *LimitIterator) SetLimit(limit int) {
iter.limit = limit
}
2015-08-12 01:27:54 +00:00
func (iter *LimitIterator) Next() *RankedNode {
if iter.seen == iter.limit {
return nil
}
option := iter.nextOption()
2015-08-12 01:27:54 +00:00
if option == nil {
return nil
}
if len(iter.skippedNodes) < iter.maxSkip {
// Try skipping ahead up to maxSkip to find an option with score lesser than the threshold
for option != nil && option.FinalScore <= iter.scoreThreshold && len(iter.skippedNodes) < iter.maxSkip {
iter.skippedNodes = append(iter.skippedNodes, option)
option = iter.source.Next()
}
}
2015-08-12 01:27:54 +00:00
iter.seen += 1
if option == nil { // Didn't find anything, so use the skipped nodes instead
return iter.nextOption()
}
2015-08-12 01:27:54 +00:00
return option
}
// nextOption uses the iterator's list of skipped nodes if the source iterator is exhausted
func (iter *LimitIterator) nextOption() *RankedNode {
sourceOption := iter.source.Next()
if sourceOption == nil && iter.skippedNodeIndex < len(iter.skippedNodes) {
skippedOption := iter.skippedNodes[iter.skippedNodeIndex]
iter.skippedNodeIndex += 1
return skippedOption
}
return sourceOption
}
2015-08-13 22:01:02 +00:00
func (iter *LimitIterator) Reset() {
iter.source.Reset()
iter.seen = 0
2018-01-29 18:35:11 +00:00
iter.skippedNodes = make([]*RankedNode, 0, iter.maxSkip)
iter.skippedNodeIndex = 0
2015-08-13 22:01:02 +00:00
}
2015-08-12 01:27:54 +00:00
// MaxScoreIterator is a RankIterator used to return only a single result
// of the item with the highest score. This iterator will consume all of the
// possible inputs and only returns the highest ranking result.
type MaxScoreIterator struct {
ctx Context
source RankIterator
max *RankedNode
}
// NewMaxScoreIterator returns a MaxScoreIterator over the given source
2015-08-13 17:05:54 +00:00
func NewMaxScoreIterator(ctx Context, source RankIterator) *MaxScoreIterator {
iter := &MaxScoreIterator{
ctx: ctx,
source: source,
}
return iter
}
2015-08-12 01:27:54 +00:00
func (iter *MaxScoreIterator) Next() *RankedNode {
2015-08-13 17:05:54 +00:00
// Check if we've found the max, return nil
if iter.max != nil {
return nil
}
// Consume and determine the max
2015-08-12 01:27:54 +00:00
for {
option := iter.source.Next()
if option == nil {
2015-08-13 17:05:54 +00:00
return iter.max
2015-08-12 01:27:54 +00:00
}
if iter.max == nil || option.FinalScore > iter.max.FinalScore {
2015-08-12 01:27:54 +00:00
iter.max = option
}
}
}
2015-08-13 22:01:02 +00:00
func (iter *MaxScoreIterator) Reset() {
iter.source.Reset()
iter.max = nil
}