use std::sync::LazyLock; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use redis::Script; use crate::error::{CacheError, CacheResult}; use crate::keys; use crate::kv::{Cache, millis}; const RELEASE_CODE: &str = r#" if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0 "#; const REFRESH_CODE: &str = r#" if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) end return 0 "#; const COMPARE_AND_SET_CODE: &str = r#" local current = redis.call('GET', KEYS[1]) local matches = current == ARGV[1] if ARGV[4] == '1' and current == false then matches = true end if not matches then return 0 end redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3]) return 1 "#; const INCR_EX_CODE: &str = r#" local value = redis.call('INCR', KEYS[1]) if value == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[1]) end return value "#; /// 验证码原子消费:值匹配时同时删除验证码与尝试计数,匹配失败不动键。 const CONSUME_MATCHING_CODE: &str = r#" if redis.call('GET', KEYS[1]) == ARGV[1] then redis.call('DEL', KEYS[1]) redis.call('DEL', KEYS[2]) return 1 end return 0 "#; const GETSET_EX_CODE: &str = r#" local previous = redis.call('GET', KEYS[1]) redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2]) return previous "#; const LPUSH_CAPPED_CODE: &str = r#" local length = redis.call('LPUSH', KEYS[1], ARGV[1]) local limit = tonumber(ARGV[2]) if limit > 0 and length > limit then redis.call('LTRIM', KEYS[1], 0, limit - 1) return limit end return length "#; const HSET_EX_CODE: &str = r#" redis.call('HSET', KEYS[1], ARGV[1], ARGV[2]) local ttl = tonumber(ARGV[3]) if ttl > 0 then redis.call('PEXPIRE', KEYS[1], ttl) end return 1 "#; const LOCK_MANY_CODE: &str = r#" for index = 1, #KEYS do if redis.call('SET', KEYS[index], ARGV[1], 'PX', ARGV[2], 'NX') == false then for released = 1, index - 1 do redis.call('DEL', KEYS[released]) end return 0 end end return 1 "#; const RATE_LIMIT_CODE: &str = r#" -- 时间取自 Redis 服务器(TIME),避免多实例应用时钟漂移破坏窗口。 local t = redis.call('TIME') local now = t[1] * 1000 + math.floor(t[2] / 1000) local window = tonumber(ARGV[1]) local limit = tonumber(ARGV[2]) redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, now - window) local count = redis.call('ZCARD', KEYS[1]) if count >= limit then return {0, count} end redis.call('ZADD', KEYS[1], now, ARGV[3]) redis.call('PEXPIRE', KEYS[1], window) return {1, count + 1} "#; static RELEASE: LazyLock