feat: new subject cache

This commit is contained in:
John Yeo
2026-04-13 10:24:58 +01:00
parent 9489cf90e9
commit c8e154fe53
108 changed files with 8684 additions and 1983 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 })