dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/servicediscovery/store/cache_manager.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package store
    19  
    20  import (
    21  	"encoding/gob"
    22  	"os"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  import (
    28  	"github.com/dubbogo/gost/log/logger"
    29  
    30  	"github.com/hashicorp/golang-lru"
    31  )
    32  
    33  type CacheManager struct {
    34  	name         string        // The name of the cache manager
    35  	cacheFile    string        // The file path where the cache is stored
    36  	dumpInterval time.Duration // The duration after which the cache dump
    37  	stop         chan struct{} // Channel used to stop the cache expiration routine
    38  	cache        *lru.Cache    // The LRU cache implementation
    39  	lock         sync.Mutex
    40  	enableDump   bool
    41  }
    42  
    43  type Item struct {
    44  	Key   string
    45  	Value interface{}
    46  }
    47  
    48  // NewCacheManager creates a new CacheManager instance.
    49  // It initializes the cache manager with the provided parameters and starts a routine for cache dumping.
    50  func NewCacheManager(name, cacheFile string, dumpInterval time.Duration, maxCacheSize int, enableDump bool) (*CacheManager, error) {
    51  	cm := &CacheManager{
    52  		name:         name,
    53  		cacheFile:    cacheFile,
    54  		dumpInterval: dumpInterval,
    55  		stop:         make(chan struct{}),
    56  		enableDump:   enableDump,
    57  	}
    58  	cache, err := lru.New(maxCacheSize)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	cm.cache = cache
    63  
    64  	// Check if the cache file exists and load the cache if it does
    65  	if _, err := os.Stat(cacheFile); err == nil {
    66  		if err = cm.loadCache(); err != nil {
    67  			logger.Warnf("Failed to load the cache file:[%s].The err is %v", cm.cacheFile, err)
    68  		}
    69  	}
    70  
    71  	if enableDump {
    72  		cm.runDumpTask()
    73  	}
    74  
    75  	return cm, nil
    76  }
    77  
    78  // Get retrieves the value associated with the given key from the cache.
    79  func (cm *CacheManager) Get(key string) (interface{}, bool) {
    80  	return cm.cache.Get(key)
    81  }
    82  
    83  // Set sets the value associated with the given key in the cache.
    84  func (cm *CacheManager) Set(key string, value interface{}) {
    85  	cm.cache.Add(key, value)
    86  }
    87  
    88  // Delete removes the value associated with the given key from the cache.
    89  func (cm *CacheManager) Delete(key string) {
    90  	cm.cache.Remove(key)
    91  }
    92  
    93  // GetAll returns all the key-value pairs in the cache.
    94  func (cm *CacheManager) GetAll() map[string]interface{} {
    95  	keys := cm.cache.Keys()
    96  
    97  	result := make(map[string]interface{})
    98  	for _, k := range keys {
    99  		result[k.(string)], _ = cm.cache.Get(k)
   100  	}
   101  
   102  	return result
   103  }
   104  
   105  // loadCache loads the cache from the cache file.
   106  func (cm *CacheManager) loadCache() error {
   107  	cf, err := os.Open(cm.cacheFile)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	decoder := gob.NewDecoder(cf)
   113  	for {
   114  		var it Item
   115  		err = decoder.Decode(&it)
   116  		if err != nil {
   117  			if err.Error() == "EOF" {
   118  				break // Reached end of file
   119  			}
   120  			return err
   121  		}
   122  		// Add the loaded keys to the front of the LRU list
   123  		cm.cache.Add(it.Key, it.Value)
   124  	}
   125  
   126  	return cf.Close()
   127  }
   128  
   129  // dumpCache dumps the cache to the cache file.
   130  func (cm *CacheManager) dumpCache() error {
   131  
   132  	cm.lock.Lock()
   133  	defer cm.lock.Unlock()
   134  
   135  	items := cm.GetAll()
   136  
   137  	file, err := os.Create(cm.cacheFile)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	defer file.Close()
   142  
   143  	encoder := gob.NewEncoder(file)
   144  	for k, v := range items {
   145  		gob.Register(v)
   146  		err = encoder.Encode(&Item{
   147  			Key:   k,
   148  			Value: v,
   149  		})
   150  		if err != nil {
   151  			return err
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  func (cm *CacheManager) runDumpTask() {
   158  	go func() {
   159  		ticker := time.NewTicker(cm.dumpInterval)
   160  		for {
   161  			select {
   162  			case <-ticker.C:
   163  				// Dump the cache to the file
   164  				if err := cm.dumpCache(); err != nil {
   165  					// Handle error
   166  					logger.Warnf("Failed to dump cache,the err is %v", err)
   167  				} else {
   168  					logger.Infof("Dumping [%s] caches, latest entries %d", cm.name, cm.cache.Len())
   169  				}
   170  			case <-cm.stop:
   171  				ticker.Stop()
   172  				return
   173  			}
   174  		}
   175  	}()
   176  }
   177  
   178  func (cm *CacheManager) StopDump() {
   179  	cm.lock.Lock()
   180  	defer cm.lock.Unlock()
   181  	if cm.enableDump {
   182  		cm.stop <- struct{}{} // Stop the cache dump routine
   183  		cm.enableDump = false
   184  	}
   185  }
   186  
   187  // destroy stops the cache dump routine, clears the cache and removes the cache file.
   188  func (cm *CacheManager) destroy() {
   189  	cm.StopDump()    // Stop the cache dump routine
   190  	cm.cache.Purge() // Clear the cache
   191  
   192  	// Delete the cache file if it exists
   193  	if _, err := os.Stat(cm.cacheFile); err == nil {
   194  		if err := os.Remove(cm.cacheFile); err == nil {
   195  			logger.Infof("The cacheFile [%s] was cleared", cm.cacheFile)
   196  		}
   197  	}
   198  }