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  }