feat: new subject cache
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
--[[
|
||||
Release a FullSubject write reservation if the token matches.
|
||||
|
||||
KEYS:
|
||||
[1] reserveKey
|
||||
|
||||
ARGV:
|
||||
[1] token
|
||||
|
||||
Returns:
|
||||
"RELEASED" = reservation matched and was deleted
|
||||
"SKIPPED" = key missing or token mismatch
|
||||
]]
|
||||
|
||||
local reserveKey = KEYS[1]
|
||||
local token = ARGV[1]
|
||||
|
||||
local existingToken = redis.call("GET", reserveKey)
|
||||
if existingToken ~= token then
|
||||
return "SKIPPED"
|
||||
end
|
||||
|
||||
redis.call("DEL", reserveKey)
|
||||
return "RELEASED"
|
||||
@@ -0,0 +1,50 @@
|
||||
--[[
|
||||
Reserve a FullSubject write so only one non-overwrite writer proceeds.
|
||||
|
||||
KEYS:
|
||||
[1] subjectKey
|
||||
[2] reserveKey
|
||||
[3] guardKey
|
||||
|
||||
ARGV:
|
||||
[1] token
|
||||
[2] reserveTtl
|
||||
[3] overwrite - "true" to bypass reservation, "false" to reserve if missing
|
||||
[4] fetchTimeMs
|
||||
|
||||
Returns:
|
||||
"RESERVED" = caller may proceed with the write
|
||||
"CACHE_EXISTS" = subject already exists or another writer already reserved it
|
||||
"STALE_WRITE" = guard exists with newer timestamp than this write
|
||||
]]
|
||||
|
||||
local subjectKey = KEYS[1]
|
||||
local reserveKey = KEYS[2]
|
||||
local guardKey = KEYS[3]
|
||||
local token = ARGV[1]
|
||||
local reserveTtl = tonumber(ARGV[2])
|
||||
local overwrite = ARGV[3] == "true"
|
||||
local fetchTimeMs = tonumber(ARGV[4])
|
||||
|
||||
if overwrite then
|
||||
return "RESERVED"
|
||||
end
|
||||
|
||||
local guardTime = redis.call("GET", guardKey)
|
||||
if guardTime and guardTime ~= cjson.null and fetchTimeMs then
|
||||
local guardTimeNum = tonumber(guardTime)
|
||||
if guardTimeNum and guardTimeNum > fetchTimeMs then
|
||||
return "STALE_WRITE"
|
||||
end
|
||||
end
|
||||
|
||||
if redis.call("EXISTS", subjectKey) == 1 then
|
||||
return "CACHE_EXISTS"
|
||||
end
|
||||
|
||||
local reserved = redis.call("SET", reserveKey, token, "EX", reserveTtl, "NX")
|
||||
if not reserved then
|
||||
return "CACHE_EXISTS"
|
||||
end
|
||||
|
||||
return "RESERVED"
|
||||
@@ -0,0 +1,50 @@
|
||||
--[[
|
||||
Lua Script: Update Customer Data in FullSubject V2 Redis Cache
|
||||
|
||||
Atomically updates top-level customer fields in the cached FullSubject JSON.
|
||||
|
||||
KEYS[1] = FullSubject cache key
|
||||
|
||||
ARGV[1] = updates JSON object
|
||||
ARGV[2] = cache TTL in seconds
|
||||
ARGV[3] = current timestamp in ms
|
||||
|
||||
Returns JSON:
|
||||
{ "success": true, "updated_fields": ["name", "email"] }
|
||||
or
|
||||
{ "success": false, "cache_miss": true }
|
||||
]]
|
||||
|
||||
local subject_key = KEYS[1]
|
||||
local updates = cjson.decode(ARGV[1])
|
||||
local cache_ttl = tonumber(ARGV[2])
|
||||
local now_ms = tonumber(ARGV[3])
|
||||
|
||||
local has_updates = false
|
||||
for _ in pairs(updates) do
|
||||
has_updates = true
|
||||
break
|
||||
end
|
||||
|
||||
if not has_updates then
|
||||
return cjson.encode({ success = true, updated_fields = {} })
|
||||
end
|
||||
|
||||
local current_raw = redis.call("GET", subject_key)
|
||||
if not current_raw then
|
||||
return cjson.encode({ success = false, cache_miss = true })
|
||||
end
|
||||
|
||||
local cached = cjson.decode(current_raw)
|
||||
local updated_fields = {}
|
||||
|
||||
for field_name, field_value in pairs(updates) do
|
||||
cached.customer[field_name] = field_value
|
||||
table.insert(updated_fields, field_name)
|
||||
end
|
||||
|
||||
-- cached._cachedAt = now_ms
|
||||
|
||||
redis.call("SET", subject_key, cjson.encode(cached), "EX", cache_ttl)
|
||||
|
||||
return cjson.encode({ success = true, updated_fields = updated_fields })
|
||||
Reference in New Issue
Block a user