diff --git a/pkg/cluster/manager/scale_in.go b/pkg/cluster/manager/scale_in.go index 1b18251f34..8ae7b8809b 100644 --- a/pkg/cluster/manager/scale_in.go +++ b/pkg/cluster/manager/scale_in.go @@ -106,6 +106,14 @@ func (m *Manager) ScaleIn( return err } + deletedNodes := set.NewStringSet(nodes...) + scaledInPD := false + topo.IterInstance(func(inst spec.Instance) { + if deletedNodes.Exist(inst.ID()) && inst.ComponentName() == spec.ComponentPD { + scaledInPD = true + } + }) + b, err := m.sshTaskBuilder(name, topo, base.User, gOpt) if err != nil { return err @@ -161,6 +169,15 @@ func (m *Manager) ScaleIn( } m.logger.Infof("Scaled cluster `%s` in successfully", name) + if scaledInPD { + if dash := spec.FindComponent(topo, spec.ComponentDashboard); dash != nil && len(dash.Instances()) > 0 { + m.logger.Warnf("%s", color.YellowString( + "\nSince PD node(s) were scaled in, the standalone tidb-dashboard connects to a single PD endpoint "+ + "that may have been removed. If it can no longer reach PD, restart it to pick up a new endpoint:\n\t%s", + color.GreenString("%s restart %s -R %s", tui.OsArgs0(), name, spec.ComponentDashboard), + )) + } + } return nil } diff --git a/pkg/cluster/spec/dashboard.go b/pkg/cluster/spec/dashboard.go index e29f68c990..fc70806645 100644 --- a/pkg/cluster/spec/dashboard.go +++ b/pkg/cluster/spec/dashboard.go @@ -18,7 +18,6 @@ import ( "crypto/tls" "fmt" "path/filepath" - "strings" "time" "github.com/pingcap/tiup/pkg/cluster/ctxt" @@ -198,10 +197,6 @@ func (i *DashboardInstance) InitConfig( enableTLS := topo.GlobalOptions.TLSEnabled spec := i.InstanceSpec.(*DashboardSpec) - pds := []string{} - for _, pdspec := range topo.PDServers { - pds = append(pds, pdspec.GetAdvertiseClientURL(enableTLS)) - } cfg := &scripts.DashboardScript{ // -h, --host string listen host of the Dashboard Server Host: i.GetListenHost(), @@ -211,7 +206,7 @@ func (i *DashboardInstance) InitConfig( LogDir: paths.Log, Port: spec.Port, NumaNode: spec.NumaNode, - PD: strings.Join(pds, ","), + PD: dashboardPDEndpoint(topo.PDServers, enableTLS), TLSEnabled: enableTLS, } @@ -243,3 +238,17 @@ func (i *DashboardInstance) InitConfig( func (i *DashboardInstance) setTLSConfig(ctx context.Context, enableTLS bool, configs map[string]any, paths meta.DirPaths) (map[string]any, error) { return nil, nil } + +// dashboardPDEndpoint returns the PD URL for standalone tidb-dashboard. +// +// Dashboard treats --pd as a single endpoint and does not split a +// comma-separated list, so passing all PD endpoints breaks its PD +// connection. See https://github.com/pingcap/tidb-dashboard/issues/1920. +// Pass only the first PD until Dashboard supports multiple PD HTTP API +// endpoints, then restore joining all PD URLs. +func dashboardPDEndpoint(pdServers []*PDSpec, enableTLS bool) string { + if len(pdServers) == 0 { + return "" + } + return pdServers[0].GetAdvertiseClientURL(enableTLS) +}