lookup_cache.go 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. package util
  2. import (
  3. "sync"
  4. "time"
  5. )
  6. // LookupCache is a single-value cache with a time-to-live (TTL). The cache has a lookup function
  7. // to retrieve the value and stores it until TTL is reached.
  8. //
  9. // Example:
  10. //
  11. // lookup := func() (string, error) {
  12. // r, _ := http.Get("...")
  13. // s, _ := io.ReadAll(r.Body)
  14. // return string(s), nil
  15. // }
  16. // c := NewLookupCache[string](lookup, time.Hour)
  17. // fmt.Println(c.Get()) // Fetches the string via HTTP
  18. // fmt.Println(c.Get()) // Uses cached value
  19. type LookupCache[T any] struct {
  20. value *T
  21. lookup func() (T, error)
  22. ttl time.Duration
  23. updated time.Time
  24. mu sync.Mutex
  25. }
  26. // LookupFunc is a function that is called by the LookupCache if the underlying
  27. // value is out-of-date. It returns the new value, or an error.
  28. type LookupFunc[T any] func() (T, error)
  29. // NewLookupCache creates a new LookupCache with a given time-to-live (TTL)
  30. func NewLookupCache[T any](lookup LookupFunc[T], ttl time.Duration) *LookupCache[T] {
  31. return &LookupCache[T]{
  32. value: nil,
  33. lookup: lookup,
  34. ttl: ttl,
  35. }
  36. }
  37. // Value returns the cached value, or retrieves it via the lookup function
  38. func (c *LookupCache[T]) Value() (T, error) {
  39. c.mu.Lock()
  40. defer c.mu.Unlock()
  41. if c.value == nil || (c.ttl > 0 && time.Since(c.updated) > c.ttl) {
  42. value, err := c.lookup()
  43. if err != nil {
  44. var t T
  45. return t, err
  46. }
  47. c.value = &value
  48. c.updated = time.Now()
  49. }
  50. return *c.value, nil
  51. }