# Dashboard Legacy Deploy Type Query

## Background and Goal

Production access logs show a legacy dashboard caller sending the exact
`deploy_type=dedicated&deploy_type=byoc` pair. The legacy column is not indexed,
and a simple conversion to a multi-value `deploy_type_v2 IN (...)` predicate
still makes MySQL scan many recent rows for the default created-at page.

The goal is to preserve the two dashboard API contracts while making sparse
multi-type list shapes use the existing `(deploy_type_v2, created_at)` index
efficiently.

## Current Behavior

- `/clinic/api/v1/dashboard/clusters` and `/clusters2` share
  `datasource.ListOrganizationAndClusterPage`.
- Legacy and v2 deploy-type filters are separate predicates when both are
  explicitly supplied.
- The default list is ordered by `created_at` and supports either an exact
  total or `has_more` pagination.
- Numeric search already builds indexed UNION branches.

## Target Contract

- Convert only legacy values with exact one-to-one mappings: `dedicated` and
  `byoc`. Ambiguous or explicitly combined legacy/v2 filters remain unchanged.
- For two or more canonical v2 values that do not include `starter`, with no
  text query, no companion filters other than the dashboard's normal
  deleted-status exclusion, and the created-at sort, run one bounded equality
  branch per deploy type and merge the branches with `UNION ALL`.
- Cap the number of UNION branches. Single-value, `starter`, non-canonical, and
  over-limit inputs stay on the generic path.
- Keep SQL values parameterized. Branches and the outer query use the same
  stable `created_at, id` ordering so page boundaries are deterministic.
- Preserve exact-total behavior, `has_more`, page size, and offset semantics.
- Keep text search, single deploy types, companion filters, and other sort modes
  on the generic query path.

## Affected Modules and Routes

- Go module: repository root, `github.com/pingcap-inc/clinic`.
- Packages: `pkg/datasource`, `pkg/datasource/view`, and the existing index name
  declaration in `pkg/db`.
- Entrypoints and images: `cmd/server/main.go` / `clinic` and
  `cmd/internal_server/main.go` / `internal_api`, which both compile the shared
  datasource package.
- Routes: `/clinic/api/v1/dashboard/clusters` and `/clusters2`.
- Required Clinic CD components: `clinic,internal-api`.
- app-delivery application/workloads: `clinic-api` / Clinic Deployment and
  Internal API Deployment.

## Implementation

1. Normalize only exact legacy deploy-type mappings without mutating the input.
2. Recognize bounded, non-starter multi-type default-list shapes and deduplicate
   repeated values without enumerating combinations.
3. Build one parameterized equality branch per selected type, force the
   existing composite index, cap each branch at `offset + limit`, and bind the
   outer limit/offset.
4. Add a stable internal row-ID tie-breaker to list projections and ordering.
5. Use the same parameterized statement builder for the existing numeric UNION
   path so the shared mechanism does not execute display/interpolated SQL.
6. Reject invalid pagination before calculating an offset; valid fast-path
   arithmetic remains bounded and other eligible shapes fall back safely.

## Exclusions and Compatibility

- No UI or search-box changes.
- No conversion for `shared`, `premium`, `nextgen-host`, or other ambiguous
  values.
- No new database index or schema migration; migration 91 already owns the
  required index.
- No changes to count caching, text-search policy, or caller attribution.
- Existing clients can keep sending legacy parameters; response JSON remains
  unchanged because the internal row ID is not serialized.

## Test Plan

- Focused dry-run SQL tests for normalization, eligibility, branch bounds,
  parameter binding (including quote, backslash, and `?` values), stable order,
  and overflow fallback.
- Existing datasource, dashboard API, and database package tests.
- `git diff --check` and repository CI.

## Staging Verification

1. Deploy the feature SHA with Clinic CD: `env=staging`,
   `components=clinic,internal-api`.
2. Verify the app-delivery PR changes only the `clinic` and `internal_api`
   image tags and merges.
3. Verify the Clinic and Internal API Deployments are Ready, use the intended
   images, and have no unexpected restart.
4. Compare the same low-frequency legacy request before/after: HTTP status,
   ordered cluster-ID digest, item count, `has_more`, and latency.
5. Check focused Clinic logs for unexpected errors if the API check fails.

## Release Boundary

The Clinic source PR remains unmerged until required checks/review pass and the
user explicitly authorizes merge. Production requires a separate explicit
`prod-cloud` release authorization and app-delivery merge authorization.
