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.48k stars 199 forks source link

Cannot use other type except "any" when using Marshaler #196

Open rajatbhatnagar94 opened 1 year ago

rajatbhatnagar94 commented 1 year ago

Steps for Reproduction

Changing the generic type of the cache from any to any other type doesn't work. The code fails to compile as Marshaler doesn't implement the specific generic type. For example, in your test cases for marshal, if I change the generic type while initializing cache to string instead of any I see an error while initializing the marshal object.

Sample Code:

func TestNew(t *testing.T) {
    // Given
    ctrl := gomock.NewController(t)

    cache := cache.NewMockCacheInterface[string](ctrl) // changed the type from `any` to `string`
    // When
    marshaler := New(cache) // (errors out here)

    // Then
    assert.IsType(t, new(Marshaler), marshaler)
    assert.Equal(t, cache, marshaler.cache)
}

Let me know if I'm using it incorrectly, but according to the Readme with the Book example, it should be correct. Here's another example

package main

import (
    "fmt"

    redis "github.com/go-redis/redis/v8"
    cache "github.com/eko/gocache/lib/v4/cache"
    metrics "github.com/eko/gocache/lib/v4/metrics"
    marshaler "github.com/eko/gocache/lib/v4/marshaler"
    redis_store "github.com/eko/gocache/store/redis/v4"
)
type Book struct{}
func main() {

    // Initialize Redis client and store
        redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
        redisStore := redis_store.NewRedis(redisClient)

        promMetrics := metrics.NewPrometheus("my-test-app")
        // Initialize chained cache
        cacheManager := cache.NewMetric[*Book](
            promMetrics,
            cache.New[*Book](redisStore),
        )

        // Initializes marshaler
        marshal := marshaler.New(cacheManager) // throws an error here
        fmt.Println(marshal)
}

Expected behavior: It should be able to initialize the object properly.

Actual behavior:

./marshaler_test.go:26:19: cannot use cache (variable of type *cache.MockCacheInterface[string]) as cache.CacheInterface[any] value in argument to New: *cache.MockCacheInterface[string] does not implement cache.CacheInterface[any] (wrong type for method Get)
                have Get(context.Context, any) (string, error)
                want Get(context.Context, any) (any, error)
FAIL    github.com/eko/gocache/lib/v4/marshaler [build failed]

Second error:

cannot use cacheManager (variable of type *cache.MetricCache[*Book]) as cache.CacheInterface[any] value in argument to marshaler.New: *cache.MetricCache[*Book] does not implement cache.CacheInterface[any] (wrong type for method Get)
        have Get(context.Context, any) (*Book, error)
        want Get(context.Context, any) (any, error)compiler[InvalidIfaceAssign](https://pkg.go.dev/golang.org/x/tools/internal/typesinternal#InvalidIfaceAssign)

Platforms:

Include browser, operating system and respective versions: Linux

Versions:

Which versions are you running? v4.1.2

hoshsadiq commented 1 year ago

Got test same issue here:

package main_test

import (
    "context"
    "github.com/eko/gocache/lib/v4/cache"
    "github.com/eko/gocache/lib/v4/marshaler"
    redis_store "github.com/eko/gocache/store/redis/v4"
    "github.com/redis/go-redis/v9"
    "testing"
)

func TestInvalidTypes(t *testing.T) {
    // Client stores the client to the underlying cache service
    var client *redis.Client

    // cache stores the cache interface
    var cStore *cache.Cache[string]

    client = redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
    cacheStore := redis_store.NewRedis(client, nil)
    cStore = cache.New[string](cacheStore)

    m := marshaler.New(cStore)
    m.Set(context.Background(), "somekey", "somevalue")
}
$ go test .
# github.com/hoshsadiq/playground/main_test [github.com/hoshsadiq/playground/main.test]
./cache_test.go:23:21: cannot use cStore (variable of type *cache.Cache[string]) as cache.CacheInterface[any] value in argument to marshaler.New: *cache.Cache[string] does not implement cache.CacheInterface[any] (wrong type for method Get)
                have Get(context.Context, any) (string, error)
                want Get(context.Context, any) (any, error)
FAIL    github.com/hoshsadiq/playground/main [build failed]
FAIL
bohdand-weka commented 2 months ago

I think marshaler concept is wrong.

Better to use marshaler over store, but it requires to patch marshaler by ourself.

The target usage in my case would be like this:

        client = redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
    cacheStore := redis_store.NewRedis(client, nil)
    cStore = cache.New[string](marshaler.New[string](cacheStore))

    m.Set(context.Background(), "somekey", "somevalue")