memory (disco): Cache failing servers:

When a `remote-server-not-found` stanza error is received, cache the whole server, so all
queries to a entity from this server will be blocked by the cache, avoiding unnecessary
network requests.

During cache check, full entity JID is checked first, then its server.
This commit is contained in:
Goffi 2026-05-03 11:00:30 +02:00
commit 378f05ca35

View file

@ -48,6 +48,8 @@ TIMEOUT = 15
DISCO_CACHE_TTL = 900
# Delay before retrying a failed disco query (in seconds)
DISCO_RETRY_DELAY = 300
# Delay before retrying a server that failed (in seconds)
DISCO_SERVER_RETRY_DELAY = 3600
class HashGenerationError(Exception):
@ -225,6 +227,7 @@ class Discovery:
"""
if jid_ is None:
jid_ = jid.JID(client.jid.host)
try:
if not use_cache:
# we ignore cache, so we pretend we haven't found it
@ -262,6 +265,24 @@ class Discovery:
log.debug(f"Disco cache expired for {jid_.full()}, re-querying")
del self._jid_disco_cache[jid_]
# Entity-level cache miss or expired, check server-level cache
if use_cache and (server_jid := jid.JID(jid_.host)) in self._jid_disco_cache:
data, timestamp = self._jid_disco_cache[server_jid]
if data is None:
elapsed = time.time() - timestamp
if elapsed < DISCO_SERVER_RETRY_DELAY:
msg = (
f"Server {server_jid} is unreachable, retry in "
f"{int(DISCO_SERVER_RETRY_DELAY - elapsed)} seconds"
)
log.debug(msg)
return defer.fail(exceptions.ExternalRequestError(msg))
else:
log.debug(
f"Retry delay expired for unreachable server {server_jid}"
)
del self._jid_disco_cache[server_jid]
# No cache is used, we will do a query to return disco infos.
# The query result will be cached in JID cache.
query_key = (jid_, node)
@ -288,6 +309,17 @@ class Discovery:
if fail.check(defer.CancelledError):
reason = "request time-out"
fail = failure.Failure(exceptions.TimeOutError(str(fail.value)))
elif fail.check(StanzaError):
stanza_error = fail.value
if stanza_error.condition == "remote-server-not-found":
# Mark the server as unreachable (store as domain JID)
server_jid = jid.JID(jid_.host)
self._jid_disco_cache[server_jid] = (None, time.time())
log.warning(
f"Server {server_jid} is unreachable, will retry in "
f"{DISCO_SERVER_RETRY_DELAY} seconds"
)
reason = str(fail.value)
else:
try:
reason = str(fail.value)