Comments (9)
defmodule MyMemoize do
defmacro __using__(_) do
quote do
if Mix.env() == :benchmark do
use Memoize
else
import MyMemoize,
only: [defmemo: 1, defmemo: 2, defmemo: 3, defmemop: 1, defmemop: 2, defmemop: 3]
end
end
defmacro defmemo(call, expr_or_opts \\ nil) do
{_opts, expr} = resolve_expr_or_opts(expr_or_opts)
quote do
def(unquote(call), unquote(expr))
end
end
defmacro defmemop(call, expr_or_opts \\ nil) do
{_opts, expr} = resolve_expr_or_opts(expr_or_opts)
quote do
defp(unquote(call), unquote(expr))
end
end
defmacro defmemo(call, _opts, expr) do
quote do
def(unquote(call), unquote(expr))
end
end
defmacro defmemop(call, _opts, expr) do
quote do
defp(unquote(call), unquote(expr))
end
end
defp resolve_expr_or_opts(expr_or_opts) do
cond do
expr_or_opts == nil ->
{[], nil}
# expr_or_opts is expr
Keyword.has_key?(expr_or_opts, :do) ->
{[], expr_or_opts}
# expr_or_opts is opts
true ->
{expr_or_opts, nil}
end
end
end
defmodule Foo do
use MyMemoize
# It will not be memoized if Mix.env() is :benchmark
defmemo foo() do
do_foo()
end
end
from memoize.
I decided to implement a DummyCacheStrategy
that I use in my test env, which in the read/3
function immediately invalidates everything in its tab before returning :ok
. I think this is the equivalent of never storing the result in the cache. Seems to be working well so far.
I found that the expires_in: 0
option wasn't reliable enough for my needs, since it was only invalidating during a read after at least 1ms had passed since the value was cached. This would result in intermittent test failures in my test suite, which needs to completely disable the cache.
config :memoize, cache_strategy: MyApp.DummyCacheStrategy
defmodule MyApp.DummyCacheStrategy do
@behaviour Memoize.CacheStrategy
@ets_tab __MODULE__
def init(opts) do
:ets.new(@ets_tab, [:public, :set, :named_table, {:read_concurrency, true}])
opts
end
def tab(_), do: @ets_tab
def cache(_, _, _), do: nil
def read(_, _, _) do
invalidate()
:ok
end
def invalidate() do
:ets.select_delete(@ets_tab, [{{:_, {:completed, :_, :_}}, [], [true]}])
end
def invalidate(_), do: invalidate()
def garbage_collect(), do: invalidate()
end
from memoize.
Thank you very much! :)
I'll give it a whirl.
from memoize.
I think that such dummy strategy (as suggested by @tylerj) should be a part of the library and suggested as a way to go about testing. Most of the time that will be the behaviour the tester expects. To say more - it is confusing that the user can set "expires_in" to 0 and that is not exactly true (which is fine outside of the tests realm as requiring serialization would have performance implications).
from memoize.
if Mix.env() == :benchmark do
def foo() do
do_foo()
end
else
defmemo foo() do
do_foo()
end
end
from memoize.
Just a slightly fixed version of @melpon solution:
defmodule MyApp.Memoize do
defmacro __using__(_) do
if Mix.env() == :test do
quote do
import MyApp.Memoize,
only: [defmemo: 1, defmemo: 2, defmemo: 3, defmemop: 1, defmemop: 2, defmemop: 3]
end
else
quote do
use Memoize
end
end
end
defmacro defmemo(call, expr_or_opts \\ nil) do
{_opts, expr} = resolve_expr_or_opts(expr_or_opts)
quote do
def(unquote(call), unquote(expr))
end
end
defmacro defmemop(call, expr_or_opts \\ nil) do
{_opts, expr} = resolve_expr_or_opts(expr_or_opts)
quote do
defp(unquote(call), unquote(expr))
end
end
defmacro defmemo(call, _opts, expr) do
quote do
def(unquote(call), unquote(expr))
end
end
defmacro defmemop(call, _opts, expr) do
quote do
defp(unquote(call), unquote(expr))
end
end
defp resolve_expr_or_opts(expr_or_opts) do
cond do
expr_or_opts == nil -> {[], nil}
Keyword.has_key?(expr_or_opts, :do) -> {[], expr_or_opts}
true -> {expr_or_opts, nil}
end
end
end
from memoize.
Thank you @cblavier!
I think it would be really useful to add this into the library, perhaps as a config flag?
config :my_app, Memoize, caching_enabled: false
I don't think the particular mechanism matters, but in a standard web app scenario I imagine this would be a very common need.
By "standard web app scenario", I mean a web application that does CRUD and has integration tests. The Ecto Sandbox adapter prevents data from "bleeding over" between concurrently running tests, but when you use Memoize
there is bleed-over. This cost me a few hours of debugging before I realized Memoize was the cause.
@melpon By the way, this is a reasoned suggestion, not a complaint. Thanks for sharing this useful library!
from memoize.
If the configuration route is undesirable, instructions about this scenario could perhaps be added to the README
?
from memoize.
I implemented global :expires_in
option since 1.4.0.
config :memoize,
cache_strategy: Memoize.CacheStrategy.Default
config :memoize, Memoize.CacheStrategy.Default,
expires_in: 0 # invalidate immediately
However, this is not exactly the same as disabling the cache. All concurrent calls by many processes are serialized.
from memoize.
Related Issues (15)
- DateTime in parameter causes en error HOT 2
- Freezes up when get_or_run process crashes HOT 12
- (ExUnit.TimeoutError) test timed out after 100ms. HOT 1
- Cannot run benchmark HOT 1
- Refresh timeout option HOT 1
- Support for unquote fragments HOT 3
- Failed to invalidate cache with argument is map HOT 2
- Is it discouraged to pass a map (containing multiple key values) as a parameter to a memoized function? HOT 2
- Is there a way to invalidate cached functions based on one of multiple arguments? HOT 2
- System memory spike caused by waiter_pids HOT 8
- defmemo/defmemop suppresses compiler warnings HOT 3
- waiter_sleep_ms: 0 HOT 1
- disable in test env HOT 2
- List argument causes argument error HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from memoize.