package qos import ( "errors" "fmt" "math" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/limits" ) var errWeightsMustBeSpecified = errors.New("invalid weights: weights must be specified for all tags or not specified for any") type tagConfig struct { Shares, Limit, Reserved *float64 } func validateConfig(c *limits.Config) error { if c.MaxReadRunningOps() <= 0 { return fmt.Errorf("invalid 'max_read_running_ops = %d': must be greater than zero", c.MaxReadRunningOps()) } if c.MaxReadWaitingOps() <= 0 { return fmt.Errorf("invalid 'max_read_waiting_ops = %d': must be greater than zero", c.MaxReadWaitingOps()) } if c.MaxWriteRunningOps() <= 0 { return fmt.Errorf("invalid 'max_write_running_ops = %d': must be greater than zero", c.MaxWriteRunningOps()) } if c.MaxWriteWaitingOps() <= 0 { return fmt.Errorf("invalid 'max_write_waiting_ops = %d': must be greater than zero", c.MaxWriteWaitingOps()) } if err := validateTags(c.ReadTags()); err != nil { return fmt.Errorf("'read' config validation error: %w", err) } if err := validateTags(c.WriteTags()); err != nil { return fmt.Errorf("'write' config validation error: %w", err) } return nil } func validateTags(configTags []limits.IOTagConfig) error { tags := map[IOTag]tagConfig{ IOTagClient: {}, IOTagInternal: {}, IOTagBackground: {}, IOTagWritecache: {}, IOTagPolicer: {}, } for _, t := range configTags { tag, err := FromRawString(t.Tag) if err != nil { return fmt.Errorf("invalid tag %s: %w", t.Tag, err) } if _, ok := tags[tag]; !ok { return fmt.Errorf("tag %s is not configurable", t.Tag) } tags[tag] = tagConfig{ Shares: t.Weight, Limit: t.LimitOps, Reserved: t.ReservedOps, } } idx := 0 var shares float64 for t, v := range tags { if idx == 0 { idx++ shares = float64Value(v.Shares) } else if (shares != 0 && float64Value(v.Shares) == 0) || (shares == 0 && float64Value(v.Shares) != 0) { return errWeightsMustBeSpecified } if float64Value(v.Shares) < 0 || math.IsNaN(float64Value(v.Shares)) { return fmt.Errorf("invalid weight for tag %s: must be positive value", t.String()) } if float64Value(v.Limit) < 0 || math.IsNaN(float64Value(v.Limit)) { return fmt.Errorf("invalid limit_ops for tag %s: must be positive value", t.String()) } if float64Value(v.Reserved) < 0 || math.IsNaN(float64Value(v.Reserved)) { return fmt.Errorf("invalid limit_ops for tag %s: must be positive value", t.String()) } } return nil } func float64Value(f *float64) float64 { if f == nil { return 0.0 } return *f } func isNoop(c *limits.Config) bool { return c.MaxReadRunningOps() == limits.NoLimit && c.MaxReadWaitingOps() == limits.NoLimit && c.MaxWriteRunningOps() == limits.NoLimit && c.MaxWriteWaitingOps() == limits.NoLimit && len(c.ReadTags()) == 0 && len(c.WriteTags()) == 0 }