#!/usr/bin/env python3 """Generate the Clinic Shared Resource One Page Grafana dashboard. This source is the only hand-maintained dashboard definition. Regenerate the adjacent JSON after editing it: python3 shared_resource_diagnosis.py -o shared_resource_diagnosis.json """ from __future__ import annotations import argparse import json from dataclasses import dataclass from common import ( Layout, ) from common import heatmap_panel as common_heatmap_panel from common import stat_panel as common_stat_panel from common import target as common_target from common import ( timeseries_panel, yaxis, ) from grafanalib._gen import DashboardEncoder from grafanalib.core import GridPos, Panel, Target DS = "${DS_TEST-CLUSTER}" DATASOURCE = DS RATE = "$__rate_interval" TIKV = ( 'k8s_cluster="$k8s_cluster",tidb_cluster="$tidb_cluster",instance=~"$tikv_instance"' ) LOGICAL = 'sharedpool_id="$tidb_cluster",tidb_cluster_id=~"$cluster_id"' TIDB_PROCESS = 'sharedpool_id="$tidb_cluster",component=~"worker-tidb|tidb",tidb_cluster_id=~"$cluster_id"' PD = 'k8s_cluster="$k8s_cluster",tidb_cluster="$tidb_cluster",keyspace_name=~"$cluster_id"' FAIR = TIKV + ',name=~"unified-read.*",keyspace_id=~"$keyspace_id"' def target( expr: str, legend: str = "", instant: bool = False, ref_id: str = "A" ) -> Target: # Convert local placeholders to the Clinic physical-cluster selector. expr = expr.replace('job="tidb-tikv",tidb_cluster=~"[0-9]+"', TIKV) result = common_target(expr=expr, legend_format=legend, data_source=DATASOURCE) result.instant = instant result.refId = ref_id return result def panel_json(panel) -> dict: """Serialize a grafanalib panel while keeping this dashboard as plain JSON.""" result = json.loads(json.dumps(panel, cls=DashboardEncoder)) defaults = result.get("fieldConfig", {}).get("defaults", {}) thresholds = getattr(panel, "_shared_thresholds", None) if thresholds: defaults["thresholds"] = {"mode": "absolute", "steps": thresholds} if getattr(panel, "_shared_bars", False): defaults["custom"]["showPoints"] = "never" defaults["custom"]["stacking"] = {"mode": "normal", "group": "A"} if "fieldConfig" in result and hasattr(panel, "_shared_overrides"): result["fieldConfig"]["overrides"] = panel._shared_overrides return result def set_layout_width(panel: Panel, width: int) -> Panel: panel._shared_layout_width = width return panel def panel( title: str, width: int, targets: list[Target], *, unit: str = "short", description: str = "", thresholds: list[dict] | None = None, legend_placement: str = "right", overrides: list[dict] | None = None, bars: bool = False, ) -> Panel: result = timeseries_panel( title=title, targets=targets, description=description, unit=unit, draw_style="bars" if bars else "line", line_width=0 if bars else 1, fill_opacity=80 if bars else 0, gradient_mode="none", legend_calcs=[], legend_display_mode="list", legend_placement=legend_placement, data_source=DATASOURCE, ) result._shared_thresholds = thresholds result._shared_overrides = overrides or [] result._shared_bars = bars return set_layout_width(result, width) def stat( title: str, expr: str, unit: str = "short", width: int = 6, ) -> Panel: result = common_stat_panel( title=title, targets=[target(expr)], description="", format=unit, graph_mode="none", data_source=DATASOURCE, ) result.orientation = "horizontal" return set_layout_width(result, width) @dataclass(frozen=True) class Section: title: str def heatmap_panel(title: str, width: int, expr: str, description: str) -> Panel: """Prometheus native-histogram bucket view used by the #5534 wait-count panel.""" heatmap_target = target(expr, "{{le}}") heatmap_target.format = "heatmap" result_panel = common_heatmap_panel( title=title, metric="tikv_yatp_pool_keyspace_fair_queue_task_slice_waits_bucket", description=description, y_axis=yaxis("short", decimals=0), data_source=DATASOURCE, skip_default_instance=True, ) result_panel.targets = [heatmap_target] return set_layout_width(result_panel, width) def render_panels(items: list[Section | Panel]) -> list[dict]: """Lay out dashboard rows from panel order and relative width hints.""" layouts: list[Layout] = [] current: Layout | None = None row: list[Panel] = [] row_width = 0 def flush_row() -> None: nonlocal row, row_width if not row: return assert current is not None assert row_width == Layout.ROW_WIDTH, f"incomplete dashboard row: {row_width}" assert len({panel._shared_layout_width for panel in row}) == 1 current.row(row) row = [] row_width = 0 for item in items: if isinstance(item, Section): flush_row() current = Layout(title=item.title, collapsed=False) layouts.append(current) continue assert current is not None, "a section must precede its panels" width = item._shared_layout_width if row_width + width > Layout.ROW_WIDTH: flush_row() row.append(item) row_width += width if row_width == Layout.ROW_WIDTH: flush_row() flush_row() result: list[dict] = [] next_y = 0 next_id = 1 for layout in layouts: row_panel = layout.row_panel children = list(row_panel.panels) row_panel.panels = [] row_panel.id = next_id next_id += 1 row_panel.gridPos = GridPos(x=0, y=next_y, w=Layout.ROW_WIDTH, h=1) result.append(panel_json(row_panel)) for child in children: child.id = next_id next_id += 1 child.gridPos = GridPos( x=child.gridPos.x, y=child.gridPos.y + next_y + 1, w=child.gridPos.w, h=child.gridPos.h, ) result.append(panel_json(child)) next_y += layout.current_row_y_pos + 1 return result def dashboard() -> dict: latency_thresholds = [ {"color": "green", "value": None}, {"color": "yellow", "value": 0.1}, {"color": "red", "value": 0.5}, ] panels = [ Section("TiDB By ClusterID"), stat( "Clusters", "count(count by (tidb_cluster_id) (tidb_server_query_total{" + LOGICAL + "}))", width=4, ), stat( "QPS", "sum(rate(tidb_server_query_total{" + LOGICAL + ',result="OK"}[' + RATE + "]))", "ops", 4, ), stat( "P99 latency", "histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket{" + LOGICAL + "}[" + RATE + "])) by (le))", "s", 4, ), stat( "Error QPS", "sum(rate(tidb_server_query_total{" + LOGICAL + ',result="Error"}[' + RATE + "]))", "ops", 4, ), stat( "RU / s", "sum(rate(resource_manager_resource_unit_read_request_unit_sum{" + PD + ',type="tp"}[' + RATE + "])) + sum(rate(resource_manager_resource_unit_write_request_unit_sum{" + PD + ',type="tp"}[' + RATE + "]))", "ops", 4, ), stat( "Connections", "sum(tidb_server_connections{" + LOGICAL + "})", "short", 4, ), panel( "TiDB Server CPU utilization by cluster", 12, [ target( "topk(10, sum by (tidb_cluster_id) (rate(process_cpu_seconds_total{" + TIDB_PROCESS + "}[" + RATE + "])) / sum by (tidb_cluster_id) (tidb_server_maxprocs{" + LOGICAL + "}))", "cluster-{{tidb_cluster_id}}", ) ], unit="percentunit", description="TiDB process CPU divided by its GOMAXPROCS quota for each logical cluster. 1.0 is displayed as 100%.", thresholds=[ {"color": "green", "value": None}, {"color": "yellow", "value": 0.7}, {"color": "red", "value": 0.9}, ], ), panel( "TiDB Server status by cluster", 12, [ target( "topk(10, sum by (tidb_cluster_id) (up{" + TIDB_PROCESS + "}))", "up · cluster-{{tidb_cluster_id}}", ), target( "topk(10, count by (tidb_cluster_id) (up{" + TIDB_PROCESS + "}) - sum by (tidb_cluster_id) (up{" + TIDB_PROCESS + "}))", "down · cluster-{{tidb_cluster_id}}", ref_id="B", ), ], unit="short", description="Healthy and unavailable TiDB Server scrape targets by logical cluster. Any down series above zero requires investigation.", overrides=[ { "matcher": {"id": "byRegexp", "options": "/^up ·/"}, "properties": [ { "id": "color", "value": {"mode": "fixed", "fixedColor": "green"}, } ], }, { "matcher": {"id": "byRegexp", "options": "/^down ·/"}, "properties": [ {"id": "color", "value": {"mode": "fixed", "fixedColor": "red"}} ], }, ], ), panel( "QPS by cluster", 12, [ target( "topk(10, sum by (tidb_cluster_id) (rate(tidb_server_query_total{" + LOGICAL + ',result="OK"}[' + RATE + "])))", "cluster-{{tidb_cluster_id}}", ) ], unit="ops", description="Successful SQL throughput by logical cluster. All-cluster views are capped at Top 10; select a cluster for exact drill-down.", ), panel( "Latency by cluster", 12, [ target( "topk(10, histogram_quantile(0.99, sum(rate(tidb_server_handle_query_duration_seconds_bucket{" + LOGICAL + "}[" + RATE + "])) by (tidb_cluster_id, le)))", "P99 · cluster-{{tidb_cluster_id}}", ), target( "topk(10, histogram_quantile(0.90, sum(rate(tidb_server_handle_query_duration_seconds_bucket{" + LOGICAL + "}[" + RATE + "])) by (tidb_cluster_id, le)))", "P90 · cluster-{{tidb_cluster_id}}", ref_id="B", ), ], unit="s", description="Operational thresholds: green below 100 ms, yellow below 500 ms, red at or above 500 ms. Tune to the cluster SLA when one is defined.", thresholds=latency_thresholds, ), panel( "Error QPS by cluster", 12, [ target( "topk(10, sum by (tidb_cluster_id) (rate(tidb_server_query_total{" + LOGICAL + ',result="Error"}[' + RATE + "])))", "cluster-{{tidb_cluster_id}}", ) ], unit="ops", description="Failed SQL throughput by logical cluster, capped at Top 10 for the all-cluster view.", bars=True, ), panel( "Slow queries by cluster", 12, [ target( "topk(10, sum by (tidb_cluster_id) (rate(tidb_server_slow_query_process_duration_seconds_count{" + LOGICAL + "}[" + RATE + "])))", "cluster-{{tidb_cluster_id}}", ) ], unit="ops", description="New slow queries per second by logical cluster, capped at Top 10 for the all-cluster view.", bars=True, ), panel( "Affected rows by cluster", 12, [ target( "topk(10, sum by (tidb_cluster_id) (rate(tidb_executor_affected_rows{" + LOGICAL + "}[" + RATE + "])))", "cluster-{{tidb_cluster_id}}", ) ], unit="rows/s", description="Rows changed by each logical cluster across SQL types, capped at Top 10.", ), panel( "Idle in transaction P99", 12, [ target( "topk(10, histogram_quantile(0.99, sum by (tidb_cluster_id, le) (rate(tidb_server_conn_idle_duration_seconds_bucket{" + LOGICAL + ',in_txn="1"}[' + RATE + "]))))", "cluster-{{tidb_cluster_id}}", ) ], unit="s", description="P99 connection idle duration while a transaction remains open, by logical cluster.", thresholds=latency_thresholds, ), panel( "RU read / write by cluster", 8, [ target( "topk(10, sum by (keyspace_name) (rate(resource_manager_resource_unit_read_request_unit_sum{" + PD + ',type="tp"}[' + RATE + "])))", "read · keyspace-{{keyspace_name}}", ), target( "topk(10, sum by (keyspace_name) (rate(resource_manager_resource_unit_write_request_unit_sum{" + PD + ',type="tp"}[' + RATE + "])))", "write · keyspace-{{keyspace_name}}", ref_id="B", ), ], unit="ops", description="Read and write RU consumption rate by logical cluster in the shared PD resource manager.", ), panel( "RU / query by cluster", 8, [ target( "topk(10, (sum by (keyspace_name) (rate(resource_manager_resource_unit_read_request_unit_sum{" + PD + ',type="tp"}[' + RATE + "]) + rate(resource_manager_resource_unit_write_request_unit_sum{" + PD + ',type="tp"}[' + RATE + "]))) / on (keyspace_name) sum by (keyspace_name) (rate(tidb_session_resource_group_query_total{" + LOGICAL + "}[" + RATE + "])))", "keyspace-{{keyspace_name}}", ) ], unit="short", description="RU efficiency by logical cluster. A rise while QPS is stable points to a more expensive query mix.", ), panel( "Cop response throughput by cluster", 8, [ target( "topk(10, sum by (tidb_cluster_id) (rate(tidb_ruv2_tikv_coprocessor_response_bytes{" + LOGICAL + "}[" + RATE + "])))", "cluster-{{tidb_cluster_id}}", ) ], unit="Bps", description="Logical coprocessor response bytes returned from TiKV to TiDB by cluster. This is request-level logical throughput, not physical disk IO.", ), Section("TiKV Overview"), panel( "TiKV CPU", 12, [ target( 'sum by (instance) (rate(process_cpu_seconds_total{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + '])) / on (instance) group_left sum by (instance) (tikv_server_cpu_cores_quota{job="tidb-tikv",tidb_cluster=~"[0-9]+"})', "{{instance}}", ) ], unit="percentunit", description="Whole-process CPU utilisation against TiKV's configured CPU-core quota, by instance.", thresholds=[ {"color": "green", "value": None}, {"color": "yellow", "value": 0.7}, {"color": "red", "value": 0.9}, ], ), panel( "TiKV gRPC latency P99", 12, [ target( 'topk(12, histogram_quantile(0.99, sum by (type, le) (rate(tikv_grpc_msg_duration_seconds_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+",type!="kv_gc"}[' + RATE + "]))))", "{{type}}", ) ], unit="s", description="P99 execution latency of TiKV gRPC message types, matching TiKV Details gRPC message duration.", thresholds=latency_thresholds, ), panel( "IO throughput breakdown", 12, [ target( 'topk(10, sum by (op, type) (rate(tikv_io_bytes{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "])) > 0)", "{{op}} · {{type}}", ) ], unit="Bps", description="TiKV engine IO throughput split by operation and workload source. Top 10 active contributors only.", ), panel( "Storage & IO wait P99", 12, [ target( 'histogram_quantile(0.99, sum by (type, le) (rate(tikv_storage_engine_async_request_duration_seconds_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+",type=~"snapshot_read_index_propose_wait|snapshot_read_index_confirm|write"}[' + RATE + "])))", "storage · {{type}}", ), target( 'histogram_quantile(0.99, sum by (le) (rate(tikv_raftstore_store_write_task_wait_duration_secs_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "])))", "write task wait", ref_id="B", ), target( 'histogram_quantile(0.99, sum by (le) (rate(tikv_raftstore_store_min_write_pause_duration_seconds_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "])))", "minimum write pause", ref_id="C", ), ], unit="s", description="P99 storage request and write-path waiting latency. Use it with IO throughput to identify storage saturation and write backpressure.", thresholds=latency_thresholds, ), panel( "Thread pool wait P99", 12, [ target( '(histogram_quantile(0.99, sum by (name, le) (rate(tikv_yatp_pool_schedule_wait_duration_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "]))) or vector(0))", "{{name}}", ) ], unit="s", description="Storage-side queueing by TiKV YATP pool. Use this to identify CPU/thread-pool saturation in the shared physical pool.", thresholds=latency_thresholds, ), panel( "Rate-limit wait P99", 12, [ target( '(histogram_quantile(0.99, sum by (type, le) (rate(tikv_rate_limiter_request_wait_duration_seconds_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "]))) or vector(0))", "{{type}}", ) ], unit="s", description="Wait introduced by TiKV rate limiting, grouped by priority class. This is a shared-pool throttling signal.", thresholds=latency_thresholds, ), Section("TiKV · Unified Read Pool & Fairness"), stat( "Read CPU cores", "sum(rate(tikv_yatp_pool_keyspace_task_slice_cpu_duration_seconds_sum{" + FAIR + "}[" + RATE + "]))", "cores", ), stat( "Active keyspaces", "count(sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_task_slice_cpu_duration_seconds_sum{" + FAIR + "}[" + RATE + "])) > 0)", ), stat( "Slice wait P99", "histogram_quantile(0.99, sum by (le) (rate(tikv_yatp_pool_keyspace_fair_queue_task_slice_wait_duration_seconds_bucket{" + FAIR + "}[" + RATE + "])))", "s", ), stat( "Max groups / TiKV", 'max(max by (instance) (tikv_yatp_pool_keyspace_fair_queue_group_count{job="tidb-tikv",tidb_cluster=~"[0-9]+",name=~"unified-read.*"}))', ), panel( "Running tasks", 24, [ target( 'sum by (instance) (tikv_unified_read_pool_running_tasks{job="tidb-tikv",tidb_cluster=~"[0-9]+"})', "{{instance}}", ) ], unit="short", description="Instantaneous tasks executing in each TiKV unified read pool. This identifies which physical store is busy; it cannot yet attribute CPU to a logical cluster or SQL pattern.", ), panel( "Coprocessor queue P99", 12, [ target( 'topk(12, histogram_quantile(0.99, sum by (req, le) (rate(tikv_coprocessor_request_wait_seconds_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "]))) )", "{{req}}", ) ], unit="s", description="Request-class queueing in TiKV. This is the current best signal for anomalous internal work (for example analyse/checksum-style classes), but the req label does not establish logical-cluster ownership.", thresholds=latency_thresholds, ), panel( "Coprocessor exec P99", 12, [ target( 'topk(12, histogram_quantile(0.99, sum by (req, le) (rate(tikv_coprocessor_request_duration_seconds_bucket{job="tidb-tikv",tidb_cluster=~"[0-9]+"}[' + RATE + "]))) )", "{{req}}", ) ], unit="s", description="Execution latency by bounded TiKV request class. Pair it with the queue-wait panel to distinguish CPU/pool contention from expensive execution.", thresholds=latency_thresholds, ), panel( "CPU / wall · Top 10", 12, [ target( "topk(10, sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_task_slice_cpu_duration_seconds_sum{" + FAIR + "}[" + RATE + "])))", "CPU · KSID-{{keyspace_id}}", ), target( "topk(10, sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_task_wall_seconds_total{" + FAIR + "}[" + RATE + "])))", "wall · KSID-{{keyspace_id}}", ref_id="B", ), ], unit="short", description="#5534 CPU and wall time rate for selected unified-read keyspaces. All-keyspace views are capped at Top 10 to control series cardinality.", legend_placement="bottom", ), panel( "Slice wait · P99 / avg", 12, [ target( "topk(10, histogram_quantile(0.99, sum by (keyspace_id, le) (rate(tikv_yatp_pool_keyspace_fair_queue_task_slice_wait_duration_seconds_bucket{" + FAIR + "}[" + RATE + "]))))", "P99 · KSID-{{keyspace_id}}", ), target( "topk(10, sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_fair_queue_task_slice_wait_duration_seconds_sum{" + FAIR + "}[" + RATE + "])) / sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_fair_queue_task_slice_wait_duration_seconds_count{" + FAIR + "}[" + RATE + "])))", "avg · KSID-{{keyspace_id}}", ref_id="B", ), ], unit="s", description="#5534 P99 and average time from enqueueing a task slice until the fair queue pops it.", thresholds=latency_thresholds, ), panel( "Queue depth · P99 / avg", 12, [ target( "topk(10, histogram_quantile(0.99, sum by (keyspace_id, le) (rate(tikv_yatp_pool_keyspace_fair_queue_length_bucket{" + FAIR + "}[" + RATE + "]))))", "P99 · KSID-{{keyspace_id}}", ), target( "topk(10, sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_fair_queue_length_sum{" + FAIR + "}[" + RATE + "])) / sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_fair_queue_length_count{" + FAIR + "}[" + RATE + "])))", "avg · KSID-{{keyspace_id}}", ref_id="B", ), ], unit="short", description="#5534 P99 and average queue depth observed when a task slice enters the fair queue.", ), heatmap_panel( "Task slice waits · distribution", 12, "sum(increase(tikv_yatp_pool_keyspace_fair_queue_task_slice_waits_bucket{" + FAIR + "}[" + RATE + "])) by (le)", "#5534 distribution of other successful task-slice pops seen while selected keyspace slices wait. Multiple selected keyspaces are aggregated.", ), panel( "CPU / wall per slice", 12, [ target( "(sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_task_slice_cpu_duration_seconds_sum{" + FAIR + "}[" + RATE + "])) / sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_fair_queue_task_slice_wait_duration_seconds_count{" + FAIR + "}[" + RATE + "]))) * 1000", "CPU · KSID-{{keyspace_id}}", ), target( "(sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_task_wall_seconds_total{" + FAIR + "}[" + RATE + "])) / sum by (keyspace_id) (rate(tikv_yatp_pool_keyspace_fair_queue_task_slice_wait_duration_seconds_count{" + FAIR + "}[" + RATE + "]))) * 1000", "wall · KSID-{{keyspace_id}}", ref_id="B", ), ], unit="ms", description="#5534 average CPU and wall time consumed by each popped unified-read task slice.", legend_placement="bottom", ), panel( "Active groups by TiKV", 12, [ target( 'max by (instance) (tikv_yatp_pool_keyspace_fair_queue_group_count{job="tidb-tikv",tidb_cluster=~"[0-9]+",name=~"unified-read.*"})', "{{instance}}", ) ], unit="short", description="#5534 active keyspace groups in each TiKV unified-read fair queue. This is a scheduling-scale guardrail, not CPU usage.", ), ] return { "uid": "shared-resource-diagnosis", "title": "[SharedScope]Shared Resource One Page", "tags": ["cluster", "shared-resource", "resource-control", "tidb", "tikv"], "timezone": "browser", "schemaVersion": 41, "version": 1, "refresh": "1m", "time": {"from": "now-1h", "to": "now"}, "templating": { "list": [ { "name": "k8s_cluster", "label": "K8s cluster", "type": "query", "hide": 2, "datasource": DATASOURCE, "definition": "label_values(tikv_engine_size_bytes, k8s_cluster)", "query": { "query": "label_values(tikv_engine_size_bytes, k8s_cluster)", "refId": "StandardVariableQuery", }, "includeAll": False, }, { "name": "cluster_id", "label": "Logical Cluster", "type": "query", "datasource": DATASOURCE, "definition": 'label_values(tidb_server_query_total{sharedpool_id="$tidb_cluster"}, tidb_cluster_id)', "query": { "query": 'label_values(tidb_server_query_total{sharedpool_id="$tidb_cluster"}, tidb_cluster_id)', "refId": "StandardVariableQuery", }, "includeAll": True, "allValue": ".+", "multi": True, "current": {"text": "All", "value": ["$__all"]}, }, { "name": "keyspace_id", "label": "Keyspace ID", "type": "query", "datasource": DATASOURCE, "definition": 'label_values({__name__=~"tikv_cpu_token_wait_duration_seconds_count|tikv_yatp_pool_keyspace_task_slice_cpu_duration_seconds_count",k8s_cluster="$k8s_cluster",tidb_cluster="$tidb_cluster"}, keyspace_id)', "query": { "query": 'label_values({__name__=~"tikv_cpu_token_wait_duration_seconds_count|tikv_yatp_pool_keyspace_task_slice_cpu_duration_seconds_count",k8s_cluster="$k8s_cluster",tidb_cluster="$tidb_cluster"}, keyspace_id)', "refId": "StandardVariableQuery", }, "includeAll": True, "allValue": ".*", "multi": True, "current": {"text": "All", "value": ["$__all"]}, "sort": 1, }, { "name": "tikv_instance", "label": "TiKV instance", "type": "query", "datasource": DATASOURCE, "definition": 'label_values(tikv_engine_size_bytes{k8s_cluster="$k8s_cluster",tidb_cluster="$tidb_cluster"}, instance)', "query": { "query": 'label_values(tikv_engine_size_bytes{k8s_cluster="$k8s_cluster",tidb_cluster="$tidb_cluster"}, instance)', "refId": "StandardVariableQuery", }, "includeAll": True, "allValue": ".*", "multi": True, "current": {"text": "All", "value": ["$__all"]}, "sort": 1, }, { "name": "shared_resource_scope", "label": "Shared resource scope", "type": "constant", "hide": 2, "query": "resource_pool", "current": { "selected": True, "text": "resource_pool", "value": "resource_pool", }, "options": [ { "selected": True, "text": "resource_pool", "value": "resource_pool", } ], "skipUrlSync": False, }, ] }, "panels": render_panels(panels), "__inputs": [ { "name": "DS_TEST-CLUSTER", "label": "Prometheus", "type": "datasource", "pluginId": "prometheus", "pluginName": "Prometheus", } ], } if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-o", "--output", required=True) args = parser.parse_args() with open(args.output, "w") as output: json.dump(dashboard(), output, indent=2, ensure_ascii=False) output.write("\n")