Skip to content

Commit 07c228a

Browse files
committed
feat: 资源视图pod列表查询慢优化
1 parent 6a322a7 commit 07c228a

File tree

6 files changed

+346
-20
lines changed

6 files changed

+346
-20
lines changed

bcs-services/bcs-storage/storage/actions/v1http/dynamic/utils.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,15 +466,19 @@ func getMulticlusterStoreOption(req *restful.Request) (*lib.StoreGetOption, erro
466466
if multiClusterReq.Field != "" {
467467
fields = strings.Split(multiClusterReq.Field, ",")
468468
}
469+
sort := map[string]int{
470+
indexIdTag: -1,
471+
}
472+
if multiClusterReq.Sort != nil {
473+
sort = multiClusterReq.Sort
474+
}
469475

470476
// option
471477
return &lib.StoreGetOption{
472478
Fields: fields,
473479
// Note: do not use non-indexed fields for sorting
474480
// otherwise it will exceed the sorting RAM limit in mongoDB.
475-
Sort: map[string]int{
476-
indexIdTag: -1,
477-
},
481+
Sort: sort,
478482
Cond: condition,
479483
Offset: multiClusterReq.Offset,
480484
Limit: multiClusterReq.Limit,

bcs-services/bcs-storage/storage/types/multiclusterresource.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type MulticlusterListReqParams struct {
3232
Field string `json:"field"`
3333
LabelSelector string `json:"labelSelector"`
3434
Conditions []*operator.Condition `json:"conditions"`
35+
Sort map[string]int `json:"sort"`
3536
}
3637

3738
// EnsureConditions ensure conditions's value is operator.M instead of map[string]interface{}

bcs-services/cluster-resources/pkg/handler/multicluster/filter.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,31 @@ func (f *QueryFilter) Page(resources []*storage.Resource) []*storage.Resource {
507507
}
508508
return resources[f.Offset:end]
509509
}
510+
511+
// PageWithoutSort 分页
512+
func (f *QueryFilter) PageWithoutSort(resources []*storage.Resource) []*storage.Resource {
513+
if f.Offset >= len(resources) {
514+
return []*storage.Resource{}
515+
}
516+
end := f.Offset + f.Limit
517+
if end > len(resources) {
518+
end = len(resources)
519+
}
520+
return resources[f.Offset:end]
521+
}
522+
523+
// SortByStorage 通过storage排序
524+
func (f *QueryFilter) SortByStorage() map[string]int {
525+
order := 1
526+
if f.Order == OrderDesc {
527+
order = -1
528+
}
529+
switch f.SortBy {
530+
case SortByNamespace:
531+
return map[string]int{ConNamespace: order}
532+
case SortByAge:
533+
return map[string]int{ConAge: order}
534+
default:
535+
return map[string]int{ConName: order}
536+
}
537+
}

bcs-services/cluster-resources/pkg/handler/multicluster/query.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"strings"
2121
"sync"
2222

23+
"github.com/Tencent/bk-bcs/bcs-common/pkg/odm/operator"
2324
"golang.org/x/sync/errgroup"
2425
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -87,27 +88,54 @@ func (q *StorageQuery) Fetch(ctx context.Context, groupVersion, kind string) (ma
8788
Namespaces: v.GetNamespaces(),
8889
})
8990
}
90-
resources, err := storage.ListAllMultiClusterResources(ctx, storage.ListMultiClusterResourcesReq{
91+
// status查询需要全量获取
92+
var allRecord bool
93+
if len(q.QueryFilter.Status) != 0 {
94+
allRecord = true
95+
}
96+
resources, total, err := storage.ListAllMultiClusterResources(ctx, allRecord, storage.ListMultiClusterResourcesReq{
9197
Kind: kind,
9298
ClusteredNamespaces: clusteredNamespaces,
93-
Conditions: q.ViewFilter.ToConditions(),
99+
Conditions: q.toConditions(),
100+
Sort: q.QueryFilter.SortByStorage(),
101+
Offset: q.QueryFilter.Offset,
102+
Limit: q.QueryFilter.Limit,
94103
})
95104
if err != nil {
96105
return nil, err
97106
}
98-
resources = ApplyFilter(resources, q.ViewFilter.CreateSourceFilter)
99-
// 第二次过滤
100-
resources = ApplyFilter(resources, q.QueryFilter.CreatorFilter, q.QueryFilter.NameFilter,
101-
q.QueryFilter.StatusFilter, q.QueryFilter.LabelSelectorFilter, q.QueryFilter.IPFilter,
102-
q.QueryFilter.CreateSourceFilter)
103-
total := len(resources)
104-
resources = q.QueryFilter.Page(resources)
107+
if allRecord {
108+
resources = ApplyFilter(resources, q.QueryFilter.StatusFilter)
109+
total = len(resources)
110+
resources = q.QueryFilter.PageWithoutSort(resources)
111+
}
105112
resp := buildList(ctx, resources)
106113
resp["total"] = total
107114
resp["perms"] = map[string]interface{}{"applyURL": applyURL}
108115
return resp, nil
109116
}
110117

118+
// toConditions storage query
119+
func (q *StorageQuery) toConditions() []*operator.Condition {
120+
conditions := []*operator.Condition{}
121+
// creator 过滤条件
122+
conditions = append(conditions, q.creatorToCondition()...)
123+
124+
// name 过滤条件
125+
conditions = append(conditions, q.nameToCondition()...)
126+
127+
// label 过滤条件
128+
conditions = append(conditions, q.labelToCondition()...)
129+
130+
// createSource 过滤条件
131+
conditions = append(conditions, q.createSourceToCondition()...)
132+
133+
// ip 过滤条件
134+
conditions = append(conditions, q.ipToCondition()...)
135+
136+
return conditions
137+
}
138+
111139
// FetchPreferred fetches multicluster resources.
112140
func (q *StorageQuery) FetchPreferred(
113141
ctx context.Context, gvr *schema.GroupVersionResource) (map[string]interface{}, error) {
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making Blueking Container Service available.
3+
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4+
* Licensed under the MIT License (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
* http://opensource.org/licenses/MIT
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
9+
* either express or implied. See the License for the specific language governing permissions and
10+
* limitations under the License.
11+
*/
12+
13+
package multicluster
14+
15+
import (
16+
"fmt"
17+
18+
"github.com/Tencent/bk-bcs/bcs-common/pkg/odm/operator"
19+
"github.com/samber/lo"
20+
21+
"github.com/Tencent/bk-bcs/bcs-services/cluster-resources/pkg/resource/constants"
22+
"github.com/Tencent/bk-bcs/bcs-services/cluster-resources/pkg/util/mapx"
23+
clusterRes "github.com/Tencent/bk-bcs/bcs-services/cluster-resources/proto/cluster-resources"
24+
)
25+
26+
const (
27+
// ConName 资源名称
28+
ConName string = "data.metadata.name"
29+
// ConNamespace 资源命名空间
30+
ConNamespace string = "data.metadata.namespace"
31+
// ConAge 资源创建时间
32+
ConAge string = "data.metadata.creationTimestamp"
33+
// ConLabels 资源标签
34+
ConLabels string = "data.metadata.labels"
35+
// ConAnnotations 资源注解
36+
ConAnnotations string = "data.metadata.annotations"
37+
)
38+
39+
// creatorToCondition creator condition
40+
func (q *StorageQuery) creatorToCondition() []*operator.Condition {
41+
conditions := []*operator.Condition{}
42+
if len(q.QueryFilter.Creator) == 0 && len(q.ViewFilter.Creator) == 0 {
43+
return conditions
44+
}
45+
// 无创建者的情况比较特殊
46+
viewCreator := q.ViewFilter.Creator
47+
queryCreator := q.QueryFilter.Creator
48+
if lo.Contains(viewCreator, EmptyCreator) {
49+
viewCreator = []string{EmptyCreator}
50+
}
51+
if lo.Contains(queryCreator, EmptyCreator) {
52+
queryCreator = []string{EmptyCreator}
53+
}
54+
// creator 过滤条件
55+
var creator []string
56+
switch {
57+
case len(viewCreator) == 0:
58+
creator = queryCreator
59+
case len(queryCreator) == 0:
60+
creator = viewCreator
61+
default:
62+
// 返回交集创建人
63+
creator = lo.Intersect(viewCreator, queryCreator)
64+
}
65+
66+
if lo.Contains(creator, EmptyCreator) {
67+
// 使用全角符号代替 '.',区分字段分隔,无创建者的资源,creator字段为 null
68+
conditions = append(conditions, operator.NewLeafCondition(
69+
operator.Eq, map[string]interface{}{
70+
ConAnnotations + "." + mapx.ConvertPath(LabelCreator): nil}))
71+
} else {
72+
conditions = append(conditions, operator.NewLeafCondition(
73+
operator.In, map[string]interface{}{
74+
ConAnnotations + "." + mapx.ConvertPath(LabelCreator): creator}))
75+
}
76+
return conditions
77+
}
78+
79+
// nameToCondition name condition
80+
func (q *StorageQuery) nameToCondition() []*operator.Condition {
81+
conditions := []*operator.Condition{}
82+
// creator 过滤条件
83+
if len(q.QueryFilter.Name) != 0 && len(q.ViewFilter.Name) != 0 {
84+
con := operator.NewBranchCondition(
85+
operator.And,
86+
operator.NewLeafCondition(operator.Con, map[string]string{ConName: q.QueryFilter.Name}),
87+
operator.NewLeafCondition(operator.Con, map[string]string{ConName: q.ViewFilter.Name}),
88+
)
89+
conditions = append(conditions, con)
90+
return conditions
91+
}
92+
if len(q.ViewFilter.Name) != 0 {
93+
conditions = append(conditions, operator.NewLeafCondition(
94+
operator.Con, map[string]string{ConName: q.ViewFilter.Name}))
95+
return conditions
96+
}
97+
if len(q.QueryFilter.Name) != 0 {
98+
conditions = append(conditions, operator.NewLeafCondition(
99+
operator.Con, map[string]string{ConName: q.QueryFilter.Name}))
100+
return conditions
101+
}
102+
return conditions
103+
}
104+
105+
// labelToCondition label condition
106+
func (q *StorageQuery) labelToCondition() []*operator.Condition {
107+
conditions := []*operator.Condition{}
108+
// nolint:gocritic
109+
labelSelector := append(q.ViewFilter.LabelSelector, q.QueryFilter.LabelSelector...)
110+
// labelSelector 过滤条件
111+
for _, v := range labelSelector {
112+
if len(v.Values) == 0 && v.Op != OpExists && v.Op != OpDoesNotExist {
113+
continue
114+
}
115+
switch v.Op {
116+
case OpEQ:
117+
conditions = append(conditions, operator.NewLeafCondition(
118+
operator.Eq, map[string]interface{}{
119+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(v.Key)): v.Values[0]}))
120+
case OpNotEQ:
121+
conditions = append(conditions, operator.NewLeafCondition(
122+
operator.Not, map[string]interface{}{
123+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(v.Key)): v.Values[0]}))
124+
case OpIn:
125+
conditions = append(conditions, operator.NewLeafCondition(
126+
operator.In, map[string]interface{}{
127+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(v.Key)): v.Values}))
128+
case OpNotIn:
129+
conditions = append(conditions, operator.NewLeafCondition(
130+
operator.Nin, map[string]interface{}{
131+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(v.Key)): v.Values}))
132+
case OpExists:
133+
conditions = append(conditions, operator.NewLeafCondition(
134+
operator.Ext, map[string]interface{}{
135+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(v.Key)): ""}))
136+
case OpDoesNotExist:
137+
conditions = append(conditions, operator.NewLeafCondition(
138+
operator.Eq, map[string]interface{}{
139+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(v.Key)): nil}))
140+
}
141+
}
142+
return conditions
143+
}
144+
145+
// createSourceToCondition create source condition
146+
func (q *StorageQuery) createSourceToCondition() []*operator.Condition {
147+
conditions := []*operator.Condition{}
148+
if q.ViewFilter.CreateSource == nil && q.QueryFilter.CreateSource == nil {
149+
return conditions
150+
}
151+
152+
var createSources []*clusterRes.CreateSource
153+
154+
if q.ViewFilter.CreateSource != nil {
155+
createSources = append(createSources, q.ViewFilter.CreateSource)
156+
}
157+
if q.QueryFilter.CreateSource != nil {
158+
createSources = append(createSources, q.QueryFilter.CreateSource)
159+
}
160+
161+
// 创建来源source筛选
162+
templateCon := operator.NewBranchCondition(
163+
operator.Or,
164+
operator.NewLeafCondition(
165+
operator.Eq, map[string]interface{}{fmt.Sprintf(ConAnnotations+".%s",
166+
mapx.ConvertPath(constants.TemplateSourceType)): constants.TemplateSourceTypeValue}),
167+
operator.NewLeafCondition(
168+
operator.Eq, map[string]interface{}{fmt.Sprintf(ConLabels+".%s",
169+
mapx.ConvertPath(constants.TemplateSourceType)): constants.TemplateSourceTypeValue}),
170+
)
171+
helmCon := operator.NewLeafCondition(
172+
operator.Eq, map[string]interface{}{fmt.Sprintf(ConLabels+".%s",
173+
mapx.ConvertPath(constants.HelmSourceType)): constants.HelmCreateSource})
174+
175+
// 要排除掉helm和template的情况
176+
webCon := operator.NewBranchCondition(
177+
operator.And,
178+
operator.NewBranchCondition(
179+
operator.Nor,
180+
templateCon,
181+
helmCon,
182+
),
183+
operator.NewBranchCondition(
184+
operator.Or,
185+
operator.NewLeafCondition(operator.Ne, map[string]interface{}{
186+
fmt.Sprintf(ConAnnotations+".%s", mapx.ConvertPath(constants.CreatorAnnoKey)): nil}),
187+
operator.NewLeafCondition(operator.Ne, map[string]interface{}{
188+
fmt.Sprintf(ConLabels+".%s", mapx.ConvertPath(constants.UpdaterAnnoKey)): nil})),
189+
)
190+
for _, cs := range createSources {
191+
switch cs.Source {
192+
case constants.TemplateCreateSource:
193+
// 筛选模板名称和模板版本
194+
var temNameVersionCon *operator.Condition
195+
if cs.Template != nil {
196+
var temNameCon []*operator.Condition
197+
// template 需要筛选名称和版本
198+
if cs.Template.TemplateName != "" {
199+
temNameCon = append(temNameCon, operator.NewLeafCondition(operator.Con, map[string]interface{}{
200+
fmt.Sprintf(ConAnnotations+".%s",
201+
mapx.ConvertPath(constants.TemplateNameAnnoKey)): cs.Template.TemplateName}))
202+
203+
}
204+
if cs.Template.TemplateVersion != "" {
205+
temNameCon = append(temNameCon, operator.NewLeafCondition(operator.Con, map[string]interface{}{
206+
fmt.Sprintf(ConAnnotations+".%s",
207+
mapx.ConvertPath(constants.TemplateVersionAnnoKey)): cs.Template.TemplateVersion}))
208+
}
209+
temNameCon = append(temNameCon, templateCon)
210+
temNameVersionCon = operator.NewBranchCondition(operator.And, temNameCon...)
211+
} else {
212+
temNameVersionCon = templateCon
213+
}
214+
conditions = append(conditions, temNameVersionCon)
215+
case constants.HelmCreateSource:
216+
var helmChartCon *operator.Condition
217+
// helm 筛选chart名称
218+
if cs.Chart != nil && cs.Chart.ChartName != "" {
219+
chartCon := operator.NewLeafCondition(operator.Con, map[string]interface{}{
220+
fmt.Sprintf(ConLabels+".%s",
221+
mapx.ConvertPath(constants.HelmChartAnnoKey)): cs.Chart.ChartName})
222+
helmChartCon = operator.NewBranchCondition(operator.And, helmCon, chartCon)
223+
} else {
224+
helmChartCon = helmCon
225+
}
226+
conditions = append(conditions, helmChartCon)
227+
case constants.WebCreateSource:
228+
conditions = append(conditions, webCon)
229+
case constants.ClientCreateSource:
230+
conditions = append(conditions, operator.NewBranchCondition(
231+
operator.Nor, templateCon, helmCon, webCon,
232+
))
233+
}
234+
}
235+
236+
return conditions
237+
}
238+
239+
// ipToCondition ip condition
240+
func (q *StorageQuery) ipToCondition() []*operator.Condition {
241+
conditions := []*operator.Condition{}
242+
if q.QueryFilter.IP == "" {
243+
return conditions
244+
}
245+
// ip 过滤条件
246+
con := operator.NewBranchCondition(
247+
operator.Or,
248+
operator.NewLeafCondition(operator.Con, map[string]string{"data.spec.clusterIP": q.QueryFilter.IP}),
249+
operator.NewLeafCondition(operator.Eq,
250+
operator.M{"data.spec.clusterIPs": operator.M{"$elemMatch": operator.M{"$regex": q.QueryFilter.IP}}}),
251+
operator.NewLeafCondition(operator.Con, map[string]string{"data.status.hostIP": q.QueryFilter.IP}),
252+
operator.NewLeafCondition(operator.Eq, operator.M{"data.status.podIPs": operator.M{"$elemMatch": operator.
253+
M{"ip": operator.M{"$regex": q.QueryFilter.IP}}}}),
254+
)
255+
conditions = append(conditions, con)
256+
return conditions
257+
}

0 commit comments

Comments
 (0)