code.gitea.io/gitea@v1.22.3/modules/cache/string_cache.go (about)

     1  // Copyright 2024 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package cache
     5  
     6  import (
     7  	"errors"
     8  	"strings"
     9  
    10  	"code.gitea.io/gitea/modules/json"
    11  	"code.gitea.io/gitea/modules/setting"
    12  	"code.gitea.io/gitea/modules/util"
    13  
    14  	chi_cache "gitea.com/go-chi/cache" //nolint:depguard
    15  )
    16  
    17  type GetJSONError struct {
    18  	err         error
    19  	cachedError string // Golang error can't be stored in cache, only the string message could be stored
    20  }
    21  
    22  func (e *GetJSONError) ToError() error {
    23  	if e.err != nil {
    24  		return e.err
    25  	}
    26  	return errors.New("cached error: " + e.cachedError)
    27  }
    28  
    29  type StringCache interface {
    30  	Ping() error
    31  
    32  	Get(key string) (string, bool)
    33  	Put(key, value string, ttl int64) error
    34  	Delete(key string) error
    35  	IsExist(key string) bool
    36  
    37  	PutJSON(key string, v any, ttl int64) error
    38  	GetJSON(key string, ptr any) (exist bool, err *GetJSONError)
    39  
    40  	ChiCache() chi_cache.Cache
    41  }
    42  
    43  type stringCache struct {
    44  	chiCache chi_cache.Cache
    45  }
    46  
    47  func NewStringCache(cacheConfig setting.Cache) (StringCache, error) {
    48  	adapter := util.IfZero(cacheConfig.Adapter, "memory")
    49  	interval := util.IfZero(cacheConfig.Interval, 60)
    50  	cc, err := chi_cache.NewCacher(chi_cache.Options{
    51  		Adapter:       adapter,
    52  		AdapterConfig: cacheConfig.Conn,
    53  		Interval:      interval,
    54  	})
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	return &stringCache{chiCache: cc}, nil
    59  }
    60  
    61  func (sc *stringCache) Ping() error {
    62  	return sc.chiCache.Ping()
    63  }
    64  
    65  func (sc *stringCache) Get(key string) (string, bool) {
    66  	v := sc.chiCache.Get(key)
    67  	if v == nil {
    68  		return "", false
    69  	}
    70  	s, ok := v.(string)
    71  	return s, ok
    72  }
    73  
    74  func (sc *stringCache) Put(key, value string, ttl int64) error {
    75  	return sc.chiCache.Put(key, value, ttl)
    76  }
    77  
    78  func (sc *stringCache) Delete(key string) error {
    79  	return sc.chiCache.Delete(key)
    80  }
    81  
    82  func (sc *stringCache) IsExist(key string) bool {
    83  	return sc.chiCache.IsExist(key)
    84  }
    85  
    86  const cachedErrorPrefix = "<CACHED-ERROR>:"
    87  
    88  func (sc *stringCache) PutJSON(key string, v any, ttl int64) error {
    89  	var s string
    90  	switch v := v.(type) {
    91  	case error:
    92  		s = cachedErrorPrefix + v.Error()
    93  	default:
    94  		b, err := json.Marshal(v)
    95  		if err != nil {
    96  			return err
    97  		}
    98  		s = util.UnsafeBytesToString(b)
    99  	}
   100  	return sc.chiCache.Put(key, s, ttl)
   101  }
   102  
   103  func (sc *stringCache) GetJSON(key string, ptr any) (exist bool, getErr *GetJSONError) {
   104  	s, ok := sc.Get(key)
   105  	if !ok || s == "" {
   106  		return false, nil
   107  	}
   108  	s, isCachedError := strings.CutPrefix(s, cachedErrorPrefix)
   109  	if isCachedError {
   110  		return true, &GetJSONError{cachedError: s}
   111  	}
   112  	if err := json.Unmarshal(util.UnsafeStringToBytes(s), ptr); err != nil {
   113  		return false, &GetJSONError{err: err}
   114  	}
   115  	return true, nil
   116  }
   117  
   118  func (sc *stringCache) ChiCache() chi_cache.Cache {
   119  	return sc.chiCache
   120  }