@@ -27,35 +27,49 @@ func WithLoadSHA1(enabled bool) LuaOption {
2727// NewLuaScript creates a Lua instance whose Lua.Exec uses EVALSHA and EVAL.
2828// By default, SHA-1 is calculated client-side. Use WithLoadSHA1(true) option to load SHA-1 from Redis instead.
2929func NewLuaScript (script string , opts ... LuaOption ) * Lua {
30- return newLuaScript (script , false , false , opts ... )
30+ return newLuaScript (script , false , false , false , opts ... )
3131}
3232
3333// NewLuaScriptReadOnly creates a Lua instance whose Lua.Exec uses EVALSHA_RO and EVAL_RO.
3434// By default, SHA-1 is calculated client-side. Use WithLoadSHA1(true) option to load SHA-1 from Redis instead.
3535func NewLuaScriptReadOnly (script string , opts ... LuaOption ) * Lua {
36- return newLuaScript (script , true , false , opts ... )
36+ return newLuaScript (script , true , false , false , opts ... )
3737}
3838
3939// NewLuaScriptNoSha creates a Lua instance whose Lua.Exec uses EVAL only (never EVALSHA).
4040// No SHA-1 is calculated or loaded. The script is sent to the server every time. Use this when you want
4141// to avoid SHA-1 entirely (e.g., to fully avoid hash collision concerns).
4242func NewLuaScriptNoSha (script string ) * Lua {
43- return newLuaScript (script , false , true )
43+ return newLuaScript (script , false , true , false )
4444}
4545
4646// NewLuaScriptReadOnlyNoSha creates a Lua instance whose Lua.Exec uses EVAL_RO only (never EVALSHA_RO).
4747// No SHA-1 is calculated or loaded. The script is sent to the server every time. Use this when you want
4848// to avoid SHA-1 entirely (e.g., to fully avoid hash collision concerns).
4949func NewLuaScriptReadOnlyNoSha (script string ) * Lua {
50- return newLuaScript (script , true , true )
50+ return newLuaScript (script , true , true , false )
5151}
5252
53- func newLuaScript (script string , readonly bool , noSha1 bool , opts ... LuaOption ) * Lua {
53+ // NewLuaScriptRetryable creates a retryable Lua instance whose Lua.Exec uses EVALSHA and EVAL.
54+ // By default, SHA-1 is calculated client-side. Use WithLoadSHA1(true) option to load SHA-1 from Redis instead.
55+ func NewLuaScriptRetryable (script string , opts ... LuaOption ) * Lua {
56+ return newLuaScript (script , false , false , true , opts ... )
57+ }
58+
59+ // NewLuaScriptNoShaRetryable creates a retryable Lua instance whose Lua.Exec uses EVAL only (never EVALSHA).
60+ // No SHA-1 is calculated or loaded. The script is sent to the server every time. Use this when you want
61+ // to avoid SHA-1 entirely (e.g., to fully avoid hash collision concerns).
62+ func NewLuaScriptNoShaRetryable (script string ) * Lua {
63+ return newLuaScript (script , false , true , true )
64+ }
65+
66+ func newLuaScript (script string , readonly bool , noSha1 , retryable bool , opts ... LuaOption ) * Lua {
5467 l := & Lua {
55- script : script ,
56- maxp : runtime .GOMAXPROCS (0 ),
57- readonly : readonly ,
58- noSha1 : noSha1 ,
68+ script : script ,
69+ maxp : runtime .GOMAXPROCS (0 ),
70+ readonly : readonly ,
71+ noSha1 : noSha1 ,
72+ retryable : retryable ,
5973 }
6074 for _ , opt := range opts {
6175 opt (l )
@@ -70,14 +84,15 @@ func newLuaScript(script string, readonly bool, noSha1 bool, opts ...LuaOption)
7084
7185// Lua represents a redis lua script. It should be created from the NewLuaScript() or NewLuaScriptReadOnly().
7286type Lua struct {
73- script string
74- sha1 string
75- sha1Call call
76- maxp int
77- sha1Mu sync.RWMutex
78- readonly bool
79- noSha1 bool
80- loadSha1 bool
87+ script string
88+ sha1 string
89+ sha1Call call
90+ maxp int
91+ sha1Mu sync.RWMutex
92+ readonly bool
93+ noSha1 bool
94+ loadSha1 bool
95+ retryable bool
8196}
8297
8398// Exec the script to the given Client.
@@ -99,7 +114,7 @@ func (s *Lua) Exec(ctx context.Context, c Client, keys, args []string) (resp Red
99114 // If not loaded yet, use singleflight to load it.
100115 if scriptSha1 == "" {
101116 err := s .sha1Call .Do (ctx , func () error {
102- result := c .Do (ctx , c .B ().ScriptLoad ().Script (s .script ).Build ())
117+ result := c .Do (ctx , c .B ().ScriptLoad ().Script (s .script ).Build (). ToRetryable () )
103118 if shaStr , err := result .ToString (); err == nil {
104119 s .sha1Mu .Lock ()
105120 s .sha1 = shaStr
@@ -126,7 +141,7 @@ func (s *Lua) Exec(ctx context.Context, c Client, keys, args []string) (resp Red
126141 if s .readonly {
127142 resp = c .Do (ctx , c .B ().EvalshaRo ().Sha1 (scriptSha1 ).Numkeys (int64 (len (keys ))).Key (keys ... ).Arg (args ... ).Build ())
128143 } else {
129- resp = c .Do (ctx , c .B ().Evalsha ().Sha1 (scriptSha1 ).Numkeys (int64 (len (keys ))).Key (keys ... ).Arg (args ... ).Build ())
144+ resp = c .Do (ctx , s . mayRetryable ( c .B ().Evalsha ().Sha1 (scriptSha1 ).Numkeys (int64 (len (keys ))).Key (keys ... ).Arg (args ... ).Build () ))
130145 }
131146 err , isErr := IsRedisErr (resp .Error ())
132147 isNoScript = isErr && err .IsNoScript ()
@@ -135,7 +150,7 @@ func (s *Lua) Exec(ctx context.Context, c Client, keys, args []string) (resp Red
135150 if s .readonly {
136151 resp = c .Do (ctx , c .B ().EvalRo ().Script (s .script ).Numkeys (int64 (len (keys ))).Key (keys ... ).Arg (args ... ).Build ())
137152 } else {
138- resp = c .Do (ctx , c .B ().Eval ().Script (s .script ).Numkeys (int64 (len (keys ))).Key (keys ... ).Arg (args ... ).Build ())
153+ resp = c .Do (ctx , s . mayRetryable ( c .B ().Eval ().Script (s .script ).Numkeys (int64 (len (keys ))).Key (keys ... ).Arg (args ... ).Build () ))
139154 }
140155 }
141156 return resp
@@ -159,7 +174,7 @@ func (s *Lua) ExecMulti(ctx context.Context, c Client, multi ...LuaExec) (resp [
159174 var e atomic.Value
160175 var sha1Result atomic.Value
161176 util .ParallelVals (s .maxp , c .Nodes (), func (n Client ) {
162- result := n .Do (ctx , n .B ().ScriptLoad ().Script (s .script ).Build ())
177+ result := n .Do (ctx , n .B ().ScriptLoad ().Script (s .script ).Build (). ToRetryable () )
163178 if err := result .Error (); err != nil {
164179 e .CompareAndSwap (nil , & errs {error : err })
165180 } else if s .loadSha1 {
@@ -200,15 +215,22 @@ func (s *Lua) ExecMulti(ctx context.Context, c Client, multi ...LuaExec) (resp [
200215 if s .readonly {
201216 cmds = append (cmds , c .B ().EvalshaRo ().Sha1 (scriptSha1 ).Numkeys (int64 (len (m .Keys ))).Key (m .Keys ... ).Arg (m .Args ... ).Build ())
202217 } else {
203- cmds = append (cmds , c .B ().Evalsha ().Sha1 (scriptSha1 ).Numkeys (int64 (len (m .Keys ))).Key (m .Keys ... ).Arg (m .Args ... ).Build ())
218+ cmds = append (cmds , s . mayRetryable ( c .B ().Evalsha ().Sha1 (scriptSha1 ).Numkeys (int64 (len (m .Keys ))).Key (m .Keys ... ).Arg (m .Args ... ).Build () ))
204219 }
205220 } else {
206221 if s .readonly {
207222 cmds = append (cmds , c .B ().EvalRo ().Script (s .script ).Numkeys (int64 (len (m .Keys ))).Key (m .Keys ... ).Arg (m .Args ... ).Build ())
208223 } else {
209- cmds = append (cmds , c .B ().Eval ().Script (s .script ).Numkeys (int64 (len (m .Keys ))).Key (m .Keys ... ).Arg (m .Args ... ).Build ())
224+ cmds = append (cmds , s . mayRetryable ( c .B ().Eval ().Script (s .script ).Numkeys (int64 (len (m .Keys ))).Key (m .Keys ... ).Arg (m .Args ... ).Build () ))
210225 }
211226 }
212227 }
213228 return c .DoMulti (ctx , cmds ... )
214229}
230+
231+ func (s * Lua ) mayRetryable (cmd Completed ) Completed {
232+ if s .retryable {
233+ cmd = cmd .ToRetryable ()
234+ }
235+ return cmd
236+ }
0 commit comments