Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ability to specify strategies for target allocation #1068

Closed
wants to merge 20 commits into from

Conversation

jaronoff97
Copy link
Contributor

@jaronoff97 jaronoff97 commented Aug 30, 2022

Goals:

  • Introduce a new interface to allow a user of the TA to describe an allocation strategy
    • Allocation strategies are given the current state and the new state and should return what the next state for the allocator should be
    • Each strategy should be idempotent with minimal to no side effects
  • Refactor the allocator's current strategy - least weighted – to be the first strategy

Summary

This PR introduces the AllocationStrategy interface, implements it through a refactor of the current allocator, and changes the configuration of the TA to allow for a user to specify an option. The operator's CRDs are also updated to allow the strategy to be specified in the Collector CRD. New tests have been introduced to confirm the strategy works as expected. All previous tests should continue working.

@jaronoff97 jaronoff97 marked this pull request as ready for review September 1, 2022 15:15
@jaronoff97 jaronoff97 requested a review from a team September 1, 2022 15:15
Copy link
Member

@secustor secustor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First glance of the code 😉

cmd/otel-allocator/allocation/least_weighted.go Outdated Show resolved Hide resolved
pkg/targetallocator/container.go Outdated Show resolved Hide resolved
cmd/otel-allocator/allocation/allocator.go Outdated Show resolved Hide resolved
cmd/otel-allocator/allocation/allocator.go Outdated Show resolved Hide resolved
cmd/otel-allocator/allocation/allocator.go Outdated Show resolved Hide resolved
cmd/otel-allocator/main.go Outdated Show resolved Hide resolved
cmd/otel-allocator/allocation/strategy/strategy.go Outdated Show resolved Hide resolved
cmd/otel-allocator/allocation/strategy/strategy.go Outdated Show resolved Hide resolved
cmd/otel-allocator/allocation/strategy/strategy.go Outdated Show resolved Hide resolved
cmd/otel-allocator/discovery/discovery.go Outdated Show resolved Hide resolved
cmd/otel-allocator/discovery/discovery_test.go Outdated Show resolved Hide resolved
cmd/otel-allocator/main.go Show resolved Hide resolved
}

// SetCollectors sets the set of collectors with key=collectorName, value=Collector object.
// SetCollectors sets the set of collectors with key=collectorName, value=CollectorName object.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this comment change - it makes it seem like key == value.

collectors: make(map[string]*collector),
targetItems: make(map[string]*TargetItem),
log: log,
state: strategy.NewState(make(map[string]strategy.Collector), make(map[string]strategy.TargetItem)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't need/use the maps here, shouldn't the NewState() function be able to handle nil values and make the maps itself?

Comment on lines +39 to +40
for colName, _ := range allocator.Collectors() {
if colName == collector {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The colName can only be in the map once - can we try to access it directly rather than traversing the whole map?

}, []string{"collector_name"})
)

func New(name string) (Allocator, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this is obvious or has already been discussed, but what is the benefit of the init() registration pattern? Couldn't we pass an interface into New() instead and not require global state? Any caller will always have to import their strategy (or strategies) and this package.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a mapping of string identifiers to strategy implementations enables using the CRD or other configuration mechanisms to select from among multiple available strategies at runtime. This could be implemented by the consuming system, but would likely result in duplicate implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, thanks for explaining! 🙂

NumTargets int
}

type State struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this state stuff seems like it belongs in its own file. I'm not sure if some of these other things should also be split up?

Comment on lines +91 to +93
collectors map[string]Collector
// targetItems is a map from a target item's hash to the target items allocated state
targetItems map[string]TargetItem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lock for these maps are up in allocation.Allocator but then they are also modified in the strategy - it seems easy to change something later and mess it up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I think it is important that State is immutable and that strategies return a new State instance. Then the locks held by Allocator are protecting the "active" State and anything modifying State doesn't need to worry about locking since it's immutable and always returning a new State.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think only one object should be modifying the state if it's going to be modified - making it immutable would also solve the problem.

@jaronoff97
Copy link
Contributor Author

As discussed in slack, I'm going to close this PR in favor of a different approach where we instead make the allocator itself the interface instead of making the strategy a part of the allocator. See this conversation for more detail

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants