comparison mod_presence_cache/mod_presence_cache.lua @ 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 bb4a2e4b7ba7
comparison
equal deleted inserted replaced
2146:39d958f4a0c3 2147:ed2bb50d4f91
1 local is_contact_subscribed = require"core.rostermanager".is_contact_subscribed; 1 local is_contact_subscribed = require"core.rostermanager".is_contact_subscribed;
2 local jid_split = require"util.jid".split; 2 local jid_split = require"util.jid".split;
3 local jid_bare = require"util.jid".bare; 3 local jid_bare = require"util.jid".bare;
4 local st = require"util.stanza"; 4 local st = require"util.stanza";
5 local datetime = require"util.datetime"; 5 local datetime = require"util.datetime";
6 local cache = require "util.cache";
6 7
7 local presence_cache = {}; -- Reload to empty 8 local cache_size = module:get_option_number("presence_cache_size", 100);
9
10 local bare_cache = {}; -- [username NUL bare_jid] = { [full_jid] = timestamp, ... }
11
12 local function on_evict(cache_key)
13 local bare_cache_key = cache_key:match("^%Z+%z[^/]+");
14 local full_jid = cache_key:match("%z(.*)$");
15 local jids = bare_cache[bare_cache_key];
16
17 if jids then
18 jids[full_jid] = nil;
19 end
20 if next(jids) == nil then
21 bare_cache[bare_cache_key] = nil;
22 end
23 end
24
25 local presence_cache = cache.new(cache_size, on_evict);
8 26
9 local function cache_hook(event) 27 local function cache_hook(event)
10 local origin, stanza = event.origin, event.stanza; 28 local origin, stanza = event.origin, event.stanza;
11 local typ = stanza.attr.type; 29 local typ = stanza.attr.type;
12 module:log("debug", "Cache hook, got %s from a %s", stanza:top_tag(), origin.type); 30 module:log("debug", "Cache hook, got %s from a %s", stanza:top_tag(), origin.type);
13 if origin.type == "s2sin" and ( typ == nil or typ == "unavailable" ) then 31 if origin.type == "s2sin" and ( typ == nil or typ == "unavailable" ) then
14 local from_jid = stanza.attr.from;
15 local from_bare = jid_bare(from_jid);
16 local username = jid_split(stanza.attr.to);
17 32
18 if not is_contact_subscribed(username, module.host, from_bare) then 33 local contact_full = stanza.attr.from;
19 module:log("debug", "Not in their roster", origin.username); 34 local contact_bare = jid_bare(contact_full);
35 local username, host = jid_split(stanza.attr.to);
36
37 if not is_contact_subscribed(username, host, contact_bare) then
38 module:log("debug", "Presence from jid not in roster");
20 return; 39 return;
21 end 40 end
22 41
23 local user_presence_cache = presence_cache[username]; 42 local cache_key = username .. "\0" .. contact_full;
24 if not user_presence_cache then 43 local bare_cache_key = username .. "\0" .. contact_bare;
25 user_presence_cache = {}; 44 local stamp = datetime.datetime();
26 presence_cache[username] = user_presence_cache; 45 local jids = bare_cache[bare_cache_key];
46 if jids then
47 jids[contact_full] = stamp;
48 else
49 jids = { [contact_full] = stamp };
50 bare_cache[bare_cache_key] = jids;
27 end 51 end
28 52 presence_cache:set(cache_key, true);
29 local contact_presence_cache = user_presence_cache[from_bare];
30 if not contact_presence_cache then
31 contact_presence_cache = {};
32 user_presence_cache[from_bare] = contact_presence_cache;
33 end
34
35 if typ == "unavailable" then
36 contact_presence_cache[from_jid] = nil;
37 if next(contact_presence_cache) == nil or from_jid == from_bare then
38 user_presence_cache[from_bare] = nil;
39 if next(user_presence_cache) == nil then
40 presence_cache[username] = nil;
41 end
42 end
43 else -- only cache binary state
44 contact_presence_cache[from_jid] = datetime.datetime();
45 end
46 end 53 end
47 end 54 end
48 55
49 module:hook("presence/bare", cache_hook, 10); 56 module:hook("presence/bare", cache_hook, 10);
50 -- module:hook("presence/full", cache_hook, 10); 57 -- module:hook("presence/full", cache_hook, 10);
51 58
52 local function answer_probe_from_cache(event) 59 local function answer_probe_from_cache(event)
53 local origin, stanza = event.origin, event.stanza; 60 local origin, stanza = event.origin, event.stanza;
54 if stanza.attr.type ~= "probe" then return; end 61 if stanza.attr.type ~= "probe" then return; end
62
63 local username = origin.username;
55 local contact_bare = stanza.attr.to; 64 local contact_bare = stanza.attr.to;
56 65
57 local user_presence_cache = presence_cache[origin.username]; 66 local bare_cache_key = username .. "\0" .. contact_bare;
58 if not user_presence_cache then return; end
59 67
60 local contact_presence_cache = user_presence_cache[contact_bare]; 68 local cached = bare_cache[bare_cache_key];
61 if not contact_presence_cache then return; end 69 if not cached then return end
62 70 for jid, stamp in pairs(cached) do
63 local user_jid = stanza.attr.from; 71 local presence = st.presence({ to = origin.full_jid, from = jid })
64 for jid, presence in pairs(contact_presence_cache) do 72 :tag("delay", { xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp }):up();
65 module:log("debug", "Sending cached presence from %s", jid);
66 if presence == true then
67 presence = st.presence({ from = user_jid, from = jid });
68 elseif type(presence) == "string" then -- a timestamp
69 presence = st.presence({ from = user_jid, from = jid })
70 :tag("delay", { xmlns = "urn:xmpp:delay", from = module.host, stamp = presence }):up();
71 end
72 origin.send(presence); 73 origin.send(presence);
73 end 74 end
74 end 75 end
75 76
76 module:hook("pre-presence/bare", answer_probe_from_cache, 10); 77 module:hook("pre-presence/bare", answer_probe_from_cache, 10);
77
78 module:add_timer(3600, function (now)
79 local older = datetime.datetime(now - 7200);
80 for username, user_presence_cache in pairs(presence_cache) do
81 for contact, contact_presence_cache in pairs(user_presence_cache) do
82 for jid, presence in pairs(contact_presence_cache) do
83 if presence == true or (type(presence) == "string" and presence < older) then
84 contact_presence_cache[jid] = nil;
85 end
86 end
87 if next(contact_presence_cache) == nil then
88 user_presence_cache[contact] = nil;
89 end
90 end
91 if next(user_presence_cache) == nil then
92 presence_cache[username] = nil;
93 end
94 end
95 return 3600;
96 end);
97