perf: add global LRU cache to mod_vnc_lastactivity store reads
Add a module-scoped util.cache LRU (lastactivity_cache_size default 5000, lastactivity_cache_ttl default 300s) in front of the activity_store / remote_activity_store fallback. Write-through on all set sites (authentication-success, local last-session unavailable, remote unavailable) and read-through in both IQ handlers (batch and standard jabber:iq:last), so repeated last-activity queries for the same contacts no longer hit the synchronous SQL driver, reducing event-loop blocking. Also removes dead code (avatar-hash helpers, dumpTable, unused imports, commented-out debug logging) and the unused remote avatar-update tracking. Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/5>
This commit is contained in:
@@ -7,24 +7,36 @@
|
||||
--
|
||||
|
||||
local st = require "util.stanza";
|
||||
local is_contact_subscribed = require "core.rostermanager".is_contact_subscribed;
|
||||
local jid_bare = require "util.jid".bare;
|
||||
local jid_split = require "util.jid".split;
|
||||
local base64 = require"util.encodings".base64;
|
||||
local sha1 = require"util.hashes".sha1;
|
||||
|
||||
local my_host = module:get_host();
|
||||
local my_host_type = module:get_host_type();
|
||||
|
||||
local avt_hash_table = {};
|
||||
local remote_act_cache = {};
|
||||
local remote_act_sessions = {};
|
||||
|
||||
module:depends"vcard";
|
||||
local vcard_storage = module:open_store("vcard");
|
||||
local activity_store = module:open_store("activity", "map");
|
||||
local remote_activity_store = module:open_store("remote_activity", "map");
|
||||
local remote_avatar_update_store = module:open_store("remote_avatar", "map");
|
||||
|
||||
-- Options:
|
||||
-- lastactivity_cache_size max entries in the last-activity LRU cache (default 5000)
|
||||
-- lastactivity_cache_ttl max age (seconds) of a cached entry before re-fetch (default 300)
|
||||
local cache_lib = require "util.cache";
|
||||
local last_activity_cache = cache_lib.new(
|
||||
module:get_option_integer("lastactivity_cache_size", 5000, 1));
|
||||
local cache_ttl = module:get_option_number("lastactivity_cache_ttl", 300);
|
||||
|
||||
-- cache stores the persisted "last" timestamp (0 == online) keyed by bare JID,
|
||||
-- write-through on every store write and read-through on every store read.
|
||||
local function cache_set(bare_jid, stamp)
|
||||
last_activity_cache:set(bare_jid, { stamp = stamp, ts = os.time() });
|
||||
end
|
||||
local function cache_get(bare_jid)
|
||||
local entry = last_activity_cache:get(bare_jid);
|
||||
if not entry then return nil end
|
||||
if os.difftime(os.time(), entry.ts) > cache_ttl then return nil end
|
||||
return entry.stamp;
|
||||
end
|
||||
|
||||
local function getTableLng(tbl)
|
||||
local getN = 0
|
||||
@@ -34,70 +46,6 @@ local function getTableLng(tbl)
|
||||
return getN
|
||||
end
|
||||
|
||||
local function dumpTable(t, depth)
|
||||
local result = "";
|
||||
if (not depth) then depth = 0 end;
|
||||
|
||||
local prefix = string.rep(" ", depth);
|
||||
|
||||
if (type(t) ~= "table") then
|
||||
result = result.." "..tostring(t);
|
||||
else
|
||||
for key,value in pairs(t) do
|
||||
result = result..prefix..tostring(key).." => "..dumpTable(value, depth+1).."\n"
|
||||
end
|
||||
end
|
||||
|
||||
return result;
|
||||
end
|
||||
|
||||
|
||||
local function get_vcard(username)
|
||||
local vcard, err = vcard_storage:get(username);
|
||||
if vcard then
|
||||
vcard = st.deserialize(vcard);
|
||||
end
|
||||
if not vcard then
|
||||
vcard = st.stanza("vCard", { xmlns = "vcard-temp" });
|
||||
end
|
||||
return vcard, err;
|
||||
end
|
||||
|
||||
|
||||
local function calculate_avatar_hash(username)
|
||||
local vcard = get_vcard(username)
|
||||
local photo = vcard and vcard:get_child("PHOTO")
|
||||
if photo then
|
||||
local photo_type = photo:get_child_text("TYPE");
|
||||
local photo_b64 = photo:get_child_text("BINVAL");
|
||||
local photo_raw = photo_b64 and base64.decode(photo_b64);
|
||||
if photo_raw and photo_type then
|
||||
return sha1(photo_raw, true);
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function get_avatar_hash(username, host)
|
||||
if (my_host ~= host) then
|
||||
return nil
|
||||
end
|
||||
local session = nil
|
||||
if (prosody.hosts[host].sessions ~= nil) then
|
||||
session = prosody.hosts[host] and prosody.hosts[host].sessions[username]
|
||||
end
|
||||
local avt_hash = nil;
|
||||
if not avt_hash_table[username] then
|
||||
avt_hash = calculate_avatar_hash(username);
|
||||
avt_hash_table[username] = avt_hash;
|
||||
-- -- module:log("info", "DEBUG get_avatar_hash returning result after new calc");
|
||||
else
|
||||
-- -- module:log("info", "DEBUG get_avatar_hash returning result from cache");
|
||||
avt_hash = avt_hash_table[username];
|
||||
end;
|
||||
return avt_hash
|
||||
end
|
||||
|
||||
module:add_feature("jabber:iq:last");
|
||||
module:add_feature("jabber:iq:batch");
|
||||
|
||||
@@ -108,6 +56,7 @@ module:hook("authentication-success", function(event)
|
||||
local session = event.session;
|
||||
if session.username then
|
||||
local pers = activity_store:set(session.username, "last", 0);
|
||||
cache_set(session.username.."@"..my_host, 0);
|
||||
end
|
||||
end, 10);
|
||||
|
||||
@@ -115,8 +64,6 @@ end, 10);
|
||||
module:hook("presence/bare", function(event)
|
||||
local origin, stanza = event.origin, event.stanza;
|
||||
local typ = stanza.attr.type;
|
||||
-- module:log("info", "Cache hook, got raw %s ", dumpTable(stanza));
|
||||
-- module:log("info", "Cache hook, got %s from a %s - %s", stanza:top_tag(), origin.type, typ);
|
||||
|
||||
if origin.type == "s2sin" and ( typ == nil or typ == "unavailable" ) then
|
||||
|
||||
@@ -125,46 +72,24 @@ module:hook("presence/bare", function(event)
|
||||
local contact_user, contact_host, contact_res = jid_split(contact_full);
|
||||
local contact_sessions = remote_act_sessions[contact_bare] or {};
|
||||
|
||||
-- module:log("info", "contact_sessions from %s is %s", contact_bare, dumpTable(remote_act_sessions[contact_bare]));
|
||||
|
||||
if typ == "unavailable" then -- set current time in cache
|
||||
local t = os.time();
|
||||
local s = "";
|
||||
if contact_sessions[contact_full] then
|
||||
contact_sessions[contact_full] = nil;
|
||||
remote_act_sessions[contact_bare] = contact_sessions;
|
||||
local contact_session_count = getTableLng(contact_sessions);
|
||||
-- module:log("info", "storing online state for %s in after removing contact_full, net length %s, %s", contact_full, contact_session_count, dumpTable(contact_sessions));
|
||||
local contact_session_count = getTableLng(contact_sessions);
|
||||
if ( contact_session_count == 0 ) then
|
||||
remote_act_sessions[contact_bare] = nil;
|
||||
remote_act_cache[contact_bare] = t;
|
||||
-- module:log("info", "remote_act_cache for %s is now %s", contact_bare, remote_act_cache[contact_bare]);
|
||||
remote_activity_store:set(contact_bare, "last", t);
|
||||
cache_set(contact_bare, t);
|
||||
end
|
||||
end
|
||||
else
|
||||
-- set 0 (online) in cache
|
||||
-- module:log("info", "storing online state for %s in remote_act_cache %s", contact_bare, dumpTable(remote_act_sessions[contact_bare]));
|
||||
-- remote_act_cache[contact_bare] = 0;
|
||||
local contact_sessions = remote_act_sessions[contact_bare] or {};
|
||||
-- module:log("info", "storing online state for %s in pre local contact_sessions %s", contact_bare, dumpTable(contact_sessions));
|
||||
contact_sessions[contact_full] = 1;
|
||||
-- module:log("info", "storing online state for %s in podt local contact_sessions %s", contact_bare, dumpTable(contact_sessions));
|
||||
remote_act_sessions[contact_bare] = contact_sessions;
|
||||
local contact_session_count = getTableLng(contact_sessions);
|
||||
-- module:log("info", "contact_sessions afterstore from %s is new length %s, %s", contact_bare, contact_session_count, dumpTable(remote_act_sessions[contact_bare]));
|
||||
|
||||
local x_update = stanza:get_child("x", "vcard-temp:x:update") or nil;
|
||||
if (x_update) then
|
||||
local x_photo_update = x_update:get_child_text("photo") or nil;
|
||||
-- module:log("info", "Cache hook1 debug: %s", x_photo_update);
|
||||
if (x_photo_update) then
|
||||
-- module:log("info", "avatarupdate - need to store %s update for %s", x_photo_update, contact_bare);
|
||||
local t = os.time();
|
||||
remote_avatar_update_store:set(contact_bare, "avatar", t);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -172,9 +97,7 @@ end, 10);
|
||||
|
||||
module:hook("pre-presence/bare", function(event)
|
||||
local stanza = event.stanza;
|
||||
-- module:log("info", "presence hook - event.origin.username: %s", event.origin.username);
|
||||
if not(stanza.attr.to) and stanza.attr.type == "unavailable" then
|
||||
-- module:log("info", "unavailable hook - event.origin.username: %s", event.origin.username);
|
||||
local s = os.time();
|
||||
local t = stanza:get_child_text("status");
|
||||
map[event.origin.username] = {s = s, t = t};
|
||||
@@ -187,8 +110,8 @@ module:hook("pre-presence/bare", function(event)
|
||||
end
|
||||
end
|
||||
if lastsession then
|
||||
-- module:log("info", "last session gone - storing to activity_store for %s => %s", event.origin.username, s);
|
||||
local pers = activity_store:set(event.origin.username, "last", s);
|
||||
cache_set(event.origin.username.."@"..my_host, s);
|
||||
end
|
||||
end
|
||||
end, 10);
|
||||
@@ -196,35 +119,24 @@ end, 10);
|
||||
module:hook("iq/bare/jabber:iq:batch:query", function(event)
|
||||
local origin, stanza = event.origin, event.stanza;
|
||||
if stanza.attr.type == "get" then
|
||||
-- -- module:log("info", "got stanza: %s", dumpTable(stanza));
|
||||
local querychild = stanza:get_child("query", "jabber:iq:batch");
|
||||
local reply = st.reply(stanza):tag('query', {xmlns='jabber:iq:last'});
|
||||
-- :childtags("jid", "jabber:iq:batch");
|
||||
-- -- module:log("info", "querychilds: %s", dumpTable(querychild));
|
||||
for ajid in querychild:childtags("jid") do
|
||||
local abjid = ajid:get_text();
|
||||
-- module:log("info", "got jid: %s", abjid);
|
||||
local username = jid_split(abjid);
|
||||
local luser, lhost = jid_split(abjid);
|
||||
|
||||
local seconds, text = "-1", "";
|
||||
local hash = "";
|
||||
|
||||
-- -- module:log("info", "map is now: %s", dumpTable(map));
|
||||
if map[username] then
|
||||
-- module:log("info", "found map[ %s ] => t: %s", username, map[username].t );
|
||||
-- module:log("info", "found map[ %s ] => s: %s", username, map[username].s );
|
||||
local mt = tonumber(map[username].s);
|
||||
seconds = tostring(os.difftime(os.time(), mt));
|
||||
text = map[username].s;
|
||||
end
|
||||
if luser and lhost then
|
||||
-- -- module:log("info", "luser: %s ++ lhost %s", luser, lhost);
|
||||
local sessions_avail = false;
|
||||
if (hosts[lhost]) then
|
||||
-- module:log("info", "lhost exists: %s", lhost);
|
||||
if (hosts[lhost].sessions) then
|
||||
-- module:log("info", "got session object for lhost %s", lhost);
|
||||
sessions_avail = true;
|
||||
end
|
||||
end
|
||||
@@ -235,17 +147,13 @@ module:hook("iq/bare/jabber:iq:batch:query", function(event)
|
||||
if user_sessions then
|
||||
for x,s in pairs(user_sessions.sessions) do
|
||||
if (s.hibernating) then
|
||||
-- module:log("info", "user_sessions s.hibernated %s is %s", s, s.hibernating);
|
||||
if (s.hibernating > latest_hibernated_stamp) then
|
||||
latest_hibernated_stamp = s.hibernating;
|
||||
end
|
||||
else
|
||||
-- module:log("info", "user_sessions %s is active", s);
|
||||
all_hibernated = false;
|
||||
end
|
||||
end
|
||||
-- module:log("info","latest_hibernated_stamp is %s", latest_hibernated_stamp);
|
||||
-- module:log("info","all_hibernated is %s", all_hibernated);
|
||||
if (latest_hibernated_stamp > 0 and all_hibernated) then
|
||||
seconds = os.difftime(os.time(), latest_hibernated_stamp)
|
||||
else
|
||||
@@ -255,15 +163,12 @@ module:hook("iq/bare/jabber:iq:batch:query", function(event)
|
||||
end
|
||||
if not (prosody.hosts[lhost]) then
|
||||
local contact_bare = luser.."@"..lhost;
|
||||
-- module:log("info","getting act for remote %s", contact_bare);
|
||||
if remote_act_sessions[contact_bare] then
|
||||
local contact_sessions = getTableLng(remote_act_sessions[contact_bare]);
|
||||
if ( contact_sessions == 0 ) then
|
||||
-- module:log("info","setting seconds to 0 for remote_act_cache %s -> ", contact_bare, remote_act_cache[contact_bare]);
|
||||
seconds = tostring(os.difftime(os.time(), remote_act_cache[contact_bare]));
|
||||
else
|
||||
seconds = 0;
|
||||
-- module:log("info","setting seconds to 0 for remote %s", contact_bare);
|
||||
end
|
||||
else
|
||||
if remote_act_cache[contact_bare] then
|
||||
@@ -279,32 +184,39 @@ module:hook("iq/bare/jabber:iq:batch:query", function(event)
|
||||
|
||||
if (seconds == "-1") then
|
||||
if (prosody.hosts[lhost]) then
|
||||
local stored_seconds = activity_store:get(luser, "last") or false;
|
||||
if stored_seconds then
|
||||
-- module:log("info", "read persistant lastactivity for %s as %s", luser, dumpTable(stored_seconds));
|
||||
local nseconds = tonumber(stored_seconds);
|
||||
local bare_jid = luser.."@"..lhost;
|
||||
local nseconds = cache_get(bare_jid);
|
||||
if nseconds == nil then
|
||||
local stored_seconds = activity_store:get(luser, "last") or false;
|
||||
if stored_seconds then
|
||||
nseconds = tonumber(stored_seconds);
|
||||
cache_set(bare_jid, nseconds);
|
||||
end
|
||||
end
|
||||
if nseconds then
|
||||
if (nseconds == 0) then
|
||||
-- module:log("info","using restored for %s @ %s ", luser, lhost);
|
||||
map[event.origin.username] = {s = "0", t = text};
|
||||
else
|
||||
seconds = tostring(os.difftime(os.time(), nseconds));
|
||||
-- module:log("info","using restored for %s @ %s => %s", luser, lhost, nseconds);
|
||||
map[event.origin.username] = {s = nseconds, t = text};
|
||||
end
|
||||
end
|
||||
else
|
||||
if (luser ~= nil) then
|
||||
local contact_bare = luser.."@"..lhost;
|
||||
local stored_seconds = remote_activity_store:get(contact_bare, "last") or false;
|
||||
if stored_seconds then
|
||||
-- module:log("info", "read persistant lastactivity for %s as %s", luser, dumpTable(stored_seconds));
|
||||
local nseconds = tonumber(stored_seconds);
|
||||
local nseconds = cache_get(contact_bare);
|
||||
if nseconds == nil then
|
||||
local stored_seconds = remote_activity_store:get(contact_bare, "last") or false;
|
||||
if stored_seconds then
|
||||
nseconds = tonumber(stored_seconds);
|
||||
cache_set(contact_bare, nseconds);
|
||||
end
|
||||
end
|
||||
if nseconds then
|
||||
if (nseconds == 0) then
|
||||
-- module:log("info","using restored for %s @ %s ", luser, lhost);
|
||||
map[event.origin.username] = {s = "0", t = text};
|
||||
else
|
||||
seconds = tostring(os.difftime(os.time(), nseconds));
|
||||
-- module:log("info","using restored for %s @ %s => %s", luser, lhost, nseconds);
|
||||
map[event.origin.username] = {s = nseconds, t = text};
|
||||
end
|
||||
end
|
||||
@@ -312,19 +224,9 @@ module:hook("iq/bare/jabber:iq:batch:query", function(event)
|
||||
end
|
||||
end
|
||||
|
||||
-- hash = get_avatar_hash(luser, lhost);
|
||||
-- reply:add_child():tag("result", {jid=abjid, seconds=seconds, photo=hash}):up();
|
||||
-- module:log("info","determined for %s => %s seconds", abjid, seconds);
|
||||
-- module:log("info","determined for %s => %s seconds", abjid, tostring(seconds));
|
||||
-- module:log("info","type jid: %s", type(abjid));
|
||||
-- module:log("info","type seconds: %s", type(seconds));
|
||||
-- module:log("info"," reply before add: %s", reply);
|
||||
|
||||
-- reply:add_child():tag("result", { jid=abjid; seconds=tostring(seconds); } ):up();
|
||||
local rtag = st.stanza("result", { jid=abjid; seconds=tostring(seconds); });
|
||||
reply:add_child(rtag);
|
||||
end
|
||||
-- module:log("info", "sending reply %s", reply);
|
||||
origin.send(reply);
|
||||
end
|
||||
return true;
|
||||
@@ -335,22 +237,15 @@ module:hook("iq/bare/jabber:iq:last:query", function(event)
|
||||
if stanza.attr.type == "get" then
|
||||
local username = jid_split(stanza.attr.to) or origin.username;
|
||||
local luser, lhost = jid_split(stanza.attr.to);
|
||||
-- if not stanza.attr.to or is_contact_subscribed(username, module.host, jid_bare(stanza.attr.from)) then
|
||||
if stanza.attr.to then
|
||||
local seconds, text = "-1", "";
|
||||
local hash = "";
|
||||
if map[username] then
|
||||
mseconds = map[username].s or 0;
|
||||
-- module:log("info", "mseconds: %s", mseconds);
|
||||
if (mseconds == 0) then
|
||||
seconds=0;
|
||||
map[username].s=os.time();
|
||||
else
|
||||
-- module:log("info", "difftime from map for user %s is %s", username, dumpTable(map[username]));
|
||||
-- module:log("info", "difftime will be called for %s == type %s", map[username].s, type(map[username].s));
|
||||
seconds = tostring(os.difftime(os.time(), map[username].s));
|
||||
-- module:log("info","using2 for %s @ %s => %s --- stored was %s", luser, lhost, seconds, map[username].s);
|
||||
-- text = map[username].s;
|
||||
end
|
||||
end
|
||||
if luser and lhost then
|
||||
@@ -360,17 +255,13 @@ module:hook("iq/bare/jabber:iq:last:query", function(event)
|
||||
local all_hibernated = true;
|
||||
for x,s in pairs(user_sessions.sessions) do
|
||||
if (s.hibernating) then
|
||||
-- module:log("info", "user_sessions s.hibernated %s is %s", s, s.hibernating);
|
||||
if (s.hibernating > latest_hibernated_stamp) then
|
||||
latest_hibernated_stamp = s.hibernating;
|
||||
end
|
||||
else
|
||||
-- module:log("info", "user_sessions %s is active", s);
|
||||
all_hibernated = false;
|
||||
end
|
||||
end
|
||||
-- module:log("info","latest_hibernated_stamp is %s", latest_hibernated_stamp);
|
||||
-- module:log("info","all_hibernated is %s", all_hibernated);
|
||||
if (latest_hibernated_stamp > 0 and all_hibernated) then
|
||||
seconds = os.difftime(os.time(), latest_hibernated_stamp)
|
||||
else
|
||||
@@ -379,26 +270,25 @@ module:hook("iq/bare/jabber:iq:last:query", function(event)
|
||||
end
|
||||
end
|
||||
if (seconds == "-1") then
|
||||
local stored_seconds = activity_store:get(luser, "last") or -1;
|
||||
local bare_jid = luser.."@"..lhost;
|
||||
local stored_seconds = cache_get(bare_jid);
|
||||
if stored_seconds == nil then
|
||||
stored_seconds = activity_store:get(luser, "last") or -1;
|
||||
if stored_seconds ~= -1 then
|
||||
cache_set(bare_jid, tonumber(stored_seconds));
|
||||
end
|
||||
end
|
||||
if (stored_seconds ~= 0) then
|
||||
-- -- module:log("info", "read persistant lastactivity for %s as %s", luser, dumpTable(stored_seconds));
|
||||
if (stored_seconds == -1) then
|
||||
seconds = -1
|
||||
else
|
||||
seconds = tostring(os.difftime(os.time(), stored_seconds));
|
||||
end;
|
||||
-- module:log("info","using4 for %s @ %s => %s", luser, lhost, seconds);
|
||||
if event.origin.username ~= nil then
|
||||
map[event.origin.username] = {s = seconds, t = text};
|
||||
end
|
||||
else
|
||||
-- local now = os.time();
|
||||
-- module:log("info", "no stored data for %s @ %s", luser, lhost);
|
||||
-- local upers = activity_store:set(luser, "last", now);
|
||||
-- seconds = 0;
|
||||
end
|
||||
end
|
||||
-- module:log("info","sending reply with: seconds=%s text=%s", seconds, text);
|
||||
origin.send(st.reply(stanza):tag('query', {xmlns='jabber:iq:last', seconds=tostring(seconds)}):text(tostring(text)));
|
||||
else
|
||||
origin.send(st.error_reply(stanza, 'auth', 'forbidden'));
|
||||
|
||||
Reference in New Issue
Block a user