# Premium SharedScope Multitenant Metrics Routing Plan

## Current Design

- Premium 的非 `[UserScope]` dashboard 会使用父 Resource Pool ID 作为 `var-tidb_cluster`，因此 `/metric/query` 实际加载的是 Resource Pool 记录。
- Premium UserScope 加载租户集群，SharedScope 加载 `deploy_type_v2=premium resource pool` 或 `premium reserved resource pool`，现有数据已足够区分两类请求。
- `o11y.ClusterInfo` 只接收 `internalReadUri`，尚未接收 O11y API 新增的 `multitenantQueryUri`。
- `ResolveMetricsTarget(..., child, ...)` 对所有集群统一返回 `internalReadUri`，导致 Premium SharedScope 查询进入错误的 Metrics endpoint。
- Metrics 反向代理只拼接目标 URI 的 path，没有保留目标 URI 自带的 query string。即使改用 `multitenantQueryUri`，其中用于限制 `vm_account_id` 的 `extra_filters[]` 仍会丢失。
- SharedScope dashboard 已使用 Resource Pool ID 和 `sharedpool_id="$tidb_cluster"`，无需修改 Grafana dashboard 或新增 datasource。

## Risks

- 将所有 Premium 请求统一切换到 `multitenantQueryUri` 会破坏 UserScope；只有 Premium Resource Pool 才属于本次 SharedScope 路由。
- `multitenantQueryUri` 的 query string 属于 O11y 路由和租户隔离契约，不能被 Grafana 请求覆盖或删除。
- 在 `multitenantQueryUri` 缺失时回退到 `internalReadUri` 会重新产生“HTTP 200 但没有数据”的隐性故障。
- 现有 `metrics_scope=child|parent` 用于 Starter/Essential 的 child/control-plane 路由，不能复用为 Premium SharedScope 标识。
- `docs/plans/metrics-target-unification.md` 中“child metrics 统一使用 internalReadUri”的约定需要收窄：Premium SharedScope Resource Pool 是明确例外，Premium UserScope 仍遵循原约定。

## Target Contract

- `/metric/query` 的公共接口、Grafana datasource 和 dashboard JSON 均保持不变。
- Premium SharedScope 继续通过父 Resource Pool ID 进入现有 Metrics 路由。
- `deploy_type_v2=premium resource pool` 或 `premium reserved resource pool` 时，使用 O11y 返回的 `multitenantQueryUri`。
- Premium、Premium Reserved 租户集群继续使用 `internalReadUri`。
- Essential V2、BYOC、Dedicated、Starter/Essential 等其他类型保持现有路由。
- `multitenantQueryUri` 缺失或无法解析时返回明确的 Metrics target resolution 错误，不回退到 `internalReadUri`。
- 转发 Prometheus API 时，在目标 base path 后追加 endpoint，保留 Grafana 请求参数和 URI 自带的多值 `extra_filters[]`，删除 `metrics_scope`；同名参数以目标 URI 参数为准。

## Public Interfaces and Types

- 在 `pkg/o11y.ClusterInfo` 增加 `MultitenantQueryUri string`，JSON 字段为 `multitenantQueryUri`。
- 不增加新的 HTTP endpoint、query parameter、cookie、数据库字段或 Grafana datasource。
- `MetricsTarget.MetricsURL` 继续作为 resolver 与 proxy builder 之间的统一目标 URI，允许其中包含 query string。
- 本次不接入 `diagnosisQueryUri`。

## Implementation Steps

1. 扩展 O11y cluster response model，使 `GET /api/v1/clusters` 返回的 `multitenantQueryUri` 能被 Clinic 反序列化并保留。
2. 在 `pkg/o11y/urls` 增加 Premium SharedScope Resource Pool 判定，只匹配 Premium 与 Premium Reserved Resource Pool。
3. 调整 `resolveChildMetricsTarget`：Premium SharedScope Resource Pool 返回 `MultitenantQueryUri`，其他类型返回 `InternalReadUri`，两种 URI 缺失情况分别返回可定位的错误。
4. 调整 `buildProxyForMetricsTarget`，解析和合并目标 URI 的 path/query，保留多值固定参数并禁止请求覆盖同名固定参数。
5. 保留现有 O11y header 注入、BYOC header、权限校验和 response header 行为。
6. 增加 resolver 和 proxy 单元测试，更新原先断言 Premium Resource Pool 使用 `internalReadUri` 的测试契约。
7. 不修改 `clinic-grafana`。

## Verification Checklist

- O11y JSON fixture 能正确解析 `multitenantQueryUri`。
- Premium/Premium Reserved Resource Pool 使用 `multitenantQueryUri`。
- Premium/Premium Reserved UserScope 继续使用 `internalReadUri`。
- Essential V2 Resource Pool、BYOC Resource Pool、Dedicated 等类型路由不变。
- Premium SharedScope 缺少 `multitenantQueryUri` 时返回明确错误。
- `/query_range` 同时保留 Grafana 参数和 O11y 的全部 `extra_filters[]`，且请求不能覆盖固定参数。
- `metrics_scope` 不会转发给 O11y。
- BYOC/O11y header 注入的现有测试继续通过。
- 运行 `go test ./pkg/o11y/urls`、`go test ./pkg/proxy` 和 `go test ./pkg/o11y/...`。
- Staging 验证 SharedScope 使用 multitenant path 且面板返回数据，UserScope 仍使用 `internalReadUri`。

## Non-Goals

- 不修改或批量重写 `clinic-grafana` dashboard。
- 不新增 SharedScope 专用 Grafana datasource。
- 不通过 dashboard 标题、slug 或 Referer 判断 SharedScope。
- 不改变 Starter/Essential 的 `metrics_scope=child|parent` 语义。
- 不把 BYOC、Essential V2 Resource Pool 自动扩展到 multitenant 路由。
- 不修改 Logs、Slow Query、TopSQL、Statements 或 NGM 路由。
- 不清理旧的 Premium proxy dead code。
