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

Refactored limits to be able to define them dynamically when Cortex is vendored in another project #1549

Merged
merged 6 commits into from Aug 21, 2019

Conversation

sandeepsukhani
Copy link
Contributor

@sandeepsukhani sandeepsukhani commented Aug 1, 2019

This would be useful to define limits specific to Loki while still being able to use the same code for managing limits and per tenant overwrites.

Changes include:

  1. Limits are now stored as key-value pairs, where the key is a string and values are interfaces for being able to support dynamic limits
  2. Limits need to be registered before being able to use them
  3. Sample and Labels validation methods now accept any type which implements required methods to get required limits for validation
  4. Added Setter methods to change default limit values at runtime for testing purpose

Signed-off-by: Sandeep Sukhani sandeep.d.sukhani@gmail.com

@bboreham
Copy link
Contributor

bboreham commented Aug 6, 2019

Can you say a bit more about what has been done in the 800 lines of changes?
It looks like some words were renamed - does this relate to making things dynamic?

@sandeepsukhani
Copy link
Contributor Author

Exact usecase is being able to manage limits in Loki using same code as cortex. Loki uses some of the limits from Cortex and few additional ones specific to Loki.
pkg/util/limits/limits.go is to manage limit types and per tenant overwrites.
pkg/util/limits/validation.go provides methods to access specific limits.
With this change Loki would use code for managing limits from limits.go and provide its own methods for specific limits.

@bboreham
Copy link
Contributor

bboreham commented Aug 6, 2019

For instance, it looks like you changed the package name "validation" to "limits".
Is that true? If so, what was the reason?
Please ensure that your commit message describes all the changes made.

@sandeepsukhani
Copy link
Contributor Author

what was the reason?

Actually, I wrote a new package with the name "limits" because most of its use case was managing and accessing limits. I had copied parts of code from the previous package so it looks like just a rename.
If you feel that it made more sense with the previous name, then I can rename it to validation.

Please ensure that your commit message describes all the changes made.

Sure will do and will make sure I take care of them going forward. Sorry about that.

@bboreham
Copy link
Contributor

bboreham commented Aug 6, 2019

This isn't about the name, it's about you explaining what you did so it can be reviewed.
As it stands it's an 800+ line change. Maybe if you didn't rename so much it would be a 100-line change and much easier to review.

@sandeepsukhani
Copy link
Contributor Author

sandeepsukhani commented Aug 9, 2019

@bboreham renamed and refactored the code to make the changes minimal. Please let me know if it looks good or whether I can do any more improvements to it.
I have also updated commit message and description with the changes that I have done.

pkg/util/validation/limits.go Outdated Show resolved Hide resolved
pkg/util/validation/limits.go Outdated Show resolved Hide resolved
pkg/util/validation/limits.go Outdated Show resolved Hide resolved
pkg/util/validation/limits.go Outdated Show resolved Hide resolved
pkg/util/validation/validate.go Show resolved Hide resolved
Copy link
Contributor

@gouthamve gouthamve left a comment

Choose a reason for hiding this comment

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

LGTM

@sandeepsukhani
Copy link
Contributor Author

@bboreham @tomwilkie Can you please have a look at this and suggest any changes if required to get another approval?

@tomwilkie
Copy link
Contributor

As discussed offline, not super happy with the amount of strings here. Quick sketch of an alternative:

package foo

type Limits interface {
    RegisterFlags()
    UnmarhsalYAML()
}

type Overrides struct {
    factory    func() Limits
    defaults   Limits
    overrides map[string]Limits
}

func (o Overrides) register() {]}
    o.defaults = o.factory()
    o.defaults.RegisterFlags()
}


func (o *Overrides) getFloat64(userID string, f func(*Limits) float64) float64 {
	o.overridesMtx.RLock()
	defer o.overridesMtx.RUnlock()
	override, ok := o.overrides[userID]
	if !ok {
		return f(&o.Defaults)
	}
	return f(override)
}

func loop() {
    // load the overrides file in the map above.
}

// Cortex 

type CortexLimits struct {
    IngestionRate          float64       `yaml:"ingestion_rate"`
    ...
}

type CortexOverrider struct {
    Overrides
}

func (o *CortexOverrider) IngestionRate(userID string) float64 {
	return o.getFloat64(userID, func(l *Limits) float64 {
        cl := l.(CortexLimits)
		return l.IngestionRate
	})
}

// Loki

type LokiLimits struct {
    ....
}

type LokiOverrider astruct {
    Overrides
}

@@ -73,7 +76,172 @@ func (l *Limits) UnmarshalYAML(unmarshal func(interface{}) error) error {
// We want to set c to the defaults and then overwrite it with the input.
// To make unmarshal fill the plain data struct rather than calling UnmarshalYAML
// again, we have to hide it using a type indirection. See prometheus/config.
*l = defaultLimits
if defaultLimits != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this values were getting reset to 0 while loading cortex config which had just per tenant overrides config path set.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why did this not happen before?

Copy link
Contributor

Choose a reason for hiding this comment

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

@sandlis explained offline:

  1. we init the flags, which sets up the default in a Limits struct in cortex pkg.
  2. we then load YAML, which will overwrite them with 0s, as defaultLimits has not been inited.
  3. we Parse the flags, which set only ones we've specifices
  4. we then set this defaultLimits to the half-zero'd struct.

…g in Cortex

Added OverridesManager which would store default limits and per tenant overrides as an interface
Factory method for creating OverridesManager accepts overrides reload config, method to load overrides from yaml file and default limit

Sample and Labels validation methods now accept any type which implement required methods to get required limits for validation

Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Copy link
Contributor

@gouthamve gouthamve left a comment

Choose a reason for hiding this comment

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

LGTM besides comment. Will take another pass though.

pkg/util/validation/validate.go Show resolved Hide resolved
Copy link
Contributor

@rfratto rfratto left a comment

Choose a reason for hiding this comment

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

LGTM

@tomwilkie tomwilkie self-requested a review August 19, 2019 15:32
if err != nil {
return nil, err
// NewOverridesManager creates an instance of OverridesManager and starts reload overrides loop based on config
func NewOverridesManager(perTenantOverridePeriod time.Duration, overridesReloadPath string, overridesLoader OverridesLoader,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this would be nicer as a config struct, not individual args.

Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
@sandeepsukhani
Copy link
Contributor Author

@tomwilkie Thanks for the review! Done the suggested changes.
Waiting for a fix for broken modules. Will rebase this when it is merged in master to run the checks.

}

return overridesAsInterface, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make this take a "factory" for the Limits type? Then have the code to make a map vs the defaults would be in override.go.

I think this is possible with yaml.v3 and Node: https://godoc.org/gopkg.in/yaml.v3

Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed offline, Node.Decode doesn't honour KnownFields: go-yaml/yaml#460

Copy link
Contributor

@tomwilkie tomwilkie left a comment

Choose a reason for hiding this comment

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

Yeah this is looking much better. Just a couple of nits, should be able to merge really soon now.

…ug in yamlv3 decoder is fixed

Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
@tomwilkie tomwilkie merged commit 870fe73 into cortexproject:master Aug 21, 2019
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.

None yet

5 participants