comparison mod_pubsub_serverinfo/mod_pubsub_serverinfo.lua @ 5810: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
parents a8cae8322b7c
children cf5f77491323
comparison
equal deleted inserted replaced
5809:a8cae8322b7c 5810: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")