mirror of
https://github.com/zeromicro/go-zero.git
synced 2025-02-02 16:28:39 +08:00
feat: migrate lua script to lua file (#4069)
This commit is contained in:
parent
36088ea0d4
commit
62c88a84d1
@ -2,6 +2,7 @@ package bloom
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
@ -17,19 +18,15 @@ var (
|
||||
// ErrTooLargeOffset indicates the offset is too large in bitset.
|
||||
ErrTooLargeOffset = errors.New("too large offset")
|
||||
|
||||
setScript = redis.NewScript(`
|
||||
for _, offset in ipairs(ARGV) do
|
||||
redis.call("setbit", KEYS[1], offset, 1)
|
||||
end
|
||||
`)
|
||||
testScript = redis.NewScript(`
|
||||
for _, offset in ipairs(ARGV) do
|
||||
if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
`)
|
||||
//go:embed setscript.lua
|
||||
setScript string
|
||||
|
||||
scriptSet = redis.NewScript(setScript)
|
||||
|
||||
//go:embed testscript.lua
|
||||
testScript string
|
||||
|
||||
scriptTest = redis.NewScript(testScript)
|
||||
)
|
||||
|
||||
type (
|
||||
@ -129,7 +126,7 @@ func (r *redisBitSet) check(ctx context.Context, offsets []uint) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := r.store.ScriptRunCtx(ctx, testScript, []string{r.key}, args)
|
||||
resp, err := r.store.ScriptRunCtx(ctx, scriptTest, []string{r.key}, args)
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
@ -161,7 +158,7 @@ func (r *redisBitSet) set(ctx context.Context, offsets []uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.store.ScriptRunCtx(ctx, setScript, []string{r.key}, args)
|
||||
_, err = r.store.ScriptRunCtx(ctx, scriptSet, []string{r.key}, args)
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil
|
||||
}
|
||||
|
3
core/bloom/setscript.lua
Normal file
3
core/bloom/setscript.lua
Normal file
@ -0,0 +1,3 @@
|
||||
for _, offset in ipairs(ARGV) do
|
||||
redis.call("setbit", KEYS[1], offset, 1)
|
||||
end
|
6
core/bloom/testscript.lua
Normal file
6
core/bloom/testscript.lua
Normal file
@ -0,0 +1,6 @@
|
||||
for _, offset in ipairs(ARGV) do
|
||||
if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
@ -2,6 +2,7 @@ package limit
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -28,20 +29,10 @@ var (
|
||||
// ErrUnknownCode is an error that represents unknown status code.
|
||||
ErrUnknownCode = errors.New("unknown status code")
|
||||
|
||||
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
|
||||
periodScript = redis.NewScript(`local limit = tonumber(ARGV[1])
|
||||
local window = tonumber(ARGV[2])
|
||||
local current = redis.call("INCRBY", KEYS[1], 1)
|
||||
if current == 1 then
|
||||
redis.call("expire", KEYS[1], window)
|
||||
end
|
||||
if current < limit then
|
||||
return 1
|
||||
elseif current == limit then
|
||||
return 2
|
||||
else
|
||||
return 0
|
||||
end`)
|
||||
//go:embed periodscript.lua
|
||||
periodScript string
|
||||
|
||||
scriptPeriod = redis.NewScript(periodScript)
|
||||
)
|
||||
|
||||
type (
|
||||
@ -82,7 +73,7 @@ func (h *PeriodLimit) Take(key string) (int, error) {
|
||||
|
||||
// TakeCtx requests a permit with context, it returns the permit state.
|
||||
func (h *PeriodLimit) TakeCtx(ctx context.Context, key string) (int, error) {
|
||||
resp, err := h.limitStore.ScriptRunCtx(ctx, periodScript, []string{h.keyPrefix + key}, []string{
|
||||
resp, err := h.limitStore.ScriptRunCtx(ctx, scriptPeriod, []string{h.keyPrefix + key}, []string{
|
||||
strconv.Itoa(h.quota),
|
||||
strconv.Itoa(h.calcExpireSeconds()),
|
||||
})
|
||||
|
14
core/limit/periodscript.lua
Normal file
14
core/limit/periodscript.lua
Normal file
@ -0,0 +1,14 @@
|
||||
-- to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
|
||||
local limit = tonumber(ARGV[1])
|
||||
local window = tonumber(ARGV[2])
|
||||
local current = redis.call("INCRBY", KEYS[1], 1)
|
||||
if current == 1 then
|
||||
redis.call("expire", KEYS[1], window)
|
||||
end
|
||||
if current < limit then
|
||||
return 1
|
||||
elseif current == limit then
|
||||
return 2
|
||||
else
|
||||
return 0
|
||||
end
|
@ -2,6 +2,7 @@ package limit
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
@ -20,37 +21,12 @@ const (
|
||||
pingInterval = time.Millisecond * 100
|
||||
)
|
||||
|
||||
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
|
||||
// KEYS[1] as tokens_key
|
||||
// KEYS[2] as timestamp_key
|
||||
var script = redis.NewScript(`local rate = tonumber(ARGV[1])
|
||||
local capacity = tonumber(ARGV[2])
|
||||
local now = tonumber(ARGV[3])
|
||||
local requested = tonumber(ARGV[4])
|
||||
local fill_time = capacity/rate
|
||||
local ttl = math.floor(fill_time*2)
|
||||
local last_tokens = tonumber(redis.call("get", KEYS[1]))
|
||||
if last_tokens == nil then
|
||||
last_tokens = capacity
|
||||
end
|
||||
var (
|
||||
//go:embed tokenscript.lua
|
||||
tokenScript string
|
||||
|
||||
local last_refreshed = tonumber(redis.call("get", KEYS[2]))
|
||||
if last_refreshed == nil then
|
||||
last_refreshed = 0
|
||||
end
|
||||
|
||||
local delta = math.max(0, now-last_refreshed)
|
||||
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
|
||||
local allowed = filled_tokens >= requested
|
||||
local new_tokens = filled_tokens
|
||||
if allowed then
|
||||
new_tokens = filled_tokens - requested
|
||||
end
|
||||
|
||||
redis.call("setex", KEYS[1], ttl, new_tokens)
|
||||
redis.call("setex", KEYS[2], ttl, now)
|
||||
|
||||
return allowed`)
|
||||
scriptToken = redis.NewScript(tokenScript)
|
||||
)
|
||||
|
||||
// A TokenLimiter controls how frequently events are allowed to happen with in one second.
|
||||
type TokenLimiter struct {
|
||||
@ -112,7 +88,7 @@ func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) boo
|
||||
}
|
||||
|
||||
resp, err := lim.store.ScriptRunCtx(ctx,
|
||||
script,
|
||||
scriptToken,
|
||||
[]string{
|
||||
lim.tokenKey,
|
||||
lim.timestampKey,
|
||||
|
31
core/limit/tokenscript.lua
Normal file
31
core/limit/tokenscript.lua
Normal file
@ -0,0 +1,31 @@
|
||||
-- to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
|
||||
-- KEYS[1] as tokens_key
|
||||
-- KEYS[2] as timestamp_key
|
||||
local rate = tonumber(ARGV[1])
|
||||
local capacity = tonumber(ARGV[2])
|
||||
local now = tonumber(ARGV[3])
|
||||
local requested = tonumber(ARGV[4])
|
||||
local fill_time = capacity/rate
|
||||
local ttl = math.floor(fill_time*2)
|
||||
local last_tokens = tonumber(redis.call("get", KEYS[1]))
|
||||
if last_tokens == nil then
|
||||
last_tokens = capacity
|
||||
end
|
||||
|
||||
local last_refreshed = tonumber(redis.call("get", KEYS[2]))
|
||||
if last_refreshed == nil then
|
||||
last_refreshed = 0
|
||||
end
|
||||
|
||||
local delta = math.max(0, now-last_refreshed)
|
||||
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
|
||||
local allowed = filled_tokens >= requested
|
||||
local new_tokens = filled_tokens
|
||||
if allowed then
|
||||
new_tokens = filled_tokens - requested
|
||||
end
|
||||
|
||||
redis.call("setex", KEYS[1], ttl, new_tokens)
|
||||
redis.call("setex", KEYS[2], ttl, now)
|
||||
|
||||
return allowed
|
5
core/stores/redis/delscript.lua
Normal file
5
core/stores/redis/delscript.lua
Normal file
@ -0,0 +1,5 @@
|
||||
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("DEL", KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end
|
6
core/stores/redis/lockscript.lua
Normal file
6
core/stores/redis/lockscript.lua
Normal file
@ -0,0 +1,6 @@
|
||||
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
|
||||
return "OK"
|
||||
else
|
||||
return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
|
||||
end
|
@ -2,6 +2,7 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
@ -20,17 +21,15 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
lockScript = NewScript(`if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
|
||||
return "OK"
|
||||
else
|
||||
return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
|
||||
end`)
|
||||
delScript = NewScript(`if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("DEL", KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end`)
|
||||
//go:embed lockscript.lua
|
||||
lockScript string
|
||||
|
||||
scriptLock = NewScript(lockScript)
|
||||
|
||||
//go:embed delscript.lua
|
||||
delScript string
|
||||
|
||||
scriptDel = NewScript(delScript)
|
||||
)
|
||||
|
||||
// A RedisLock is a redis lock.
|
||||
@ -62,7 +61,7 @@ func (rl *RedisLock) Acquire() (bool, error) {
|
||||
// AcquireCtx acquires the lock with the given ctx.
|
||||
func (rl *RedisLock) AcquireCtx(ctx context.Context) (bool, error) {
|
||||
seconds := atomic.LoadUint32(&rl.seconds)
|
||||
resp, err := rl.store.ScriptRunCtx(ctx, lockScript, []string{rl.key}, []string{
|
||||
resp, err := rl.store.ScriptRunCtx(ctx, scriptLock, []string{rl.key}, []string{
|
||||
rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance),
|
||||
})
|
||||
if errors.Is(err, red.Nil) {
|
||||
@ -90,7 +89,7 @@ func (rl *RedisLock) Release() (bool, error) {
|
||||
|
||||
// ReleaseCtx releases the lock with the given ctx.
|
||||
func (rl *RedisLock) ReleaseCtx(ctx context.Context) (bool, error) {
|
||||
resp, err := rl.store.ScriptRunCtx(ctx, delScript, []string{rl.key}, []string{rl.id})
|
||||
resp, err := rl.store.ScriptRunCtx(ctx, scriptDel, []string{rl.key}, []string{rl.id})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user