运行-一次 Elixir 进程

Run-once Elixir process

我试图在应用程序启动时通过调用 genServer 模块 "Cache"

创建 "Cache warmer" 到 运行 一次

我创建了一些代码: GenServer "Cache Warmer",用于处理应用程序启动时的单个异步调用,配置为[restart::temporary]。主要思想是 return {:stop, :normal, state} 施放后,关闭进程

defmodule TlgmBot.Application do
 ...
     def start(_type, _args) do
     ...
       children = [
       ... some stuff ...
        %{
          id: Services.Cache.CacheWarmer,
          start: {Services.Cache.CacheWarmer, :start_link, [restart: :temporary]},
          type: :supervisor
      }

        %{
          id: Services.Cache.Cache,
          start: {Services.Cache.Cache, :start_link, []},
          type: :supervisor
      },
    end
end
defmodule Services.Cache.CacheWarmer do
 use GenServer

  def start_link(_state \ []) do
    GenServer.start_link(__MODULE__, [:ok], name: __MODULE__)
  end

  def handle_cast({:warm_up_cache}, state) do
    debug "loading users..."
    load_users()
    debug "done."
    load_mfc()

    {:stop, :normal, state}
  end

  defp load_users() do
    result = RedmineApi.request_rm_users()

    case result do
    {:ok, users} -> Cache.save_users(users)
                    {:ok}
    _            -> {:error}
    end
  end
end

并且过程"Cache warmer"仍然运行一次又一次

请指出完成此任务的正确方法或帮助我弄清楚我在这里做错了什么。

也许我应该在 application.start() 中添加几行来调用缓存模块并忘记它?

由于您的 Cache Warmer 不使用它的状态或者一旦它履行了它的职责就不需要存在,我建议您在启动您的应用程序时或在您的 handle_continue 内部调用一个函数缓存。这会发生 post init 以免阻止启动其他 children.

参见:GenServer.handle_continue/2

除了handle_continue/2,由@aleksei-matiushkin 发布,你还可以在你的监督树缓存 之后有一个任务:

MyApp.Cache,
{Task, &MyApp.Cache.warmup/0}

默认情况下任务是临时的,这意味着它不会在崩溃时重新启动。请注意,如果缓存进程崩溃,handle_continue/2 将在进程重新启动后再次 运行。如果监督策略为 :one_for_one,则任务不会再次 运行,但会针对 :rest_for_one:one_for_all

您发布的代码有两个问题:缓存预热器缓存之前启动,因此转换请求找不到缓存服务器。它还将子项列为 type: :supervisor,这应该只针对主管(它不会在这里导致实际错误,但它可能会在关机、热代码升级等期间导致问题)。