changeset 2147:ed2bb50d4f91

mod_presence_cache: Switch to using util.cache for limiting size of cache
author Kim Alvefur <zash@zash.se>
date Fri, 01 Apr 2016 15:18:56 +0200
parents 39d958f4a0c3
children c472a454be61
files mod_presence_cache/README.markdown mod_presence_cache/mod_presence_cache.lua
diffstat 2 files changed, 52 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/mod_presence_cache/README.markdown	Fri Apr 01 15:15:56 2016 +0200
+++ b/mod_presence_cache/README.markdown	Fri Apr 01 15:18:56 2016 +0200
@@ -1,17 +1,13 @@
 ---
-summary: Cache presence incoming presence
+summary: Cache presence from remote users
 ...
 
 Introduction
 ============
 
-This module stores presence from users contact even when they are
-offline, so that the client can see who is online faster when they sign
-in, and won't have to wait for remote servers to reply.
-
-Note that in its current form, the number of presence stanzas sent to a
-client is doubled, as the client would get both the cached stanzas and
-replies to presence probes. Also see [mod\_throttle\_presence].
+This module stores a timestamp of the latest presence received from
+users contacts so that the client can see who is online faster when they
+sign in, and won't have to wait for remote servers to reply.
 
 Configuration
 =============
@@ -26,16 +22,11 @@
 Advanced configuration
 ======================
 
-
-
+The size of the cache is tuneable:
 
-TODO
-====
+    presence_cache_size = 99
 
--   Deduplication, i.e don's send stanzas that are identical to the last
-    seen.
--   Cache invalidation or expiry, eg if a remote server goes down or is
-    gone a long time.
--   Sending probes at some interval to keep the cache reasonably fresh.
+Compatibility
+=============
 
-
+Requires 0.10 or later
--- a/mod_presence_cache/mod_presence_cache.lua	Fri Apr 01 15:15:56 2016 +0200
+++ b/mod_presence_cache/mod_presence_cache.lua	Fri Apr 01 15:18:56 2016 +0200
@@ -3,46 +3,53 @@
 local jid_bare = require"util.jid".bare;
 local st = require"util.stanza";
 local datetime = require"util.datetime";
+local cache = require "util.cache";
 
-local presence_cache = {}; -- Reload to empty
+local cache_size = module:get_option_number("presence_cache_size", 100);
+
+local bare_cache = {}; -- [username NUL bare_jid] = { [full_jid] = timestamp, ... }
+
+local function on_evict(cache_key)
+	local bare_cache_key = cache_key:match("^%Z+%z[^/]+");
+	local full_jid = cache_key:match("%z(.*)$");
+	local jids = bare_cache[bare_cache_key];
+
+	if jids then
+		jids[full_jid] = nil;
+	end
+	if next(jids) == nil then
+		bare_cache[bare_cache_key] = nil;
+	end
+end
+
+local presence_cache = cache.new(cache_size, on_evict);
 
 local function cache_hook(event)
 	local origin, stanza = event.origin, event.stanza;
 	local typ = stanza.attr.type;
 	module:log("debug", "Cache hook, got %s from a %s", stanza:top_tag(), origin.type);
 	if origin.type == "s2sin" and ( typ == nil or typ == "unavailable" ) then
-		local from_jid = stanza.attr.from;
-		local from_bare = jid_bare(from_jid);
-		local username = jid_split(stanza.attr.to);
 
-		if not is_contact_subscribed(username, module.host, from_bare) then
-			module:log("debug", "Not in their roster", origin.username);
+		local contact_full = stanza.attr.from;
+		local contact_bare = jid_bare(contact_full);
+		local username, host = jid_split(stanza.attr.to);
+
+		if not is_contact_subscribed(username, host, contact_bare) then
+			module:log("debug", "Presence from jid not in roster");
 			return;
 		end
 
-		local user_presence_cache = presence_cache[username];
-		if not user_presence_cache then
-			user_presence_cache = {};
-			presence_cache[username] = user_presence_cache;
-		end
-
-		local contact_presence_cache = user_presence_cache[from_bare];
-		if not contact_presence_cache then
-			contact_presence_cache = {};
-			user_presence_cache[from_bare] = contact_presence_cache;
+		local cache_key = username .. "\0" .. contact_full;
+		local bare_cache_key = username .. "\0" .. contact_bare;
+		local stamp = datetime.datetime();
+		local jids = bare_cache[bare_cache_key];
+		if jids then
+			jids[contact_full] = stamp;
+		else
+			jids = { [contact_full] = stamp };
+			bare_cache[bare_cache_key] = jids;
 		end
-
-		if typ == "unavailable" then
-			contact_presence_cache[from_jid] = nil;
-			if next(contact_presence_cache) == nil or from_jid == from_bare then
-				user_presence_cache[from_bare] = nil;
-				if next(user_presence_cache) == nil then
-					presence_cache[username] = nil;
-				end
-			end
-		else -- only cache binary state
-			contact_presence_cache[from_jid] = datetime.datetime();
-		end
+		presence_cache:set(cache_key, true);
 	end
 end
 
@@ -52,46 +59,19 @@
 local function answer_probe_from_cache(event)
 	local origin, stanza = event.origin, event.stanza;
 	if stanza.attr.type ~= "probe" then return; end
+
+	local username = origin.username;
 	local contact_bare = stanza.attr.to;
 
-	local user_presence_cache = presence_cache[origin.username];
-	if not user_presence_cache then return; end
-
-	local contact_presence_cache = user_presence_cache[contact_bare];
-	if not contact_presence_cache then return; end
+	local bare_cache_key = username .. "\0" .. contact_bare;
 
-	local user_jid = stanza.attr.from;
-	for jid, presence in pairs(contact_presence_cache) do
-		module:log("debug", "Sending cached presence from %s", jid);
-		if presence == true then
-			presence = st.presence({ from = user_jid, from = jid });
-		elseif type(presence) == "string" then -- a timestamp
-			presence = st.presence({ from = user_jid, from = jid })
-				:tag("delay", { xmlns = "urn:xmpp:delay", from = module.host, stamp = presence }):up();
-		end
+	local cached = bare_cache[bare_cache_key];
+	if not cached then return end
+	for jid, stamp in pairs(cached) do
+		local presence = st.presence({ to = origin.full_jid, from = jid })
+			:tag("delay", { xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp }):up();
 		origin.send(presence);
 	end
 end
 
 module:hook("pre-presence/bare", answer_probe_from_cache, 10);
-
-module:add_timer(3600, function (now)
-	local older = datetime.datetime(now - 7200);
-	for username, user_presence_cache in pairs(presence_cache) do
-		for contact, contact_presence_cache in pairs(user_presence_cache) do
-			for jid, presence in pairs(contact_presence_cache) do
-				if presence == true or (type(presence) == "string" and presence < older) then
-					contact_presence_cache[jid] = nil;
-				end
-			end
-			if next(contact_presence_cache) == nil then
-				user_presence_cache[contact] = nil;
-			end
-		end
-		if next(user_presence_cache) == nil then
-			presence_cache[username] = nil;
-		end
-	end
-	return 3600;
-end);
-