Software /
code /
prosody-modules
Comparison
mod_pubsub_serverinfo/mod_pubsub_serverinfo.lua @ 5833:76b57bcfe1b2
mod_pubsub_serverinfo: Warm-up opt-in cache
By warming up the cache that contains the opt-in data, the first publication has a better chance of including domain names for remote domains that opt-in.
Without this change, those domains are named only after the _second_ publication, which can take a while. New users are likely thrown off by that.
author | Guus der Kinderen <guus.der.kinderen@gmail.com> |
---|---|
date | Thu, 04 Jan 2024 16:19:55 +0100 |
parent | 5832:a8cae8322b7c |
child | 5835:cf5f77491323 |
comparison
equal
deleted
inserted
replaced
5832:a8cae8322b7c | 5833:76b57bcfe1b2 |
---|---|
27 module:add_extension(dataform { | 27 module:add_extension(dataform { |
28 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/network/serverinfo" }, | 28 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/network/serverinfo" }, |
29 { name = "serverinfo-pubsub-node", type = "text-single" }, | 29 { name = "serverinfo-pubsub-node", type = "text-single" }, |
30 }:form({ ["serverinfo-pubsub-node"] = ("xmpp:%s?;node=%s"):format(service, node) }, "result")); | 30 }:form({ ["serverinfo-pubsub-node"] = ("xmpp:%s?;node=%s"):format(service, node) }, "result")); |
31 | 31 |
32 cache_warm_up() | |
32 module:add_timer(10, publish_serverinfo); | 33 module:add_timer(10, publish_serverinfo); |
33 end | 34 end |
34 | 35 |
35 function module.unload() | 36 function module.unload() |
36 -- This removes all subscribers, which may or may not be desirable, depending on the reason for the unload. | 37 -- This removes all subscribers, which may or may not be desirable, depending on the reason for the unload. |
37 delete_node(); -- Should this block, to delay unload() until the node is deleted? | 38 delete_node(); -- Should this block, to delay unload() until the node is deleted? |
38 end | 39 end |
39 | 40 |
40 -- Returns a promise of a boolean | 41 -- Returns a promise of a boolean |
41 function discover_node() | 42 function discover_node() |
42 local request = st.iq({ type = "get", to = service, from = actor, id = new_id() }) | 43 local request = st.iq({ type = "get", to = service, from = actor, id = new_id() }) |
43 :tag("query", { xmlns = "http://jabber.org/protocol/disco#items" }) | 44 :tag("query", { xmlns = "http://jabber.org/protocol/disco#items" }) |
44 | 45 |
45 module:log("debug", "Sending request to discover existence of pub/sub node '%s' at %s", node, service) | 46 module:log("debug", "Sending request to discover existence of pub/sub node '%s' at %s", node, service) |
46 return module:send_iq(request):next( | 47 return module:send_iq(request):next( |
47 function(response) | 48 function(response) |
48 if response.stanza == nil or response.stanza.attr.type ~= "result" then | 49 if response.stanza == nil or response.stanza.attr.type ~= "result" then |
113 end | 114 end |
114 end | 115 end |
115 ) | 116 ) |
116 end | 117 end |
117 | 118 |
118 function publish_serverinfo() | 119 function get_remote_domain_names() |
119 -- Iterate over s2s sessions, adding them to a multimap, where the key is the local domain name, | 120 -- Iterate over s2s sessions, adding them to a multimap, where the key is the local domain name, |
120 -- mapped to a collection of remote domain names. De-duplicate all remote domain names by using | 121 -- mapped to a collection of remote domain names. De-duplicate all remote domain names by using |
121 -- them as an index in a table. | 122 -- them as an index in a table. |
122 local domains_by_host = {} | 123 local domains_by_host = {} |
123 for session, _ in pairs(prosody.incoming_s2s) do | 124 for session, _ in pairs(prosody.incoming_s2s) do |
158 domains_by_host[host] = sessions | 159 domains_by_host[host] = sessions |
159 end | 160 end |
160 end | 161 end |
161 end | 162 end |
162 | 163 |
164 return domains_by_host | |
165 end | |
166 | |
167 function publish_serverinfo() | |
168 local domains_by_host = get_remote_domain_names() | |
169 | |
163 -- Build the publication stanza. | 170 -- Build the publication stanza. |
164 local request = st.iq({ type = "set", to = service, from = actor, id = new_id() }) | 171 local request = st.iq({ type = "set", to = service, from = actor, id = new_id() }) |
165 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) | 172 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) |
166 :tag("publish", { node = node, xmlns = "http://jabber.org/protocol/pubsub" }) | 173 :tag("publish", { node = node, xmlns = "http://jabber.org/protocol/pubsub" }) |
167 :tag("item", { id = "current", xmlns = "http://jabber.org/protocol/pubsub" }) | 174 :tag("item", { id = "current", xmlns = "http://jabber.org/protocol/pubsub" }) |
168 :tag("serverinfo", { xmlns = "urn:xmpp:serverinfo:0" }) | 175 :tag("serverinfo", { xmlns = "urn:xmpp:serverinfo:0" }) |
169 | 176 |
170 request:tag("domain", { name = local_domain }) | 177 request:tag("domain", { name = local_domain }) |
171 :tag("federation") | 178 :tag("federation") |
172 | 179 |
173 local remotes = domains_by_host[host] | 180 local remotes = domains_by_host[local_domain] |
174 | 181 |
175 if remotes ~= nil then | 182 if remotes ~= nil then |
176 for remote, _ in pairs(remotes) do | 183 for remote, _ in pairs(remotes) do |
177 -- include a domain name for remote domains, but only if they advertise support. | 184 -- include a domain name for remote domains, but only if they advertise support. |
178 if does_opt_in(remote) then | 185 if does_opt_in(remote) then |
203 return publication_interval; | 210 return publication_interval; |
204 end | 211 end |
205 | 212 |
206 local opt_in_cache = {} | 213 local opt_in_cache = {} |
207 | 214 |
215 function cache_warm_up() | |
216 module:log("debug", "Warming up opt-in cache") | |
217 local domains_by_host = get_remote_domain_names() | |
218 local remotes = domains_by_host[local_domain] | |
219 if remotes ~= nil then | |
220 for remote, _ in pairs(remotes) do | |
221 does_opt_in(remote) | |
222 end | |
223 end | |
224 end | |
225 | |
208 function does_opt_in(remoteDomain) | 226 function does_opt_in(remoteDomain) |
209 | 227 |
210 -- try to read answer from cache. | 228 -- try to read answer from cache. |
211 local cached_value = opt_in_cache[remoteDomain] | 229 local cached_value = opt_in_cache[remoteDomain] |
212 if cached_value ~= nil and os.difftime(cached_value.expires, os.time()) > 0 then | 230 if cached_value ~= nil and os.difftime(cached_value.expires, os.time()) > 0 then |
216 | 234 |
217 -- TODO worry about not having multiple requests in flight to the same domain.cached_value | 235 -- TODO worry about not having multiple requests in flight to the same domain.cached_value |
218 | 236 |
219 -- Cache could not provide an answer. Perform service discovery. | 237 -- Cache could not provide an answer. Perform service discovery. |
220 module:log("debug", "No cached opt-in status for '%s': performing disco/info to determine opt-in.", remoteDomain) | 238 module:log("debug", "No cached opt-in status for '%s': performing disco/info to determine opt-in.", remoteDomain) |
221 local discoRequest = st.iq({ type = "get", to = remoteDomain, from = actor, id = new_id() }) | 239 local discoRequest = st.iq({ type = "get", to = remoteDomain, from = actor, id = new_id() }) |
222 :tag("query", { xmlns = "http://jabber.org/protocol/disco#info" }) | 240 :tag("query", { xmlns = "http://jabber.org/protocol/disco#info" }) |
223 | 241 |
224 module:send_iq(discoRequest):next( | 242 module:send_iq(discoRequest):next( |
225 function(response) | 243 function(response) |
226 if response.stanza ~= nil and response.stanza.attr.type == "result" then | 244 if response.stanza ~= nil and response.stanza.attr.type == "result" then |
227 local query = response.stanza:get_child("query", "http://jabber.org/protocol/disco#info") | 245 local query = response.stanza:get_child("query", "http://jabber.org/protocol/disco#info") |