eko / gocache

☔️ A complete Go cache library that brings you multiple ways of managing your caches
https://vincent.composieux.fr/article/i-wrote-gocache-a-complete-and-extensible-go-cache-library/
MIT License
2.4k stars 193 forks source link

Getting a key that is not in the store doesn't automatically save it after the LoadFunction fetches value from data source #255

Closed Michael-Girma closed 1 month ago

Michael-Girma commented 1 month ago

I used gocache from github.com/patrickmn/go-cache and created a store. I then created a new loadable from that store with a LoadFunction to fetch the value if it's not in the cache already. The function seems to work fine but upon retrieval of the data element, it is not automatically stored in the store. Here is a small snippet for further investigation:

package main

import (
    "fmt"
    "time"

    "github.com/eko/gocache/cache"
    "github.com/eko/gocache/store"
    gocache "github.com/patrickmn/go-cache"
)

func main() {
    cacheClient := gocache.New(6*time.Hour, 12*time.Hour)
    cacheStore := store.NewGoCache(cacheClient, &store.Options{Expiration: 6 * time.Hour})

    mockCache := cache.NewLoadable(
        func(cacheApiKey interface{}) (interface{}, error) {
            fmt.Printf("Not in cache. Going to Datasource")
            // mock database response
            return "value", nil
        },
        cacheStore)

    mockCache.Get("Key")
    mockCache.Get("Key")
}

Steps for Reproduction

  1. Run the following snippet to cache unstored key
  2. View output

Expected behavior: Expected output would only have a single log

Not in cache. Going to Datasource

Actual behavior: Multiple logs indicating that fetched value isn't saved

Not in cache. Going to Datasource
Not in cache. Going to Datasource

Platforms: uname -a: Linux 6.8.0-36-generic #36-Ubuntu SMP PREEMPT_DYNAMIC Mon Jun 10 10:49:14 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Include browser, operating system and respective versions

Versions:

semihbkgr commented 1 month ago

The values returned from the loadFunc are sent to the setChannel instead of being directly set in the cache. The values in setChannel are iterated over in a separate goroutine and then set to the cache.

// Unable to find in cache, try to load it from load function
object, err = c.loadFunc(key)
if err != nil {
  return object, err
}

// Then, put it back in cache
c.setChannel <- &loadableKeyValue{key, object}

Therefore, you need to wait a bit before calling the second Get operation.

cacheClient := gocache.New(6*time.Hour, 12*time.Hour)
cacheStore := store.NewGoCache(cacheClient, &store.Options{Expiration: 6 * time.Hour})

mockCache := cache.NewLoadable(
  func(cacheApiKey interface{}) (interface{}, error) {
      fmt.Printf("Not in cache. Going to Datasource")
      // mock database response
      return "value", nil
  },
  cacheStore)

mockCache.Get("Key")
time.Sleep(time.Second / 10)
mockCache.Get("Key")
johnwook commented 2 weeks ago

Thank you for clarifying! time.Sleep made it work!