Files
zhuangang/topics/backend/redis/examples/stock-lock-ratelimit/stock.go
T

41 lines
1012 B
Go

package stocklock
import (
"context"
"errors"
"github.com/redis/go-redis/v9"
)
var (
ErrSoldOut = errors.New("库存不足")
ErrDuplicate = errors.New("每人限购一件,已经抢过了")
)
// 原子地:检查是否已购买 → 检查库存 → 扣库存 → 记录用户
var reserveScript = redis.NewScript(`
if redis.call("SISMEMBER", KEYS[2], ARGV[1]) == 1 then
return -2
end
local left = tonumber(redis.call("GET", KEYS[1]) or "0")
if left <= 0 then
return -1
end
redis.call("DECR", KEYS[1])
redis.call("SADD", KEYS[2], ARGV[1])
return left - 1`)
// Reserve 预扣库存。成功后应把订单消息发到 MQ,由消费者异步落库。
func Reserve(ctx context.Context, rdb *redis.Client, sku, userID string) (left int, err error) {
n, err := reserveScript.Run(ctx, rdb, []string{"stock:" + sku, "stock:" + sku + ":buyers"}, userID).Int()
switch {
case err != nil:
return 0, err
case n == -1:
return 0, ErrSoldOut
case n == -2:
return 0, ErrDuplicate
}
return n, nil
}