From 357a0f22e36d5aacf65af462c1239046a2317b82 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Fri, 31 Oct 2025 10:19:24 +0800 Subject: [PATCH] fix(scheduler): enable profiling (#23642) * fix(scheduler): enable profiling * feat(scheduler): cache NetworkAdditionalWire --- pkg/scheduler/cache/candidate/base.go | 7 +- pkg/scheduler/data_manager/common/common.go | 8 +- .../network_additional_wire/doc.go | 1 + .../network_additional_wire/naw.go | 95 +++++++++++++++++++ pkg/scheduler/handler/handler.go | 3 +- pkg/scheduler/handler/profiling.go | 40 ++++++++ pkg/scheduler/service/service.go | 4 +- pkg/util/gin/middleware/keystone_auth.go | 13 ++- 8 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 pkg/scheduler/data_manager/network_additional_wire/doc.go create mode 100644 pkg/scheduler/data_manager/network_additional_wire/naw.go create mode 100644 pkg/scheduler/handler/profiling.go diff --git a/pkg/scheduler/cache/candidate/base.go b/pkg/scheduler/cache/candidate/base.go index 5518ca1c2d..d4ba5aeb92 100644 --- a/pkg/scheduler/cache/candidate/base.go +++ b/pkg/scheduler/cache/candidate/base.go @@ -34,6 +34,7 @@ import ( "yunion.io/x/onecloud/pkg/scheduler/data_manager/cloudregion" "yunion.io/x/onecloud/pkg/scheduler/data_manager/netinterface" "yunion.io/x/onecloud/pkg/scheduler/data_manager/network" + "yunion.io/x/onecloud/pkg/scheduler/data_manager/network_additional_wire" "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku" "yunion.io/x/onecloud/pkg/scheduler/data_manager/zone" schedmodels "yunion.io/x/onecloud/pkg/scheduler/models" @@ -634,11 +635,7 @@ func (b *BaseHostDesc) fillNetworks(host *computemodels.SHost, netGetter *networ nets := make([]computemodels.SNetwork, 0) allNets := network.Manager.GetStore().GetAll() for _, net := range allNets { - netAdditionalWireIds, err := computemodels.NetworkAdditionalWireManager.FetchNetworkAdditionalWireIds(net.Id) - if err != nil { - log.Errorf("NetworkAdditionalWireManager.FetchNetworkAdditionalWireIds %s error %s", net.Id, err) - netAdditionalWireIds = []string{} - } + netAdditionalWireIds := network_additional_wire.FetchNetworkAdditionalWireIds(net.Id) if wireIds.Has(net.WireId) || wireIds.HasAny(netAdditionalWireIds...) { nets = append(nets, net) } diff --git a/pkg/scheduler/data_manager/common/common.go b/pkg/scheduler/data_manager/common/common.go index a708a9b268..d8efc40f94 100644 --- a/pkg/scheduler/data_manager/common/common.go +++ b/pkg/scheduler/data_manager/common/common.go @@ -224,7 +224,13 @@ func (s *ResourceStore[O]) GetByPrefix(prefixId string) []O { } func (s *ResourceStore[O]) GetAll() []O { - ret := make([]O, 0) + // To avoid repeated slice growth during append, count first and preallocate capacity + count := 0 + s.dataMap.Range(func(key, value any) bool { + count++ + return true + }) + ret := make([]O, 0, count) s.dataMap.Range(func(key, value any) bool { ret = append(ret, value.(O)) return true diff --git a/pkg/scheduler/data_manager/network_additional_wire/doc.go b/pkg/scheduler/data_manager/network_additional_wire/doc.go new file mode 100644 index 0000000000..da8aae511c --- /dev/null +++ b/pkg/scheduler/data_manager/network_additional_wire/doc.go @@ -0,0 +1 @@ +package network_additional_wire // import "yunion.io/x/onecloud/pkg/scheduler/data_manager/network_additional_wire" diff --git a/pkg/scheduler/data_manager/network_additional_wire/naw.go b/pkg/scheduler/data_manager/network_additional_wire/naw.go new file mode 100644 index 0000000000..38d2548a56 --- /dev/null +++ b/pkg/scheduler/data_manager/network_additional_wire/naw.go @@ -0,0 +1,95 @@ +package network_additional_wire + +import ( + "context" + "sync" + "time" + + "yunion.io/x/log" + "yunion.io/x/pkg/util/wait" + + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/compute/models" +) + +var ( + manager *networkAdditionalWireManager +) + +func Start(ctx context.Context, refreshInterval time.Duration) { + manager = &networkAdditionalWireManager{ + dataMap: newNawMap(), + refreshInterval: refreshInterval, + } + manager.sync() +} + +type nawList []*models.SNetworkAdditionalWire + +type nawMap struct { + *sync.Map +} + +func newNawMap() *nawMap { + return &nawMap{ + Map: new(sync.Map), + } +} + +func (m *nawMap) GetByNetworkId(id string) nawList { + value, ok := m.Load(id) + if ok { + return value.(nawList) + } + return nil +} + +func (m *nawMap) Add(item *models.SNetworkAdditionalWire) { + items := m.GetByNetworkId(item.NetworkId) + if items == nil { + items = make([]*models.SNetworkAdditionalWire, 0) + } + items = append(items, item) + m.Store(item.NetworkId, items) +} + +type networkAdditionalWireManager struct { + dataMap *nawMap + refreshInterval time.Duration +} + +func (m *networkAdditionalWireManager) syncOnce() { + log.Infof("NetworkAdditionalWireManager start sync") + startTime := time.Now() + q := models.NetworkAdditionalWireManager.Query() + ret := make([]models.SNetworkAdditionalWire, 0) + err := db.FetchModelObjects(models.NetworkAdditionalWireManager, q, &ret) + if err != nil { + log.Errorf("NetworkAdditionalWireManager fetch err: %v", err) + return + } + m.dataMap = newNawMap() + for i := range ret { + obj := &ret[i] + m.dataMap.Add(obj) + } + log.Infof("NetworkAdditionalWireManager end sync, consume %s", time.Since(startTime)) +} + +func (m *networkAdditionalWireManager) sync() { + wait.Forever(m.syncOnce, m.refreshInterval) +} + +func (m *networkAdditionalWireManager) GetByNetworkId(networkId string) []*models.SNetworkAdditionalWire { + list := m.dataMap.GetByNetworkId(networkId) + return list +} + +func FetchNetworkAdditionalWireIds(networkId string) []string { + naws := manager.GetByNetworkId(networkId) + wires := make([]string, len(naws)) + for i := range naws { + wires[i] = naws[i].WireId + } + return wires +} diff --git a/pkg/scheduler/handler/handler.go b/pkg/scheduler/handler/handler.go index 3f5431c439..b85a1bb611 100644 --- a/pkg/scheduler/handler/handler.go +++ b/pkg/scheduler/handler/handler.go @@ -35,12 +35,13 @@ import ( // InstallHandler is an interface that registes route and // handles scheduler's services. -func InstallHandler(r *gin.Engine) { +func InstallHandler(r *gin.Engine, enableProfiling bool) { r.POST("/scheduler", timer(scheduleHandler)) r.POST("/scheduler/:action", timer(schedulerActionHandler)) r.POST("/scheduler/:action/:ident", timer(schedulerActionIdentHandler)) InstallPingHandler(r) InstallVersionHandler(r) + InstallProfiling(r, enableProfiling) } func timer(f gin.HandlerFunc) gin.HandlerFunc { diff --git a/pkg/scheduler/handler/profiling.go b/pkg/scheduler/handler/profiling.go new file mode 100644 index 0000000000..f99457252f --- /dev/null +++ b/pkg/scheduler/handler/profiling.go @@ -0,0 +1,40 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handler + +import ( + "net/http/pprof" + + gin "github.com/gin-gonic/gin" +) + +// InstallProfiling 在 gin 引擎上注册 pprof 相关路由。 +// 路由与 appsrv 中保持一致:/debug/pprof, /cmdline, /profile, /symbol, /trace +func InstallProfiling(r *gin.Engine, enableProfiling bool) { + if !enableProfiling { + return + } + + base := "/debug/pprof" + + // 与 net/http/pprof 的默认注册保持一致 + r.GET(base, gin.WrapF(pprof.Index)) + r.GET(base+"/", gin.WrapF(pprof.Index)) + r.GET(base+"/cmdline", gin.WrapF(pprof.Cmdline)) + r.GET(base+"/profile", gin.WrapF(pprof.Profile)) + r.GET(base+"/symbol", gin.WrapF(pprof.Symbol)) + r.POST(base+"/symbol", gin.WrapF(pprof.Symbol)) + r.GET(base+"/trace", gin.WrapF(pprof.Trace)) +} diff --git a/pkg/scheduler/service/service.go b/pkg/scheduler/service/service.go index f66dae84ad..499e0e27c9 100644 --- a/pkg/scheduler/service/service.go +++ b/pkg/scheduler/service/service.go @@ -42,6 +42,7 @@ import ( "yunion.io/x/onecloud/pkg/scheduler/data_manager/cloudregion" "yunion.io/x/onecloud/pkg/scheduler/data_manager/netinterface" "yunion.io/x/onecloud/pkg/scheduler/data_manager/network" + "yunion.io/x/onecloud/pkg/scheduler/data_manager/network_additional_wire" "yunion.io/x/onecloud/pkg/scheduler/data_manager/schedtag" skuman "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku" "yunion.io/x/onecloud/pkg/scheduler/data_manager/wire" @@ -119,6 +120,7 @@ func StartService() error { ctx := context.Background() go skuman.Start(ctx, utils.ToDuration(o.Options.SkuRefreshInterval)) go schedtag.Start(ctx, utils.ToDuration("30s")) + go network_additional_wire.Start(ctx, utils.ToDuration("30m")) for _, f := range []func(ctx context.Context){ cloudregion.Manager.Start, @@ -151,7 +153,7 @@ func startHTTP(app *appsrv.Application, opt *o.SchedulerOptions) error { router.Use(middleware.KeystoneTokenVerifyMiddleware()) // prometheus.InstallHandler(router) - schedhandler.InstallHandler(router) + schedhandler.InstallHandler(router, opt.EnableAppProfiling) server := appsrv.InitHTTPServer(app, net.JoinHostPort(opt.Address, strconv.Itoa(int(opt.Port)))) server.Handler = router diff --git a/pkg/util/gin/middleware/keystone_auth.go b/pkg/util/gin/middleware/keystone_auth.go index fa9b5f1995..a1e4fb0389 100644 --- a/pkg/util/gin/middleware/keystone_auth.go +++ b/pkg/util/gin/middleware/keystone_auth.go @@ -31,7 +31,18 @@ const ( func KeystoneTokenVerifyMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // hack - escapeAuth := []string{"ping", "version", "metrics", "k8s/predicates", "k8s/priorities"} + escapeAuth := []string{ + "ping", + "version", + "metrics", + "k8s/predicates", + "k8s/priorities", + "debug/pprof", + "debug/pprof/cmdline", + "debug/pprof/profile", + "debug/pprof/symbol", + "debug/pprof/trace", + } for _, s := range escapeAuth { if strings.HasSuffix(c.Request.URL.Path, s) { c.Next()