Comparison

plugins/mod_blocklist.lua @ 13345:a74251a790ed

mod_blocklist: Remove weak cache (and increase default LRU cache size) Weak tables are said to have suboptimal performance, so we might as well get replace it with an increased default LRU cache size. Sorry about the 'and'
author Kim Alvefur <zash@zash.se>
date Sun, 26 Nov 2023 17:41:38 +0100
parent 13213:50324f66ca2a
child 13481:1c87c0a7ece6
comparison
equal deleted inserted replaced
13344:958c759d3897 13345:a74251a790ed
22 22
23 local storage = module:open_store(); 23 local storage = module:open_store();
24 local sessions = prosody.hosts[module.host].sessions; 24 local sessions = prosody.hosts[module.host].sessions;
25 local full_sessions = prosody.full_sessions; 25 local full_sessions = prosody.full_sessions;
26 26
27 -- First level cache of blocklists by username. 27 -- Cache of blocklists, keeps a fixed number of items.
28 -- Weak table so may randomly expire at any time.
29 local cache = setmetatable({}, { __mode = "v" });
30
31 -- Second level of caching, keeps a fixed number of items, also anchors
32 -- items in the above cache.
33 -- 28 --
34 -- The size of this affects how often we will need to load a blocklist from 29 -- The size of this affects how often we will need to load a blocklist from
35 -- disk, which we want to avoid during routing. On the other hand, we don't 30 -- disk, which we want to avoid during routing. On the other hand, we don't
36 -- want to use too much memory either, so this can be tuned by advanced 31 -- want to use too much memory either, so this can be tuned by advanced
37 -- users. TODO use science to figure out a better default, 64 is just a guess. 32 -- users. TODO use science to figure out a better default, 64 is just a guess.
38 local cache_size = module:get_option_integer("blocklist_cache_size", 64, 1); 33 local cache_size = module:get_option_integer("blocklist_cache_size", 256, 1);
39 local cache2 = require"prosody.util.cache".new(cache_size); 34 local blocklist_cache = require"prosody.util.cache".new(cache_size);
40 35
41 local null_blocklist = {}; 36 local null_blocklist = {};
42 37
43 module:add_feature("urn:xmpp:blocking"); 38 module:add_feature("urn:xmpp:blocking");
44 39
46 local ok, err = storage:set(username, blocklist); 41 local ok, err = storage:set(username, blocklist);
47 if not ok then 42 if not ok then
48 return ok, err; 43 return ok, err;
49 end 44 end
50 -- Successful save, update the cache 45 -- Successful save, update the cache
51 cache2:set(username, blocklist); 46 blocklist_cache:set(username, blocklist);
52 cache[username] = blocklist;
53 return true; 47 return true;
54 end 48 end
55 49
56 -- Migrates from the old mod_privacy storage 50 -- Migrates from the old mod_privacy storage
57 -- TODO mod_privacy was removed in 0.10.0, this should be phased out 51 -- TODO mod_privacy was removed in 0.10.0, this should be phased out
84 return nil; 78 return nil;
85 end 79 end
86 end 80 end
87 81
88 local function get_blocklist(username) 82 local function get_blocklist(username)
89 local blocklist = cache2:get(username); 83 local blocklist = blocklist_cache:get(username);
90 if not blocklist then 84 if not blocklist then
91 if not user_exists(username, module.host) then 85 if not user_exists(username, module.host) then
92 return null_blocklist; 86 return null_blocklist;
93 end 87 end
94 blocklist = storage:get(username); 88 blocklist = storage:get(username);
96 blocklist = migrate_privacy_list(username); 90 blocklist = migrate_privacy_list(username);
97 end 91 end
98 if not blocklist then 92 if not blocklist then
99 blocklist = { [false] = { created = os.time(); }; }; 93 blocklist = { [false] = { created = os.time(); }; };
100 end 94 end
101 cache2:set(username, blocklist); 95 blocklist_cache:set(username, blocklist);
102 end 96 end
103 cache[username] = blocklist;
104 return blocklist; 97 return blocklist;
105 end 98 end
106 99
107 module:hook("iq-get/self/urn:xmpp:blocking:blocklist", function (event) 100 module:hook("iq-get/self/urn:xmpp:blocking:blocklist", function (event)
108 local origin, stanza = event.origin, event.stanza; 101 local origin, stanza = event.origin, event.stanza;
109 local username = origin.username; 102 local username = origin.username;
110 local reply = st.reply(stanza):tag("blocklist", { xmlns = "urn:xmpp:blocking" }); 103 local reply = st.reply(stanza):tag("blocklist", { xmlns = "urn:xmpp:blocking" });
111 local blocklist = cache[username] or get_blocklist(username); 104 local blocklist = get_blocklist(username);
112 for jid in pairs(blocklist) do 105 for jid in pairs(blocklist) do
113 if jid then 106 if jid then
114 reply:tag("item", { jid = jid }):up(); 107 reply:tag("item", { jid = jid }):up();
115 end 108 end
116 end 109 end
165 -- <block/> element does not contain at least one <item/> child element 158 -- <block/> element does not contain at least one <item/> child element
166 origin.send(st_error_reply(stanza, "modify", "bad-request")); 159 origin.send(st_error_reply(stanza, "modify", "bad-request"));
167 return true; 160 return true;
168 end 161 end
169 162
170 local blocklist = cache[username] or get_blocklist(username); 163 local blocklist = get_blocklist(username);
171 164
172 local new_blocklist = { 165 local new_blocklist = {
173 -- We set the [false] key to something as a signal not to migrate privacy lists 166 -- We set the [false] key to something as a signal not to migrate privacy lists
174 [false] = blocklist[false] or { created = now; }; 167 [false] = blocklist[false] or { created = now; };
175 }; 168 };
239 module:hook("iq-set/self/urn:xmpp:blocking:unblock", edit_blocklist, -1); 232 module:hook("iq-set/self/urn:xmpp:blocking:unblock", edit_blocklist, -1);
240 233
241 -- Cache invalidation, solved! 234 -- Cache invalidation, solved!
242 module:hook_global("user-deleted", function (event) 235 module:hook_global("user-deleted", function (event)
243 if event.host == module.host then 236 if event.host == module.host then
244 cache2:set(event.username, nil); 237 blocklist_cache:set(event.username, nil);
245 cache[event.username] = nil;
246 end 238 end
247 end); 239 end);
248 240
249 -- Buggy clients 241 -- Buggy clients
250 module:hook("iq-error/self/blocklist-push", function (event) 242 module:hook("iq-error/self/blocklist-push", function (event)
255 module.name, condition, text and ": " or "", text or ""); 247 module.name, condition, text and ": " or "", text or "");
256 return true; 248 return true;
257 end); 249 end);
258 250
259 local function is_blocked(user, jid) 251 local function is_blocked(user, jid)
260 local blocklist = cache[user] or get_blocklist(user); 252 local blocklist = get_blocklist(user);
261 if blocklist[jid] then return true; end 253 if blocklist[jid] then return true; end
262 local node, host = jid_split(jid); 254 local node, host = jid_split(jid);
263 return blocklist[host] or node and blocklist[node..'@'..host]; 255 return blocklist[host] or node and blocklist[node..'@'..host];
264 end 256 end
265 257