# Dedicated topology synchronization

## Background

The Clinic cluster-list API reads the cached `clusters.topology` field, while
the cluster detail view has current dedicated component data. For
`prod-us-saas-gosaas-region-tidb-v85-05`, the cached list topology is stale
(`TiDB=7`, `TiKV=30`, `PD=3`, `TiFlash=3`) although the current dedicated
components report `TiDB=10` and `TiKV=21`.

The management-service conversion populates `ClusterComponent` for dedicated
clusters but does not populate `Topology`. The existing persistence path uses
struct updates, so a nil topology does not replace the historical database
value.

## Goal and contract

For dedicated clusters, derive the legacy topology summary from the same
management-service component payload used for component details. Persist that
summary when a cluster is created or updated. The summary contains TiDB, TiKV,
PD, and TiFlash replica counts and must represent removed optional components
as zero.

The change must not turn the update into a blanket full-row overwrite. Fields
not supplied by the management-service conversion, such as legacy network
metadata, must retain their existing behavior.

## Scope

- Root Go module: `pkg/datasource/utils` and its focused unit tests.
- Runtime owner: Clinic API (`cmd/server/main.go`), including the cluster event
  consumer and periodic management-cluster synchronization.
- Produced image: `clinic`.
- Clinic CD input: `components=clinic`.
- App-delivery application/workload: `clinic-api` / Clinic Deployment.
- Target environment for validation: staging. No production release is in
  scope for this change.

## Implementation

1. Add a small helper that derives `db.Topology` from dedicated component
   records using the same component categories as the component-detail
   conversion.
2. Set `Topology` from that helper in `MgmtClusterToModel` for dedicated
   clusters, including a non-nil all-zero topology when no dedicated
   components are present.
3. Add focused table-driven tests for multiple TiDB groups, optional TiFlash,
   and an empty component list.
4. Run the package test, inspect the diff, commit, push, and open an English
   PR targeting `master`.

## Exclusions and compatibility

- Do not change the persistence update mode to `Select("*")`.
- Do not backfill production records or dispatch a deployment in this change.
- Do not alter serverless or next-generation topology semantics.
- The existing JSON API keeps its `topology` shape; it will receive correct
  dedicated counts after the normal synchronization path runs.

## Validation and release boundary

Run `go test ./pkg/datasource/utils` from the root module. After PR creation,
staging validation requires an authorized Clinic CD dispatch from the feature
branch with `environment=staging` and `components=clinic`, followed by the
normal app-delivery and GitOps rollout checks. Merging and any production
release remain separately authorized actions.
