From 1862ee40dffd41eb5b16d44861475cab0698c717 Mon Sep 17 00:00:00 2001 From: David Bickford Date: Wed, 12 Mar 2025 17:10:44 -0400 Subject: [PATCH 01/11] fixing multi slot selection --- cacheaside.go | 56 ++++++++++++++++++++------------------------------- go.mod | 10 ++++----- go.sum | 8 ++++++++ 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/cacheaside.go b/cacheaside.go index e3f820a..2f4c08e 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -243,27 +243,27 @@ func (rca *CacheAside) registerAll(keys []string) map[string]<-chan struct{} { } func (rca *CacheAside) tryGetMulti(ctx context.Context, ttl time.Duration, keys []string) (map[string]string, error) { - resps, err := rca.client.DoCache( - ctx, - rca.client.B().Mget().Key(keys...).Cache(), - ttl, - ).ToArray() + multi := make([]rueidis.CacheableTTL, 0, len(keys)) + for _, key := range keys { + cmd := rca.client.B().Get().Key(key).Cache() + multi = append(multi, rueidis.CacheableTTL{ + Cmd: cmd, + TTL: ttl, + }) + } + resps := rca.client.DoMultiCache(ctx, multi...) res := make(map[string]string) - if err != nil && rueidis.IsRedisNil(err) { - return nil, err - } else if err == nil && len(resps) != 0 { - for i, resp := range resps { - val, err2 := resp.ToString() - if err2 != nil && rueidis.IsRedisNil(err2) { - continue - } else if err2 != nil { - return nil, err2 - } - if !strings.HasPrefix(val, prefix) { - res[keys[i]] = val - continue - } + for i, resp := range resps { + val, err := resp.ToString() + if err != nil && rueidis.IsRedisNil(err) { + continue + } else if err != nil { + return nil, err + } + if !strings.HasPrefix(val, prefix) { + res[keys[i]] = val + continue } } return res, nil @@ -348,28 +348,16 @@ type valAndLock struct { } func (rca *CacheAside) setMultiWithLock(ctx context.Context, ttl time.Duration, keyValLock map[string]valAndLock) ([]string, error) { - - setStmts := make([]rueidis.LuaExec, 0, len(keyValLock)) - keyOrd := make([]string, 0, len(keyValLock)) - for k, vl := range keyValLock { - keyOrd = append(keyOrd, k) - setStmts = append(setStmts, rueidis.LuaExec{ - Keys: []string{k}, - Args: []string{vl.lockVal, vl.val, strconv.FormatInt(ttl.Milliseconds(), 10)}, - }) - } - - setResps := setKeyLua.ExecMulti(ctx, rca.client, setStmts...) out := make([]string, 0) - for i, resp := range setResps { - err := resp.Error() + for k, vl := range keyValLock { + _, err := rca.setWithLock(ctx, ttl, k, vl) if err != nil { if !rueidis.IsRedisNil(err) { return nil, err } continue } - out = append(out, keyOrd[i]) + out = append(out, k) } return out, nil } diff --git a/go.mod b/go.mod index 96198c6..0b10c33 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ module github.com/dcbickfo/redcache -go 1.22.4 +go 1.24.1 require ( - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/redis/rueidis v1.0.38 - github.com/stretchr/testify v1.9.0 + github.com/redis/rueidis v1.0.55 + github.com/stretchr/testify v1.10.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.19.0 // indirect + golang.org/x/sys v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ae701ea..69b37a2 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -14,12 +16,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/rueidis v1.0.38 h1:ZlEBumHM+ECCMgf/zQZImLfmxb/sxGKnBP0R0AxoH/Y= github.com/redis/rueidis v1.0.38/go.mod h1:bnbkk4+CkXZgDPEbUtSos/o55i4RhFYYesJ4DS2zmq0= +github.com/redis/rueidis v1.0.55 h1:PrRv6eETcanBgYVNdwxn6RyUaPfxN6H+b5jUA4mfpkw= +github.com/redis/rueidis v1.0.55/go.mod h1:cr7ILwt1AqyMRfjWlA9Orubj6gp1xzn1DPyhmrhv/x0= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 4d0fc77d7646435c2c69b22f34784df1b7ce5286 Mon Sep 17 00:00:00 2001 From: David Bickford Date: Wed, 12 Mar 2025 17:19:51 -0400 Subject: [PATCH 02/11] downgrade go --- go.mod | 2 +- go.sum | 20 ++++++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 0b10c33..f724e99 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/dcbickfo/redcache -go 1.24.1 +go 1.22.4 require ( github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index 69b37a2..920b8de 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -10,26 +8,20 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= -github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/rueidis v1.0.38 h1:ZlEBumHM+ECCMgf/zQZImLfmxb/sxGKnBP0R0AxoH/Y= -github.com/redis/rueidis v1.0.38/go.mod h1:bnbkk4+CkXZgDPEbUtSos/o55i4RhFYYesJ4DS2zmq0= github.com/redis/rueidis v1.0.55 h1:PrRv6eETcanBgYVNdwxn6RyUaPfxN6H+b5jUA4mfpkw= github.com/redis/rueidis v1.0.55/go.mod h1:cr7ILwt1AqyMRfjWlA9Orubj6gp1xzn1DPyhmrhv/x0= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 2d6e34f198d720da5b93582c1fe976f64a77aded Mon Sep 17 00:00:00 2001 From: David Bickford Date: Wed, 12 Mar 2025 17:55:49 -0400 Subject: [PATCH 03/11] added threading to set multi with lock --- cacheaside.go | 40 +++++++++++++++++++++++++++++++++------- go.mod | 5 ++++- go.sum | 2 ++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/cacheaside.go b/cacheaside.go index 2f4c08e..005e4ac 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -11,6 +11,7 @@ import ( "github.com/dcbickfo/redcache/internal/syncx" "github.com/google/uuid" "github.com/redis/rueidis" + "golang.org/x/sync/errgroup" ) type CacheAside struct { @@ -348,18 +349,43 @@ type valAndLock struct { } func (rca *CacheAside) setMultiWithLock(ctx context.Context, ttl time.Duration, keyValLock map[string]valAndLock) ([]string, error) { - out := make([]string, 0) + eg, ctx := errgroup.WithContext(ctx) + resps := make([]struct { + val string + err error + }, len(keyValLock)) + keyOrd := make([]string, 0, len(keyValLock)) + i := 0 for k, vl := range keyValLock { - _, err := rca.setWithLock(ctx, ttl, k, vl) - if err != nil { - if !rueidis.IsRedisNil(err) { - return nil, err + keyOrd = append(keyOrd, k) + eg.Go(func() error { + _, err := rca.setWithLock(ctx, ttl, k, vl) + if err != nil { + if !rueidis.IsRedisNil(err) { + return err + } } - continue + resps[i] = struct { + val string + err error + }{ + val: k, + err: err, + } + return nil + }) + } + if err := eg.Wait(); err != nil { + return nil, err + } + out := make([]string, 0, len(resps)) + for j, r := range resps { + if r.err == nil { + out = append(out, keyOrd[j]) } - out = append(out, k) } return out, nil + } func (rca *CacheAside) unlockMulti(ctx context.Context, lockVals map[string]string) { diff --git a/go.mod b/go.mod index f724e99..6977630 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/dcbickfo/redcache -go 1.22.4 +go 1.23.0 + +toolchain go1.23.4 require ( github.com/google/go-cmp v0.7.0 @@ -12,6 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 920b8de..e5bd629 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= From c210495e699e751c28d0544794d039391c3af3ef Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:00:03 -0400 Subject: [PATCH 04/11] calculate slot for lua command --- cacheaside.go | 62 ++++++++++++------------ internal/cmdx/slot.go | 109 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 30 deletions(-) create mode 100644 internal/cmdx/slot.go diff --git a/cacheaside.go b/cacheaside.go index 005e4ac..591d485 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -7,11 +7,11 @@ import ( "strings" "time" + "github.com/dcbickfo/redcache/internal/cmdx" "github.com/dcbickfo/redcache/internal/mapsx" "github.com/dcbickfo/redcache/internal/syncx" "github.com/google/uuid" "github.com/redis/rueidis" - "golang.org/x/sync/errgroup" ) type CacheAside struct { @@ -349,43 +349,45 @@ type valAndLock struct { } func (rca *CacheAside) setMultiWithLock(ctx context.Context, ttl time.Duration, keyValLock map[string]valAndLock) ([]string, error) { - eg, ctx := errgroup.WithContext(ctx) - resps := make([]struct { - val string - err error - }, len(keyValLock)) - keyOrd := make([]string, 0, len(keyValLock)) - i := 0 + type keyOrderAndSet struct { + keyOrder []string + setStmts []rueidis.LuaExec + } + + stmts := make(map[uint16]keyOrderAndSet) + for k, vl := range keyValLock { - keyOrd = append(keyOrd, k) - eg.Go(func() error { - _, err := rca.setWithLock(ctx, ttl, k, vl) + slot := cmdx.Slot(k) + kos, ok := stmts[slot] + if !ok { + kos = keyOrderAndSet{ + keyOrder: make([]string, 0), + setStmts: make([]rueidis.LuaExec, 0), + } + } + kos.keyOrder = append(kos.keyOrder, k) + kos.setStmts = append(kos.setStmts, rueidis.LuaExec{ + Keys: []string{k}, + Args: []string{vl.lockVal, vl.val, strconv.FormatInt(ttl.Milliseconds(), 10)}, + }) + stmts[slot] = kos + } + + out := make([]string, 0) + for _, kos := range stmts { + setResps := setKeyLua.ExecMulti(ctx, rca.client, kos.setStmts...) + for i, resp := range setResps { + err := resp.Error() if err != nil { if !rueidis.IsRedisNil(err) { - return err + return nil, err } + continue } - resps[i] = struct { - val string - err error - }{ - val: k, - err: err, - } - return nil - }) - } - if err := eg.Wait(); err != nil { - return nil, err - } - out := make([]string, 0, len(resps)) - for j, r := range resps { - if r.err == nil { - out = append(out, keyOrd[j]) + out = append(out, kos.keyOrder[i]) } } return out, nil - } func (rca *CacheAside) unlockMulti(ctx context.Context, lockVals map[string]string) { diff --git a/internal/cmdx/slot.go b/internal/cmdx/slot.go new file mode 100644 index 0000000..1351291 --- /dev/null +++ b/internal/cmdx/slot.go @@ -0,0 +1,109 @@ +package cmdx + +// https://redis.io/topics/cluster-spec + +func Slot(key string) uint16 { + var s, e int + for ; s < len(key); s++ { + if key[s] == '{' { + break + } + } + if s == len(key) { + return crc16(key) & 16383 + } + for e = s + 1; e < len(key); e++ { + if key[e] == '}' { + break + } + } + if e == len(key) || e == s+1 { + return crc16(key) & 16383 + } + return crc16(key[s+1:e]) & 16383 +} + +/* + * Copyright 2001-2010 Georges Menie (www.menie.org) + * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style) + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* CRC16 implementation according to CCITT standards. + * + * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the + * following parameters: + * + * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" + * Width : 16 bit + * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) + * Initialization : 0000 + * Reflect Input byte : False + * Reflect Output CRC : False + * Xor constant to output CRC : 0000 + * Output for "123456789" : 31C3 + */ + +var crc16tab = [256]uint16{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +} + +func crc16(key string) (crc uint16) { + for i := 0; i < len(key); i++ { + crc = (crc << 8) ^ crc16tab[(uint8(crc>>8)^key[i])&0x00FF] + } + return crc +} From 40164d31b7712987a496492df454247e2a9de4b6 Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:08:09 -0400 Subject: [PATCH 05/11] downgrade back to 1.22 --- go.mod | 5 +---- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6977630..f724e99 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/dcbickfo/redcache -go 1.23.0 - -toolchain go1.23.4 +go 1.22.4 require ( github.com/google/go-cmp v0.7.0 @@ -14,7 +12,6 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e5bd629..9f175f3 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 8a165d8fbf78fc41161fb63c011ba24749962a53 Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:38:22 -0400 Subject: [PATCH 06/11] thread multi commands --- cacheaside.go | 33 ++++++++++++++++++++++++--------- go.mod | 1 + go.sum | 4 ++-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/cacheaside.go b/cacheaside.go index 591d485..bddd82a 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -12,6 +12,7 @@ import ( "github.com/dcbickfo/redcache/internal/syncx" "github.com/google/uuid" "github.com/redis/rueidis" + "golang.org/x/sync/errgroup" ) type CacheAside struct { @@ -374,18 +375,32 @@ func (rca *CacheAside) setMultiWithLock(ctx context.Context, ttl time.Duration, } out := make([]string, 0) + keyByStmt := make([][]string, len(stmts)) + i := 0 + eg, ctx := errgroup.WithContext(ctx) for _, kos := range stmts { - setResps := setKeyLua.ExecMulti(ctx, rca.client, kos.setStmts...) - for i, resp := range setResps { - err := resp.Error() - if err != nil { - if !rueidis.IsRedisNil(err) { - return nil, err + ii := i + eg.Go(func() error { + setResps := setKeyLua.ExecMulti(ctx, rca.client, kos.setStmts...) + for j, resp := range setResps { + err := resp.Error() + if err != nil { + if !rueidis.IsRedisNil(err) { + return err + } + continue } - continue + keyByStmt[ii] = append(out, kos.keyOrder[j]) } - out = append(out, kos.keyOrder[i]) - } + return nil + }) + i += 1 + } + if err := eg.Wait(); err != nil { + return nil, err + } + for _, keys := range keyByStmt { + out = append(out, keys...) } return out, nil } diff --git a/go.mod b/go.mod index f724e99..07d437e 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/uuid v1.6.0 github.com/redis/rueidis v1.0.55 github.com/stretchr/testify v1.10.0 + golang.org/x/sync v0.10.0 ) require ( diff --git a/go.sum b/go.sum index 9f175f3..0a1e8ea 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 8bb96121aa1eeb796c8151ccc1779376eb5be55b Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:51:19 -0400 Subject: [PATCH 07/11] address more multi issues --- cacheaside.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/cacheaside.go b/cacheaside.go index bddd82a..4f7f723 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -5,6 +5,7 @@ import ( "errors" "strconv" "strings" + "sync" "time" "github.com/dcbickfo/redcache/internal/cmdx" @@ -113,7 +114,13 @@ func (rca *CacheAside) Del(ctx context.Context, key string) error { } func (rca *CacheAside) DelMulti(ctx context.Context, keys ...string) error { - return rca.client.Do(ctx, rca.client.B().Del().Key(keys...).Build()).Error() + resps := rca.client.DoMulti(ctx, rca.client.B().Del().Key(keys...).Build()) + for _, resp := range resps { + if err := resp.Error(); err != nil { + return err + } + } + return nil } var errNotFound = errors.New("not found") @@ -409,12 +416,21 @@ func (rca *CacheAside) unlockMulti(ctx context.Context, lockVals map[string]stri if len(lockVals) == 0 { return } - delStmts := make([]rueidis.LuaExec, 0, len(lockVals)) + delStmts := make(map[uint16][]rueidis.LuaExec) for key, lockVal := range lockVals { - delStmts = append(delStmts, rueidis.LuaExec{ + slot := cmdx.Slot(key) + delStmts[slot] = append(delStmts[slot], rueidis.LuaExec{ Keys: []string{key}, Args: []string{lockVal}, }) } - delKeyLua.ExecMulti(ctx, rca.client, delStmts...) + wg := sync.WaitGroup{} + for _, stmts := range delStmts { + wg.Add(1) + go func() { + delKeyLua.ExecMulti(ctx, rca.client, stmts...) + wg.Done() + }() + } + wg.Wait() } From 29227057a4b80b29aecde6b7981a444f006c0732 Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:52:08 -0400 Subject: [PATCH 08/11] use defer --- cacheaside.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cacheaside.go b/cacheaside.go index 4f7f723..45be524 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -428,8 +428,8 @@ func (rca *CacheAside) unlockMulti(ctx context.Context, lockVals map[string]stri for _, stmts := range delStmts { wg.Add(1) go func() { + defer wg.Done() delKeyLua.ExecMulti(ctx, rca.client, stmts...) - wg.Done() }() } wg.Wait() From 2754dda3d972234469e9b6bd7de9c91a76cacf70 Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:56:33 -0400 Subject: [PATCH 09/11] separate statements on delete --- cacheaside.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cacheaside.go b/cacheaside.go index 45be524..6d569b2 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -114,7 +114,11 @@ func (rca *CacheAside) Del(ctx context.Context, key string) error { } func (rca *CacheAside) DelMulti(ctx context.Context, keys ...string) error { - resps := rca.client.DoMulti(ctx, rca.client.B().Del().Key(keys...).Build()) + cmds := make(rueidis.Commands, 0, len(keys)) + for i, key := range keys { + cmds[i] = rca.client.B().Del().Key(key).Build() + } + resps := rca.client.DoMulti(ctx, cmds...) for _, resp := range resps { if err := resp.Error(); err != nil { return err From f4c7906726a80715089d39e488735e9cacb405ef Mon Sep 17 00:00:00 2001 From: David Bickford Date: Thu, 13 Mar 2025 10:58:04 -0400 Subject: [PATCH 10/11] fix looping through array --- cacheaside.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cacheaside.go b/cacheaside.go index 6d569b2..c72b488 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -115,8 +115,8 @@ func (rca *CacheAside) Del(ctx context.Context, key string) error { func (rca *CacheAside) DelMulti(ctx context.Context, keys ...string) error { cmds := make(rueidis.Commands, 0, len(keys)) - for i, key := range keys { - cmds[i] = rca.client.B().Del().Key(key).Build() + for _, key := range keys { + cmds = append(cmds, rca.client.B().Del().Key(key).Build()) } resps := rca.client.DoMulti(ctx, cmds...) for _, resp := range resps { From 54537af0f8c8856e99c93c220b54366b83bd2efa Mon Sep 17 00:00:00 2001 From: David Bickford Date: Fri, 21 Mar 2025 15:13:25 -0400 Subject: [PATCH 11/11] bug fix --- cacheaside.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cacheaside.go b/cacheaside.go index c72b488..b178198 100644 --- a/cacheaside.go +++ b/cacheaside.go @@ -310,6 +310,11 @@ func (rca *CacheAside) trySetMultiKeyFn( } }() + // Case where we were unable to get any locks + if len(lockVals) == 0 { + return res, nil + } + vals, err := fn(ctx, mapsx.Keys(lockVals)) if err != nil { return nil, err