github.com/polarismesh/polaris@v1.17.8/admin/job/delete_empty_service.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package job 19 20 import ( 21 "time" 22 23 "github.com/mitchellh/mapstructure" 24 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 25 26 "github.com/polarismesh/polaris/cache" 27 api "github.com/polarismesh/polaris/common/api/v1" 28 "github.com/polarismesh/polaris/common/model" 29 "github.com/polarismesh/polaris/common/utils" 30 "github.com/polarismesh/polaris/service" 31 "github.com/polarismesh/polaris/store" 32 ) 33 34 type DeleteEmptyServiceJobConfig struct { 35 ServiceDeleteTimeout time.Duration `mapstructure:"serviceDeleteTimeout"` 36 } 37 38 type deleteEmptyServiceJob struct { 39 cfg *DeleteEmptyServiceJobConfig 40 namingServer service.DiscoverServer 41 cacheMgn *cache.CacheManager 42 storage store.Store 43 emptyServices map[string]time.Time 44 } 45 46 func (job *deleteEmptyServiceJob) init(raw map[string]interface{}) error { 47 cfg := &DeleteEmptyServiceJobConfig{ 48 ServiceDeleteTimeout: 30 * time.Minute, 49 } 50 decodeConfig := &mapstructure.DecoderConfig{ 51 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 52 Result: cfg, 53 } 54 decoder, err := mapstructure.NewDecoder(decodeConfig) 55 if err != nil { 56 log.Errorf("[Maintain][Job][DeleteEmptyServiceJob] new config decoder err: %v", err) 57 return err 58 } 59 err = decoder.Decode(raw) 60 if err != nil { 61 log.Errorf("[Maintain][Job][DeleteEmptyServiceJob] parse config err: %v", err) 62 return err 63 } 64 job.cfg = cfg 65 job.emptyServices = map[string]time.Time{} 66 return nil 67 } 68 69 func (job *deleteEmptyServiceJob) execute() { 70 err := job.deleteEmptyServices() 71 if err != nil { 72 log.Errorf("[Maintain][Job][DeleteEmptyServiceJob] delete empty autocreated services, err: %v", err) 73 } 74 } 75 76 func (job *deleteEmptyServiceJob) interval() time.Duration { 77 return job.cfg.ServiceDeleteTimeout 78 } 79 80 func (job *deleteEmptyServiceJob) clear() { 81 job.emptyServices = map[string]time.Time{} 82 } 83 84 func (job *deleteEmptyServiceJob) getEmptyServices() []*model.Service { 85 services := job.getAllEmptyServices() 86 return job.filterToDeletedServices(services, time.Now(), job.cfg.ServiceDeleteTimeout) 87 } 88 89 func (job *deleteEmptyServiceJob) getAllEmptyServices() []*model.Service { 90 var res []*model.Service 91 _ = job.cacheMgn.Service().IteratorServices(func(key string, svc *model.Service) (bool, error) { 92 if svc.IsAlias() { 93 return true, nil 94 } 95 count := job.cacheMgn.Instance().GetInstancesCountByServiceID(svc.ID) 96 if count.TotalInstanceCount == 0 { 97 res = append(res, svc) 98 } 99 return true, nil 100 }) 101 return res 102 } 103 104 func (job *deleteEmptyServiceJob) filterToDeletedServices(services []*model.Service, 105 now time.Time, timeout time.Duration) []*model.Service { 106 var toDeleteServices []*model.Service 107 m := map[string]time.Time{} 108 for _, svc := range services { 109 value, ok := job.emptyServices[svc.ID] 110 if !ok { 111 m[svc.ID] = now 112 continue 113 } 114 if now.After(value.Add(timeout)) { 115 toDeleteServices = append(toDeleteServices, svc) 116 } else { 117 m[svc.ID] = value 118 } 119 } 120 job.emptyServices = m 121 122 return toDeleteServices 123 } 124 125 func (job *deleteEmptyServiceJob) deleteEmptyServices() error { 126 emptyServices := job.getEmptyServices() 127 128 deleteBatchSize := 100 129 for i := 0; i < len(emptyServices); i += deleteBatchSize { 130 j := i + deleteBatchSize 131 if j > len(emptyServices) { 132 j = len(emptyServices) 133 } 134 135 ctx, err := buildContext(job.storage) 136 if err != nil { 137 log.Errorf("[Maintain][Job][DeleteUnHealthyInstance] build conetxt, err: %v", err) 138 return err 139 } 140 resp := job.namingServer.DeleteServices(ctx, convertDeleteServiceRequest(emptyServices[i:j])) 141 if api.CalcCode(resp) != 200 { 142 log.Errorf("[Maintain][Job][DeleteEmptyAutoCreatedService] delete services err, code: %d, info: %s", 143 resp.Code.GetValue(), resp.Info.GetValue()) 144 } 145 } 146 147 log.Infof("[Maintain][Job][DeleteEmptyAutoCreatedService] delete empty auto-created services count %d", 148 len(emptyServices)) 149 return nil 150 } 151 152 func convertDeleteServiceRequest(infos []*model.Service) []*apiservice.Service { 153 var entries = make([]*apiservice.Service, len(infos)) 154 for i, info := range infos { 155 entries[i] = &apiservice.Service{ 156 Namespace: utils.NewStringValue(info.Namespace), 157 Name: utils.NewStringValue(info.Name), 158 } 159 } 160 return entries 161 }