-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add metrics to track requests throttled in QueryThrottler #18740
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
Changes from 10 commits
e8e405d
4c738ce
71c159f
ac6a770
6963067
70db473
987f17a
bf7b785
db1fd04
71c0429
456e618
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,8 @@ import ( | |
| "sync" | ||
| "time" | ||
|
|
||
| "vitess.io/vitess/go/stats" | ||
| "vitess.io/vitess/go/vt/servenv" | ||
| "vitess.io/vitess/go/vt/sqlparser" | ||
|
|
||
| "vitess.io/vitess/go/vt/log" | ||
|
|
@@ -37,10 +39,18 @@ import ( | |
| ) | ||
|
|
||
| const ( | ||
| _queryThrottlerAppName = "QueryThrottler" | ||
| // defaultPriority is the default priority value when none is specified | ||
| defaultPriority = 100 // sqlparser.MaxPriorityValue | ||
| ) | ||
|
|
||
| type Stats struct { | ||
| requestsTotal *stats.CountersWithMultiLabels | ||
| requestsThrottled *stats.CountersWithMultiLabels | ||
| totalLatency *servenv.MultiTimingsWrapper | ||
| evaluateLatency *servenv.MultiTimingsWrapper | ||
| } | ||
|
|
||
| type QueryThrottler struct { | ||
| ctx context.Context | ||
| throttleClient *throttle.Client | ||
|
|
@@ -52,6 +62,8 @@ type QueryThrottler struct { | |
| cfgLoader ConfigLoader | ||
| // strategy is the current throttling strategy handler. | ||
| strategy registry.ThrottlingStrategyHandler | ||
| env tabletenv.Env | ||
| stats Stats | ||
| } | ||
|
|
||
| // NewQueryThrottler creates a new query throttler. | ||
|
|
@@ -65,6 +77,13 @@ func NewQueryThrottler(ctx context.Context, throttler *throttle.Throttler, cfgLo | |
| cfg: Config{}, | ||
| cfgLoader: cfgLoader, | ||
| strategy: ®istry.NoOpStrategy{}, // default strategy until config is loaded | ||
| env: env, | ||
| stats: Stats{ | ||
| requestsTotal: env.Exporter().NewCountersWithMultiLabels(_queryThrottlerAppName+"Requests", "query throttler requests", []string{"Strategy", "Workload", "Priority"}), | ||
| requestsThrottled: env.Exporter().NewCountersWithMultiLabels(_queryThrottlerAppName+"Throttled", "query throttler requests throttled", []string{"Strategy", "Workload", "Priority", "MetricName", "MetricValue", "DryRun"}), | ||
|
||
| totalLatency: env.Exporter().NewMultiTimings(_queryThrottlerAppName+"TotalLatencyNs", "Total time each request takes in query throttling including evaluation, metric checks, and other overhead (nanoseconds)", []string{"Strategy", "Workload", "Priority"}), | ||
| evaluateLatency: env.Exporter().NewMultiTimings(_queryThrottlerAppName+"EvaluateLatencyNs", "Time each request takes to make the throttling decision (nanoseconds)", []string{"Strategy", "Workload", "Priority"}), | ||
| }, | ||
| } | ||
|
|
||
| // Start the initial strategy | ||
|
|
@@ -97,27 +116,49 @@ func (qt *QueryThrottler) Shutdown() { | |
| func (qt *QueryThrottler) Throttle(ctx context.Context, tabletType topodatapb.TabletType, parsedQuery *sqlparser.ParsedQuery, transactionID int64, options *querypb.ExecuteOptions) error { | ||
| // Lock-free read: for maximum performance in the hot path as cfg and strategy are updated rarely (default once per minute). | ||
| // They are word-sized and safe for atomic reads; stale data for one query is acceptable and avoids mutex contention in the hot path. | ||
| if !qt.cfg.Enabled { | ||
| tCfg := qt.cfg | ||
| tStrategy := qt.strategy | ||
|
|
||
| if !tCfg.Enabled { | ||
| return nil | ||
| } | ||
|
|
||
| // Capture start time for latency measurements only when throttling is enabled | ||
| startTime := time.Now() | ||
|
|
||
| // Extract query attributes once to avoid re computation in strategies | ||
| attrs := registry.QueryAttributes{ | ||
| WorkloadName: extractWorkloadName(options), | ||
| Priority: extractPriority(options), | ||
| } | ||
| strategyName := tStrategy.GetStrategyName() | ||
| workload := attrs.WorkloadName | ||
| priorityStr := strconv.Itoa(attrs.Priority) | ||
|
|
||
| // Defer total latency recording to ensure it's always emitted regardless of return path. | ||
| defer func() { | ||
| qt.stats.totalLatency.Record([]string{strategyName, workload, priorityStr}, startTime) | ||
| }() | ||
|
|
||
| // Evaluate the throttling decision | ||
| decision := qt.strategy.Evaluate(ctx, tabletType, parsedQuery, transactionID, attrs) | ||
|
|
||
| // Record evaluate-window latency immediately after Evaluate returns | ||
| qt.stats.evaluateLatency.Record([]string{strategyName, workload, priorityStr}, startTime) | ||
|
|
||
| qt.stats.requestsTotal.Add([]string{strategyName, workload, priorityStr}, 1) | ||
|
|
||
| // If no throttling is needed, allow the query | ||
| if !decision.Throttle { | ||
| return nil | ||
| } | ||
|
|
||
| // Emit metric of query being throttled. | ||
| qt.stats.requestsThrottled.Add([]string{strategyName, workload, priorityStr, decision.MetricName, strconv.FormatFloat(decision.MetricValue, 'f', -1, 64), strconv.FormatBool(tCfg.DryRun)}, 1) | ||
|
|
||
| // If dry-run mode is enabled, log the decision but don't throttle | ||
| if qt.cfg.DryRun { | ||
| log.Warningf("[DRY-RUN] %s", decision.Message) | ||
| if tCfg.DryRun { | ||
| log.Warningf("[DRY-RUN] %s, metric name: %s, metric value: %f", decision.Message, decision.MetricName, decision.MetricValue) | ||
| return nil | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.