Module: Sentiero::Stores::Redis::Lua
- Defined in:
- lib/sentiero/stores/redis/lua.rb
Overview
EVAL scripts for operations that need atomicity a MULTI/EXEC pipeline can't give a read-modify-write (or delete-if-empty) across keys.
Constant Summary collapse
- SAVE_METADATA =
<<~LUA local key = KEYS[1] if redis.call("EXISTS", key) == 0 then return 0 end local existing_json = redis.call("HGET", key, "metadata") local existing = existing_json and cjson.decode(existing_json) or {} local new_data = cjson.decode(ARGV[1]) for k, v in pairs(new_data) do existing[k] = v end redis.call("HSET", key, "metadata", cjson.encode(existing)) return 1 LUA
- EVICT_SESSION =
Atomic so a concurrent save_events adding a new window mid-delete can't orphan its events key (which a read-then-pipeline sequence would miss).
<<~LUA local windows_key = KEYS[1] local session_key = KEYS[2] local sessions_key = KEYS[3] local session_id = ARGV[1] local prefix = ARGV[2] for _, window_id in ipairs(redis.call("SMEMBERS", windows_key)) do redis.call("DEL", prefix .. "events:" .. session_id .. ":" .. window_id) end redis.call("DEL", windows_key) redis.call("DEL", session_key) redis.call("ZREM", sessions_key, session_id) LUA
- DELETE_WINDOW =
<<~LUA local events_key = KEYS[1] local windows_key = KEYS[2] local session_key = KEYS[3] local sessions_key = KEYS[4] local window_id = ARGV[1] local session_id = ARGV[2] local now = ARGV[3] redis.call("DEL", events_key) redis.call("SREM", windows_key, window_id) local remaining = redis.call("SCARD", windows_key) if remaining == 0 then redis.call("DEL", session_key) redis.call("DEL", windows_key) redis.call("ZREM", sessions_key, session_id) else redis.call("HSET", session_key, "updated_at", now) redis.call("ZADD", sessions_key, tonumber(now), session_id) end return remaining LUA
- PROBLEM_UPSERT =
Atomic problem upsert: a read-then-write in Ruby lost concurrent count/last_seen updates for the same fingerprint. The count+1 / min first_seen / max last_seen / reopen-if-resolved logic mirrors ErrorStore#touched_problem_attrs (kept in Ruby for Memory/File); the new record is built in Ruby (new_problem_attrs) and passed in pre-serialized. Returns 1 when it created a new problem, 0 when it updated an existing one.
<<~LUA local prob_key, problems_key, proj_key = KEYS[1], KEYS[2], KEYS[3] local fp, ts, message, new_json = ARGV[1], tonumber(ARGV[2]), ARGV[3], ARGV[4] local existing_json = redis.call("GET", prob_key) if existing_json then local p = cjson.decode(existing_json) p.count = (p.count or 0) + 1 if ts < p.first_seen then p.first_seen = ts end if ts > p.last_seen then p.last_seen = ts end p.message = message if p.status == "resolved" then p.status = "open" p.resolved_at = nil end redis.call("SET", prob_key, cjson.encode(p)) redis.call("ZADD", problems_key, p.last_seen, fp) return 0 else redis.call("SET", prob_key, new_json) redis.call("ZADD", problems_key, ts, fp) redis.call("SADD", proj_key, fp) return 1 end LUA
- UPDATE_TIMESTAMPS =
<<~LUA local key = KEYS[1] local batch_min = ARGV[1] local batch_max = ARGV[2] if batch_min ~= "" then local current = redis.call("HGET", key, "first_event_at") if not current or tonumber(batch_min) < tonumber(current) then redis.call("HSET", key, "first_event_at", batch_min) end end if batch_max ~= "" then local current = redis.call("HGET", key, "last_event_at") if not current or tonumber(batch_max) > tonumber(current) then redis.call("HSET", key, "last_event_at", batch_max) end end LUA