eye-gu commented on code in PR #3371:
URL: https://github.com/apache/dubbo-go/pull/3371#discussion_r3367383566


##########
registry/servicediscovery/service_discovery_registry.go:
##########
@@ -298,12 +320,142 @@ func (s *serviceDiscoveryRegistry) IsAvailable() bool {
 }
 
 func (s *serviceDiscoveryRegistry) Destroy() {
+       s.stopMetadataTimers()
        err := s.serviceDiscovery.Destroy()
        if err != nil {
                logger.Errorf("[Registry][ServiceDiscovery] destroy 
serviceDiscovery catch error, err=%s", err.Error())
        }
 }
 
+func (s *serviceDiscoveryRegistry) stopMetadataTimers() {
+       s.lock.Lock()
+       defer s.lock.Unlock()
+       if s.renewAppMetadataTimer != nil {
+               s.renewAppMetadataTimer.Stop()
+               s.renewAppMetadataTimer = nil
+       }
+}
+
+// ========== renewAppMetadata: daily app-level metadata re-publish ==========
+
+func (s *serviceDiscoveryRegistry) startRenewAppMetadataTimer() {
+       if !s.url.GetParamBool(constant.CycleReportKey, true) {
+               return
+       }
+
+       // Run immediately on start
+       if s.url.GetParamBool(constant.MetadataRenewOnStartupKey, true) {
+               go s.doRenewAppMetadata()
+       }
+
+       delay := s.calculateRenewAppMetadataDelay()
+       s.renewAppMetadataTimer = time.AfterFunc(delay, func() {
+               s.doRenewAppMetadata()
+               // Reschedule for next day
+               s.lock.Lock()
+               if s.renewAppMetadataTimer != nil {
+                       s.renewAppMetadataTimer.Reset(24 * time.Hour)
+               }
+               s.lock.Unlock()
+       })
+}
+
+func (s *serviceDiscoveryRegistry) doRenewAppMetadata() {
+       registryID := s.url.GetParam(constant.RegistryIdKey, "")
+       metaInfo := metadata.GetMetadataInfo(registryID)
+       if metaInfo == nil || metaInfo.Revision == "0" {
+               return
+       }
+       metaInfo.LastUpdatedTime = time.Now().UnixMilli()
+       if err := s.metadataReport.PublishAppMetadata(metaInfo.App, 
metaInfo.Revision, metaInfo); err != nil {
+               logger.Errorf("[Metadata][renewAppMetadata] failed to 
re-publish metadata for app=%s revision=%s: %v", metaInfo.App, 
metaInfo.Revision, err)
+       } else {
+               logger.Infof("[Metadata][renewAppMetadata] refreshed metadata 
for app=%s revision=%s", metaInfo.App, metaInfo.Revision)
+       }
+
+       // Run garbage collection if enabled, after each renew cycle
+       if s.url.GetParamBool(constant.MetadataGCEnabledKey, true) {
+               s.doGarbageCollect()
+       }
+}
+
+func (s *serviceDiscoveryRegistry) calculateRenewAppMetadataDelay() 
time.Duration {
+       now := time.Now()
+       // Next day 2:00 AM
+       nextDay2AM := time.Date(now.Year(), now.Month(), now.Day()+1, 2, 0, 0, 
0, now.Location())
+       // Add random offset 0~4 hours to avoid thundering herd
+       randomOffset := time.Duration(rand.Int64N(int64(4 * time.Hour)))
+       return time.Until(nextDay2AM) + randomOffset
+}
+
+// ========== GC: stale revision cleanup ==========
+
+func (s *serviceDiscoveryRegistry) doGarbageCollect() {
+       registryID := s.url.GetParam(constant.RegistryIdKey, "")
+       metaInfo := metadata.GetMetadataInfo(registryID)
+       if metaInfo == nil {
+               return
+       }
+       app := metaInfo.App
+       if app == "" {
+               return
+       }
+
+       // Step 1: List all revisions for this app
+       revisions, err := s.metadataReport.ListAppRevisions(app)
+       if err != nil {
+               logger.Warnf("[Metadata][GC] failed to list app revisions: %v", 
err)
+               return
+       }
+       if len(revisions) == 0 {
+               return
+       }
+
+       // Step 2: Filter stale candidates (exceed GC window in days)
+       gcWindowDays := s.url.GetParamByIntValue(constant.MetadataGCWindowKey, 
5)
+       if gcWindowDays <= 0 || gcWindowDays > 365 {
+               gcWindowDays = 5
+       }
+       cutoff := time.Now().AddDate(0, 0, -gcWindowDays).UnixMilli()
+       candidates := make(map[string]bool)
+       for _, rev := range revisions {
+               // Skip special revisions
+               if rev.Revision == "0" || rev.Revision == "N/A" || rev.Revision 
== "" || rev.Revision == metaInfo.Revision {
+                       continue
+               }
+               // ModifyTime == 0 means old metadata produced by a version 
that does not set
+               // lastUpdatedTime — never garbage-collect such entries. Only 
delete when the
+               // revision is older than gcWindow and no alive instance 
references it.
+               if rev.ModifyTime > 0 && rev.ModifyTime < cutoff {
+                       candidates[rev.Revision] = true
+               }
+       }
+       if len(candidates) == 0 {
+               return
+       }
+
+       // Step 3: Get alive instances and their revisions
+       instances := s.serviceDiscovery.GetInstances(app)
+       aliveRevisions := make(map[string]bool)
+       for _, inst := range instances {
+               rev := 
inst.GetMetadata()[constant.ExportedServicesRevisionPropertyName]

Review Comment:
   已添加nil校验



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to