github.com/safedep/dry@v0.0.0-20241016050132-a15651f0548b/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"github.com/safedep/dry/log"
     9  )
    10  
    11  // Type for cache data
    12  type CacheData []byte
    13  
    14  // Represent an unique cache data identifier
    15  // Multiple attributes are used to help in indexing / partitioning
    16  type CacheKey struct {
    17  	Source string
    18  	Type   string
    19  	Id     string
    20  }
    21  
    22  // Defines a type for a cacheable function (closure)
    23  type CachableFunc[T any] func() (T, error)
    24  
    25  // Internally maintained default caching adapter
    26  var globalCachingAdapter Cache
    27  
    28  // Cache define the contract for implementing caches
    29  type Cache interface {
    30  	Put(key *CacheKey, data *CacheData, ttl time.Duration) error
    31  	Get(key *CacheKey) (*CacheData, error)
    32  }
    33  
    34  func init() {
    35  	Disable()
    36  }
    37  
    38  func setGlobalCachingAdapter(a Cache) {
    39  	globalCachingAdapter = a
    40  }
    41  
    42  func Disable() {
    43  	setGlobalCachingAdapter(newNoCache())
    44  }
    45  
    46  func EnableWith(adapter Cache) {
    47  	setGlobalCachingAdapter(adapter)
    48  }
    49  
    50  // Through define the devex sugar - Read through cache
    51  func Through[T any](key *CacheKey, ttl time.Duration, fun CachableFunc[T]) (T, error) {
    52  	system := globalCachingAdapter
    53  	if system == nil {
    54  		panic("default cache adapter is not set")
    55  	}
    56  
    57  	var empty T
    58  	data, err := system.Get(key)
    59  	if err != nil {
    60  		// Cache lookup failed, invoke actual function
    61  		realData, err := fun()
    62  		if err != nil {
    63  			return empty, err
    64  		}
    65  
    66  		// Cache output from actual function - Must not fail original path
    67  		serializedData, err := JsonSerialize(realData)
    68  		if err != nil {
    69  			log.Debugf("Cache: Failed to serialize type:%T err:%v",
    70  				realData, err)
    71  			return realData, nil
    72  		}
    73  
    74  		err = system.Put(key, &serializedData, ttl)
    75  		if err != nil {
    76  			log.Debugf("Cache: Failed to put due to: %v", err)
    77  		}
    78  
    79  		return realData, nil
    80  	} else {
    81  		// Cache lookup is successful
    82  		// Adapter bug in case of NPE on data
    83  		return JsonDeserialize[T](*data)
    84  	}
    85  }
    86  
    87  func JsonSerialize[T any](data T) (CacheData, error) {
    88  	serialized, err := json.Marshal(data)
    89  	if err != nil {
    90  		return CacheData{}, err
    91  	}
    92  
    93  	return CacheData(serialized), nil
    94  }
    95  
    96  func JsonDeserialize[T any](data CacheData) (T, error) {
    97  	var deserialized T
    98  	err := json.NewDecoder(bytes.NewReader([]byte(data))).Decode(&deserialized)
    99  
   100  	return deserialized, err
   101  }