fix : reaible vistor count
This commit is contained in:
+29
-5
@@ -36,16 +36,19 @@ async function purge(
|
||||
|
||||
export async function updateVisitorPresence({
|
||||
tabId,
|
||||
browserId,
|
||||
guideId,
|
||||
active,
|
||||
}: {
|
||||
tabId: string;
|
||||
browserId: string;
|
||||
guideId: string | null;
|
||||
active: boolean;
|
||||
}) {
|
||||
const redis = await getRedisClient();
|
||||
const activeKey = `${prefix()}:visitors`;
|
||||
const scopeKey = `${prefix()}:visitor-scopes`;
|
||||
const browserKey = `${prefix()}:visitor-browsers`;
|
||||
const now = Date.now();
|
||||
const serialized = await redis.eval(`
|
||||
local now = tonumber(ARGV[1])
|
||||
@@ -53,31 +56,52 @@ export async function updateVisitorPresence({
|
||||
for _, id in ipairs(expired) do
|
||||
redis.call('ZREM', KEYS[1], id)
|
||||
redis.call('HDEL', KEYS[2], id)
|
||||
redis.call('HDEL', KEYS[3], id)
|
||||
end
|
||||
if ARGV[5] == '1' then
|
||||
if redis.call('ZCARD', KEYS[1]) >= 10000 and not redis.call('ZSCORE', KEYS[1], ARGV[2]) then
|
||||
return redis.error_reply('presence capacity reached')
|
||||
end
|
||||
local previousScope = redis.call('HGET', KEYS[2], ARGV[2])
|
||||
local previousBrowser = redis.call('HGET', KEYS[3], ARGV[2])
|
||||
redis.call('ZADD', KEYS[1], now + tonumber(ARGV[4]), ARGV[2])
|
||||
redis.call('HSET', KEYS[2], ARGV[2], ARGV[3])
|
||||
redis.call('HSET', KEYS[3], ARGV[2], ARGV[6])
|
||||
if previousScope ~= ARGV[3] or previousBrowser ~= ARGV[6] then redis.call('DEL', KEYS[4]) end
|
||||
else
|
||||
redis.call('ZREM', KEYS[1], ARGV[2])
|
||||
redis.call('HDEL', KEYS[2], ARGV[2])
|
||||
redis.call('HDEL', KEYS[3], ARGV[2])
|
||||
redis.call('DEL', KEYS[4])
|
||||
end
|
||||
redis.call('PEXPIRE', KEYS[1], tonumber(ARGV[4]) * 2)
|
||||
redis.call('PEXPIRE', KEYS[2], tonumber(ARGV[4]) * 2)
|
||||
local snapshot = redis.call('GET', KEYS[3])
|
||||
redis.call('PEXPIRE', KEYS[3], tonumber(ARGV[4]) * 2)
|
||||
if #expired > 0 then redis.call('DEL', KEYS[4]) end
|
||||
local snapshot = redis.call('GET', KEYS[4])
|
||||
if snapshot then return snapshot end
|
||||
local ids = redis.call('ZRANGEBYSCORE', KEYS[1], now + 1, '+inf')
|
||||
local counts = {}
|
||||
local browsers = {}
|
||||
local guideBrowsers = {}
|
||||
for _, id in ipairs(ids) do
|
||||
local browser = redis.call('HGET', KEYS[3], id) or id
|
||||
local scope = redis.call('HGET', KEYS[2], id)
|
||||
if scope and scope ~= '' then counts[scope] = (counts[scope] or 0) + 1 end
|
||||
browsers[browser] = true
|
||||
if scope and scope ~= '' then
|
||||
local key = scope .. ':' .. browser
|
||||
if not guideBrowsers[key] then
|
||||
counts[scope] = (counts[scope] or 0) + 1
|
||||
guideBrowsers[key] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
snapshot = cjson.encode({global = #ids, byGuide = counts})
|
||||
redis.call('SET', KEYS[3], snapshot, 'PX', 5000)
|
||||
local global = 0
|
||||
for _ in pairs(browsers) do global = global + 1 end
|
||||
snapshot = cjson.encode({global = global, byGuide = counts})
|
||||
redis.call('SET', KEYS[4], snapshot, 'PX', 5000)
|
||||
return snapshot
|
||||
`, 3, activeKey, scopeKey, `${prefix()}:visitor-counts`, now, tabId, guideId ?? "", PRESENCE_TTL_MS, active ? "1" : "0") as string;
|
||||
`, 4, activeKey, scopeKey, browserKey, `${prefix()}:visitor-counts`, now, tabId, guideId ?? "", PRESENCE_TTL_MS, active ? "1" : "0", browserId) as string;
|
||||
const snapshot = JSON.parse(serialized) as { global: number; byGuide: Record<string, number> };
|
||||
return { ...snapshot, guide: guideId ? snapshot.byGuide[guideId] ?? 0 : null };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user