From 84e2f0af9f5e584ac64feee3fc7d6fb171673d94 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Thu, 11 Apr 2019 11:14:27 +0200
Subject: [PATCH] See #689: added a task to refresh nodeinfo data on known
 domain periodically

---
 api/config/settings/common.py         |  7 +++++++
 api/funkwhale_api/federation/tasks.py | 16 ++++++++++++++++
 api/tests/federation/test_tasks.py    | 25 +++++++++++++++++++++++++
 3 files changed, 48 insertions(+)

diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index a85a46f97c..be2fa136cf 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -486,8 +486,15 @@ CELERY_BEAT_SCHEDULE = {
         "schedule": crontab(minute="0", hour="0"),
         "options": {"expires": 60 * 60 * 24},
     },
+    "federation.refresh_nodeinfo_known_nodes": {
+        "task": "federation.refresh_nodeinfo_known_nodes",
+        "schedule": crontab(minute="0", hour="*"),
+        "options": {"expires": 60 * 60},
+    },
 }
 
+NODEINFO_REFRESH_DELAY = env.int("NODEINFO_REFRESH_DELAY", default=3600 * 24)
+
 JWT_AUTH = {
     "JWT_ALLOW_REFRESH": True,
     "JWT_EXPIRATION_DELTA": datetime.timedelta(days=7),
diff --git a/api/funkwhale_api/federation/tasks.py b/api/funkwhale_api/federation/tasks.py
index 8aebcb27a8..38e8eb6776 100644
--- a/api/funkwhale_api/federation/tasks.py
+++ b/api/funkwhale_api/federation/tasks.py
@@ -212,6 +212,22 @@ def update_domain_nodeinfo(domain):
     domain.save(update_fields=["nodeinfo", "nodeinfo_fetch_date", "service_actor"])
 
 
+@celery.app.task(name="federation.refresh_nodeinfo_known_nodes")
+def refresh_nodeinfo_known_nodes():
+    """
+    Trigger a node info refresh on all nodes that weren't refreshed since
+    settings.NODEINFO_REFRESH_DELAY
+    """
+    limit = timezone.now() - datetime.timedelta(seconds=settings.NODEINFO_REFRESH_DELAY)
+    candidates = models.Domain.objects.external().exclude(
+        nodeinfo_fetch_date__gte=limit
+    )
+    names = candidates.values_list("name", flat=True)
+    logger.info("Launching periodic nodeinfo refresh on %s domains", len(names))
+    for domain_name in names:
+        update_domain_nodeinfo.delay(domain_name=domain_name)
+
+
 def delete_qs(qs):
     label = qs.model._meta.label
     result = qs.delete()
diff --git a/api/tests/federation/test_tasks.py b/api/tests/federation/test_tasks.py
index 5c699cd721..4428484b98 100644
--- a/api/tests/federation/test_tasks.py
+++ b/api/tests/federation/test_tasks.py
@@ -216,6 +216,31 @@ def test_update_domain_nodeinfo_error(factories, r_mock, now):
     }
 
 
+def test_refresh_nodeinfo_known_nodes(settings, factories, mocker, now):
+    settings.NODEINFO_REFRESH_DELAY = 666
+
+    refreshed = [
+        factories["federation.Domain"](nodeinfo_fetch_date=None),
+        factories["federation.Domain"](
+            nodeinfo_fetch_date=now
+            - datetime.timedelta(seconds=settings.NODEINFO_REFRESH_DELAY + 1)
+        ),
+    ]
+    factories["federation.Domain"](
+        nodeinfo_fetch_date=now
+        - datetime.timedelta(seconds=settings.NODEINFO_REFRESH_DELAY - 1)
+    )
+
+    update_domain_nodeinfo = mocker.patch.object(tasks.update_domain_nodeinfo, "delay")
+
+    tasks.refresh_nodeinfo_known_nodes()
+
+    assert update_domain_nodeinfo.call_count == len(refreshed)
+
+    for d in refreshed:
+        update_domain_nodeinfo.assert_any_call(domain_name=d.name)
+
+
 def test_handle_purge_actors(factories, mocker):
     to_purge = factories["federation.Actor"]()
     keeped = [
-- 
GitLab