pingcap / pingcap/tidb

Expose RU calculation inputs, factors, and contributions in observability output

Open
#70,251 0 comments 1 reaction 0 assignees View on GitHub
component/executor component/observability component/pd type/enhancement
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Enhancement

### Problem

`RUDetails` currently exposes the final RU values, but not enough information to explain or reproduce how those values were calculated.

For RU v1, `rmpb.Consumption` already carries inputs such as read/write bytes, KV CPU time, and read/write RPC counts. However, `client-go`'s `RUDetails.Update` only retains `RRU`, `WRU`, and RU wait duration. The inputs are discarded before TiDB formats:

- `EXPLAIN ANALYZE`, which currently emits only `RU:` at the statement root;
- slow query logs and `INFORMATION_SCHEMA.SLOW_QUERY`, which currently expose RRU, WRU, and wait duration;
- statement-level observability consumers using `RUDetails`.

RU v2 preserves more raw counters through `RUV2Metrics`, and the slow log has `Request_unit_v2_detail`, but the calculation weights are not included. `EXPLAIN ANALYZE` still emits only the total RU.

This is a code-level follow-up to #47269. That issue described the same explainability problem but was closed after the formula was documented; the runtime output is still not self-contained or reproducible.

### Goal

Expose a versioned, statement-level RU calculation breakdown that contains the actual inputs, factors, and per-term contributions used to produce the final RU value.

The detail should be available in:

1. `EXPLAIN ANALYZE`, including text and `tidb_json` formats;
2. slow query logs;
3. `INFORMATION_SCHEMA.SLOW_QUERY`.

Existing numeric fields such as `Request_unit_read`, `Request_unit_write`, `Request_unit_v2`, and `RU:` should remain available for backward compatibility.

### Required semantics

For RU v1, the breakdown should make these calculations reproducible:

```text
RRU =
read RPC base contributions
+ read byte contributions
+ KV CPU contributions
+ paging settlement/refund contributions

WRU =
write RPC base contributions
+ replica-weighted write byte contributions
+ failed-request payback contributions
```

At minimum, record:

- RU model version and source (`tidb`, `tikv`, or `tiflash`);
- read/write RPC counts;
- read/write bytes actually used by the calculator;
- KV CPU milliseconds actually used by the calculator;
- replica-weighted write inputs;
- paging precharge, settlement, refund, and failed-request payback when applicable;
- the effective factors, including:
- read base cost;
- read per-batch base cost;
- read bytes cost;
- write base cost;
- write per-batch base cost;
- write bytes cost;
- CPU-ms cost;
- the batch proportion used by the model;
- the RU contribution of each term;
- a config revision or factor snapshot.

The following invariants should hold before formatting:

```text
RRU == sum(read-side RU contributions)
WRU == sum(write-side RU contributions)
```

If a statement spans multiple factor configurations, the output should contain separate configuration buckets or explicitly mark the detail as mixed. Applying one end-of-statement configuration snapshot to all accumulated inputs would produce misleading results.

For RU v2, include the weights used for the TiDB-side and TiKV-side counter calculations, so the existing raw counters can reproduce `tidb_ru` and `tikv_ru`. The top-level invariant should be:

```text
total_ru == tidb_ru + tikv_ru + tiflash_ru
```

### Suggested implementation boundaries

#### `pd/client`

Produce the detailed calculation delta at the same seam where `KVCalculator` computes request and response consumption. This is the only layer that simultaneously knows the actual factor snapshot, replica multiplier, paging adjustment, and per-request contribution.

Avoid deriving the detail from `ResourceGroupsController.GetConfig()` at statement completion. The active group calculator may retain a configuration captured when the group controller was created, while the controller's current configuration can be replaced by a config-watch update.

Prefer a backward-compatible optional detailed interceptor interface or another additive mechanism so existing `ResourceGroupKVInterceptor` implementations and test doubles are not forced to change immediately.

#### `client-go`

Extend `RUDetails` with a concurrency-safe calculation-detail accumulator and update both synchronous and asynchronous interceptor paths.

`Clone` and `Merge` must preserve the breakdown. The hot path should avoid per-RPC strings and unbounded allocations; formatting should happen only when the statement result is consumed.

`UpdateTiFlash` should preserve any raw detail supplied by TiFlash rather than retaining only RRU/WRU.

#### TiDB

Compose storage-side `RUDetails` with TiDB-side `RUV2Metrics` and weight snapshots in one formatter.

Suggested surfaces:

- append a compact, versioned `RU_detail` section to the root runtime stats in `EXPLAIN ANALYZE`;
- add a new slow-log field such as `Request_unit_detail`;
- add the corresponding long-text column to `INFORMATION_SCHEMA.SLOW_QUERY`;
- keep parsers tolerant of absent fields and future detail versions.

Statement-level detail is required for the first iteration. Exact per-plan-operator attribution is a separate problem because the current `RUDetails` object is shared by the whole statement and KV RPCs are not associated with individual plan operator IDs.

#### TiFlash

TiFlash currently returns a serialized `resource_manager.Consumption`. If complete TiFlash factor-level explainability is required, the TiFlash/tipb response must also carry the factor snapshot or contribution breakdown. Until then, TiFlash RU should be explicitly marked as an opaque contribution rather than presented as reproducible.

### Acceptance criteria

- A statement executed with non-default RU factors can be exactly reproduced from the emitted detail within the chosen floating-point tolerance.
- Read, write, retry/failure, replica-count, and paging precharge/refund paths have regression coverage.
- A statement spanning a factor update does not silently apply one factor set to all inputs.
- `EXPLAIN ANALYZE`, slow query logs, and `INFORMATION_SCHEMA.SLOW_QUERY` report consistent totals and breakdowns.
- RU v2 output includes the weights needed to reproduce TiDB and TiKV component totals.
- TiFlash detail is either reproducible from returned data or clearly marked opaque.
- Existing RU total fields retain their current meaning and remain backward compatible.
- The statement/RPC hot path does not introduce per-request formatting or significant allocation overhead.

### Alternatives considered

1. **Document the formula only.** This was the outcome of #47269, but it does not expose the exact runtime inputs or configuration used by a particular statement.
2. **Infer inputs from existing `EXPLAIN ANALYZE` execution statistics.** This is not reliable: the resource-control path can choose different read-byte semantics, applies replica multipliers, and performs paging settlement and failed-request payback that are not fully represented in the plan output.
3. **Attach the controller's current factors when the statement finishes.** This can be incorrect when configuration changes or when an active group calculator uses a different snapshot.

### User value

This makes RU consumption auditable and easier to reason about when:

- sizing resource groups from QPS and workload characteristics;
- investigating unexpectedly high RU usage;
- comparing query-plan changes;
- validating changes to RU factors;
- explaining slow queries and billing-adjacent runtime behavior.

Contributor guide

Open the contributing guide

Research direction

Start at the pd/client KVCalculator seam and trace how detailed consumption reaches client-go RUDetails. Read the synchronous and asynchronous interceptor paths, including Clone, Merge, and UpdateTiFlash, then follow TiDB's EXPLAIN ANALYZE and slow-query formatting with RUV2Metrics. Done means consistent, versioned breakdowns and regression coverage across the listed RU paths without changing existing totals.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
databases, observability
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.