mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-11 05:47:27 -08:00
Refactor concurrency control
Signed-off-by: Danny Kopping <danny.kopping@grafana.com>
This commit is contained in:
parent
ed2933ca60
commit
e7758d187e
|
@ -769,6 +769,7 @@ func main() {
|
||||||
ForGracePeriod: time.Duration(cfg.forGracePeriod),
|
ForGracePeriod: time.Duration(cfg.forGracePeriod),
|
||||||
ResendDelay: time.Duration(cfg.resendDelay),
|
ResendDelay: time.Duration(cfg.resendDelay),
|
||||||
MaxConcurrentEvals: cfg.maxConcurrentEvals,
|
MaxConcurrentEvals: cfg.maxConcurrentEvals,
|
||||||
|
ConcurrentEvalsEnabled: cfg.enableConcurrentRuleEval,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -435,11 +435,12 @@ func (g *Group) Eval(ctx context.Context, ts time.Time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
eval := func(i int, rule Rule, async bool) {
|
eval := func(i int, rule Rule, async bool) {
|
||||||
if async {
|
|
||||||
defer func() {
|
defer func() {
|
||||||
g.opts.ConcurrentEvalSema.Release(1)
|
if async {
|
||||||
}()
|
g.opts.ConcurrencyController.Done()
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
logger := log.WithPrefix(g.logger, "name", rule.Name(), "index", i)
|
logger := log.WithPrefix(g.logger, "name", rule.Name(), "index", i)
|
||||||
ctx, sp := otel.Tracer("").Start(ctx, "rule")
|
ctx, sp := otel.Tracer("").Start(ctx, "rule")
|
||||||
sp.SetAttributes(attribute.String("name", rule.Name()))
|
sp.SetAttributes(attribute.String("name", rule.Name()))
|
||||||
|
@ -568,7 +569,7 @@ func (g *Group) Eval(ctx context.Context, ts time.Time) {
|
||||||
|
|
||||||
// If the rule has no dependencies, it can run concurrently because no other rules in this group depend on its output.
|
// If the rule has no dependencies, it can run concurrently because no other rules in this group depend on its output.
|
||||||
// Try run concurrently if there are slots available.
|
// Try run concurrently if there are slots available.
|
||||||
if g.dependencyMap.isIndependent(rule) && g.opts.ConcurrentEvalSema != nil && g.opts.ConcurrentEvalSema.TryAcquire(1) {
|
if g.dependencyMap.isIndependent(rule) && g.opts.ConcurrencyController.Allow() {
|
||||||
go eval(i, rule, true)
|
go eval(i, rule, true)
|
||||||
} else {
|
} else {
|
||||||
eval(i, rule, false)
|
eval(i, rule, false)
|
||||||
|
|
|
@ -116,7 +116,8 @@ type ManagerOptions struct {
|
||||||
ForGracePeriod time.Duration
|
ForGracePeriod time.Duration
|
||||||
ResendDelay time.Duration
|
ResendDelay time.Duration
|
||||||
MaxConcurrentEvals int64
|
MaxConcurrentEvals int64
|
||||||
ConcurrentEvalSema *semaphore.Weighted
|
ConcurrentEvalsEnabled bool
|
||||||
|
ConcurrencyController ConcurrencyController
|
||||||
GroupLoader GroupLoader
|
GroupLoader GroupLoader
|
||||||
|
|
||||||
Metrics *Metrics
|
Metrics *Metrics
|
||||||
|
@ -133,7 +134,7 @@ func NewManager(o *ManagerOptions) *Manager {
|
||||||
o.GroupLoader = FileLoader{}
|
o.GroupLoader = FileLoader{}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.ConcurrentEvalSema = semaphore.NewWeighted(o.MaxConcurrentEvals)
|
o.ConcurrencyController = NewConcurrencyController(o.ConcurrentEvalsEnabled, o.MaxConcurrentEvals)
|
||||||
|
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
groups: map[string]*Group{},
|
groups: map[string]*Group{},
|
||||||
|
@ -408,3 +409,28 @@ func SendAlerts(s Sender, externalURL string) NotifyFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConcurrencyController struct {
|
||||||
|
enabled bool
|
||||||
|
sema *semaphore.Weighted
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConcurrencyController(enabled bool, maxConcurrency int64) ConcurrencyController {
|
||||||
|
return ConcurrencyController{enabled: enabled, sema: semaphore.NewWeighted(maxConcurrency)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ConcurrencyController) Allow() bool {
|
||||||
|
if !c.enabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.sema.TryAcquire(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ConcurrencyController) Done() {
|
||||||
|
if !c.enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.sema.Release(1)
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
"golang.org/x/sync/semaphore"
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/model/labels"
|
"github.com/prometheus/prometheus/model/labels"
|
||||||
|
@ -1672,7 +1671,7 @@ func TestAsyncRuleEvaluation(t *testing.T) {
|
||||||
|
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
// Allow up to 2 concurrent rule evaluations.
|
// Allow up to 2 concurrent rule evaluations.
|
||||||
group.opts.ConcurrentEvalSema = semaphore.NewWeighted(2)
|
group.opts.ConcurrencyController = NewConcurrencyController(true, 2)
|
||||||
require.Len(t, group.rules, expectedRules)
|
require.Len(t, group.rules, expectedRules)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -1725,6 +1724,7 @@ func TestBoundedRuleEvalConcurrency(t *testing.T) {
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Logger: log.NewNopLogger(),
|
Logger: log.NewNopLogger(),
|
||||||
Appendable: storage,
|
Appendable: storage,
|
||||||
|
ConcurrentEvalsEnabled: true,
|
||||||
MaxConcurrentEvals: maxConcurrency,
|
MaxConcurrentEvals: maxConcurrency,
|
||||||
QueryFunc: func(ctx context.Context, q string, ts time.Time) (promql.Vector, error) {
|
QueryFunc: func(ctx context.Context, q string, ts time.Time) (promql.Vector, error) {
|
||||||
inflightQueries.Add(1)
|
inflightQueries.Add(1)
|
||||||
|
|
Loading…
Reference in a new issue