defmodule Backend.Crawler.StaleInstanceManager do use GenServer alias Backend.{Crawl, Instance, Repo} import Ecto.Query import Backend.Util require Logger @moduledoc """ This module regularly finds stale instances (i.e. instances that haven't been updated for longer than the crawl interval) and adds them to the job queue. It runs once a minute. """ def start_link(_opts) do GenServer.start_link(__MODULE__, [], name: __MODULE__) end @impl true def init(_opts) do instance_count = Instance |> where([i], not is_nil(i.version)) |> select([i], count(i.domain)) |> Repo.one() case instance_count do # Add m.s. as the seed and schedule the next add 0 -> add_to_queue("mastodon.social") schedule_add() # Start immediately _ -> Process.send(self(), :queue_stale_domains, []) end {:ok, []} end @impl true def handle_info(:queue_stale_domains, state) do queue_stale_domains() schedule_add() {:noreply, state} end defp schedule_add() do Process.send_after(self(), :queue_stale_domains, 60_000) end defp queue_stale_domains() do interval = -1 * get_config(:crawl_interval_mins) # Get domains that have never been crawled and where the last crawl is past the threshold crawls_subquery = Crawl |> select([c], %{ instance_domain: c.instance_domain, most_recent_crawl: max(c.inserted_at), crawl_count: count(c.id) }) |> where([c], is_nil(c.error)) |> group_by([c], c.instance_domain) stale_domains = Instance |> join(:left, [i], c in subquery(crawls_subquery), on: i.domain == c.instance_domain) |> where( [i, c], c.most_recent_crawl < datetime_add(^NaiveDateTime.utc_now(), ^interval, "minute") or is_nil(c.crawl_count) ) |> select([i], i.domain) |> Repo.all() Logger.debug("Adding #{length(stale_domains)} stale domains to queue.") stale_domains |> Enum.each(fn domain -> add_to_queue(domain) end) end defp add_to_queue(domain) do {:run, [domain]} |> Honeydew.async(:crawl_queue) end end