From 9ae9cdd9ecb417f2745a9fee4b4f43fe1b69804f Mon Sep 17 00:00:00 2001 From: Andriy Biletsky Date: Wed, 4 Dec 2024 02:45:23 +0700 Subject: [PATCH] Implement LZ4 compression for query cache --- app/query/cache.go | 29 +++++++++++++++++++++++++++-- app/query/cache_test.go | 20 ++++++++++++++++++++ go.mod | 3 +-- go.sum | 2 -- pkg/sturdycache/sturdycache.go | 19 ------------------- pkg/sturdycache/sturdycache_test.go | 1 - 6 files changed, 48 insertions(+), 26 deletions(-) diff --git a/app/query/cache.go b/app/query/cache.go index a81b38f8..06dbf2ce 100644 --- a/app/query/cache.go +++ b/app/query/cache.go @@ -1,16 +1,19 @@ package query import ( + "bytes" "context" "crypto" "encoding/json" "errors" "fmt" + "io" "time" "github.com/OdyseeTeam/odysee-api/internal/monitor" "github.com/OdyseeTeam/odysee-api/pkg/chainquery" "github.com/OdyseeTeam/odysee-api/pkg/rpcerrors" + "github.com/pierrec/lz4" "github.com/eko/gocache/lib/v4/cache" "github.com/eko/gocache/lib/v4/marshaler" @@ -220,11 +223,33 @@ func (r *CachedResponse) RPCResponse(id int) *jsonrpc.RPCResponse { } func (r *CachedResponse) MarshalBinary() ([]byte, error) { - return json.Marshal(r) + val, err := json.Marshal(r) + if err != nil { + return nil, err + } + + vr := bytes.NewBuffer(val) + vw := &bytes.Buffer{} + zw := lz4.NewWriter(vw) + _, err = io.Copy(zw, vr) + zw.Close() + if err != nil { + return nil, err + } + return vw.Bytes(), nil } func (r *CachedResponse) UnmarshalBinary(data []byte) error { - return json.Unmarshal(data, r) + vr := bytes.NewBuffer(data) + vw := &bytes.Buffer{} + zr := lz4.NewReader(vr) + _, err := io.Copy(vw, zr) + if err != nil { + return err + } + decoder := json.NewDecoder(vw) + decoder.UseNumber() + return decoder.Decode(r) } func preflightCacheHook(caller *Caller, ctx context.Context) (*jsonrpc.RPCResponse, error) { diff --git a/app/query/cache_test.go b/app/query/cache_test.go index b3fc0f29..a32e5654 100644 --- a/app/query/cache_test.go +++ b/app/query/cache_test.go @@ -34,3 +34,23 @@ func TestGetCacheKey(t *testing.T) { } assert.Contains(seen, genCacheKey(params[1])) } + +func TestCachedResponseMarshal(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + jr, err := decodeResponse(resolveResponseWithPurchase) + require.NoError(err) + require.NotNil(jr.Result) + r := &CachedResponse{ + Result: jr.Result, + Error: jr.Error, + } + mr, err := r.MarshalBinary() + require.NoError(err) + require.NotEmpty(mr) + assert.Less(len(mr), len(resolveResponseWithPurchase)) + r2 := &CachedResponse{} + err = r2.UnmarshalBinary(mr) + require.NoError(err) + assert.Equal(r, r2) +} diff --git a/go.mod b/go.mod index 86d68cb3..1efec443 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/dgraph-io/ristretto v0.2.0 github.com/eko/gocache/lib/v4 v4.1.6 github.com/eko/gocache/store/redis/v4 v4.2.2 - github.com/eko/gocache/store/ristretto/v4 v4.2.2 github.com/friendsofgo/errors v0.9.2 github.com/getsentry/sentry-go v0.13.0 github.com/go-chi/chi/v5 v5.0.8 @@ -189,7 +188,7 @@ require ( github.com/oschwald/maxminddb-golang v1.8.0 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pierrec/lz4 v2.6.1+incompatible // indirect + github.com/pierrec/lz4 v2.6.1+incompatible github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/prometheus/common v0.37.0 // indirect diff --git a/go.sum b/go.sum index 865added..c8299b0a 100644 --- a/go.sum +++ b/go.sum @@ -840,8 +840,6 @@ github.com/eko/gocache/lib/v4 v4.1.6 h1:5WWIGISKhE7mfkyF+SJyWwqa4Dp2mkdX8QsZpnEN github.com/eko/gocache/lib/v4 v4.1.6/go.mod h1:HFxC8IiG2WeRotg09xEnPD72sCheJiTSr4Li5Ameg7g= github.com/eko/gocache/store/redis/v4 v4.2.2 h1:Thw31fzGuH3WzJywsdbMivOmP550D6JS7GDHhvCJPA0= github.com/eko/gocache/store/redis/v4 v4.2.2/go.mod h1:LaTxLKx9TG/YUEybQvPMij++D7PBTIJ4+pzvk0ykz0w= -github.com/eko/gocache/store/ristretto/v4 v4.2.2 h1:lXFzoZ5ck6Gy6ON7f5DHSkNt122qN7KoroCVgVwF7oo= -github.com/eko/gocache/store/ristretto/v4 v4.2.2/go.mod h1:uIvBVJzqRepr5L0RsbkfQ2iYfbyos2fuji/s4yM+aUM= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/pkg/sturdycache/sturdycache.go b/pkg/sturdycache/sturdycache.go index 693f782d..ada83ecd 100644 --- a/pkg/sturdycache/sturdycache.go +++ b/pkg/sturdycache/sturdycache.go @@ -4,11 +4,9 @@ import ( "context" "time" - "github.com/dgraph-io/ristretto" "github.com/eko/gocache/lib/v4/cache" "github.com/eko/gocache/lib/v4/store" redis_store "github.com/eko/gocache/store/redis/v4" - ristretto_store "github.com/eko/gocache/store/ristretto/v4" "github.com/redis/go-redis/v9" "golang.org/x/exp/rand" ) @@ -64,23 +62,6 @@ func NewReplicatedCache( return baseStore, nil } -// AddLocalCache adds a local in-memory cache layer to a replicated cache instance. -func AddLocalCache(baseCache *ReplicatedCache) (cache.CacheInterface[any], error) { - // About 50k resolve responses with average size of 10KB - ristrettoCache, err := ristretto.NewCache(&ristretto.Config{NumCounters: 500_000, MaxCost: 500_000_000, BufferItems: 64}) - if err != nil { - return nil, err - } - ristrettoStore := ristretto_store.NewRistretto(ristrettoCache) - - cache := cache.NewChain[any]( - cache.New[any](ristrettoStore), - cache.New[any](baseCache), - ) - - return cache, nil -} - // Set writes to master. func (rc *ReplicatedCache) Set(ctx context.Context, key any, value any, options ...store.Option) error { return rc.masterCache.Set(ctx, key, value, options...) diff --git a/pkg/sturdycache/sturdycache_test.go b/pkg/sturdycache/sturdycache_test.go index b3eb2bc0..642e9f42 100644 --- a/pkg/sturdycache/sturdycache_test.go +++ b/pkg/sturdycache/sturdycache_test.go @@ -42,7 +42,6 @@ func (t *TestStruct) UnmarshalBinary(data []byte) error { func (s *ReplicatedCacheTestSuite) SetupTest() { var err error s.replicatedCache, s.master, s.replicas, s.teardownFunc = CreateTestCache(s.T()) - s.cache, err = AddLocalCache(s.replicatedCache) s.Require().NoError(err) }