I am using melpon/memoize for cache in my phoenix aplication. It is working very well in runtime.
However, I have a few mix tasks in my application, which I execute sparingly. Now these tasks eventually call a memoized function, for example:
defmodule Mix.Tasks.MyTask do
use Mix.Task
alias MemoizedModule
require Logger;
use Memoize
def run(_) do
MemoizedModule.get_value()
end
end
defmodule MemoizedModule do
use Memoize
defmemo get_value() do
1
end
end
and it crashes with:
/opt/app # mix my_task
** (ArgumentError) errors were found at the given arguments:
* 1st argument: no persistent term stored with this key
:persistent_term.get(:memoize_cache_strategy)
(memoize 1.4.0) lib/memoize/config.ex:29: Memoize.Config.cache_strategy/0
(memoize 1.4.0) lib/memoize/cache.ex:11: Memoize.Cache.tab/1
(memoize 1.4.0) lib/memoize/cache.ex:103: Memoize.Cache.do_get_or_run/3
(mix 1.12.3) lib/mix/task.ex:394: anonymous fn/3 in Mix.Task.run_task/3
(mix 1.12.3) lib/mix/cli.ex:84: Mix.CLI.run_task/2
(elixir 1.12.3) lib/code.ex:1261: Code.require_file/2
If I try to execute the code inside IEx, it works as expected:
/opt/app # iex -S mix
Interactive Elixir (1.12.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Mix.Tasks.MyTask.run()
1
I know it has something to do with some initialization that is not done in mix tasks, but I can´t figure out what it is.
We need to start applications explicitly in mix tasks.
Codes are loaded, but applications are not started when running mix commands.
Mix does not automatically start our application or any of its dependencies ... Ref
So we need to start the it explicitly:
def run(_) do
{:ok, _} = Application.ensure_all_started(:memoize)
MemoizedModule.get_value()
end
You may need to change :memoize
to :my_otp_app
if you need other initialization tasks in the app.
Why does it work inside iex -S mix
?
That's because iex -S mix [run]
has started your application for you.