diff --git a/lib/cacheman.ex b/lib/cacheman.ex index d08817e..982dab3 100644 --- a/lib/cacheman.ex +++ b/lib/cacheman.ex @@ -41,6 +41,13 @@ defmodule Cacheman do dashboard end) + 2. Timeouts: + + By default, every redis command has to execute in 5 seconds. If there is a need + for longer timeout, it can be passed as: + + {:ok, entry} = Cacheman.put_batch(:app, [{key, value}, ...], timeout: 20_000) + 2. Get a key: {:ok, entry} = Cacheman.get(:app, "user-dashboard") @@ -81,8 +88,8 @@ defmodule Cacheman do use GenServer require Logger + @default_global_options [timeout: 5_000] @default_put_options [ttl: :infinity] - # # Cacheman API # @@ -148,12 +155,15 @@ defmodule Cacheman do Where in the previous example, the cache key will be storred for 5 minutes. Nil values are not storrable in the cache. + """ - def put(name, key, value, put_opts \\ @default_put_options) do + def put(name, key, value, opts \\ []) do + opts = (@default_put_options ++ @default_global_options) |> Keyword.merge(opts) + if value == nil do {:ok, nil} else - GenServer.call(full_process_name(name), {:put, key, value, put_opts}) + GenServer.call(full_process_name(name), {:put, key, value, opts}, opts[:timeout]) end end @@ -173,8 +183,10 @@ defmodule Cacheman do @spec put_batch(String.t(), list(key_value_pair), list()) :: {:ok, integer} | {:error, Redix.Protocol.ParseError | Redix.Error | Redix.ConnectionError} - def put_batch(name, key_value_pairs, put_opts \\ @default_put_options) do - GenServer.call(full_process_name(name), {:put_batch, key_value_pairs, put_opts}) + def put_batch(name, key_value_pairs, opts \\ []) do + opts = (@default_put_options ++ @default_global_options) |> Keyword.merge(opts) + + GenServer.call(full_process_name(name), {:put_batch, key_value_pairs, opts}, opts[:timeout]) end @doc """ @@ -306,6 +318,8 @@ defmodule Cacheman do end def handle_call({:put_batch, key_value_pairs, put_opts}, _from, opts) do + Logger.debug("[Cacheman] OPTS: #{inspect(put_opts)}") + response = apply(opts.backend_module, :put_batch, [ opts.backend_pid, diff --git a/lib/cacheman/backend/redis.ex b/lib/cacheman/backend/redis.ex index 794e00b..b080a1a 100644 --- a/lib/cacheman/backend/redis.ex +++ b/lib/cacheman/backend/redis.ex @@ -41,24 +41,34 @@ defmodule Cacheman.Backend.Redis do end) end - def put(conn, key, value, ttl) do - :poolboy.transaction(conn, fn c -> - case Redix.command(c, ["SET", key, value] ++ ttl_command(ttl)) do - {:ok, "OK"} -> {:ok, value} - e -> e - end - end) + def put(conn, key, value, opts) do + :poolboy.transaction( + conn, + fn c -> + case Redix.command(c, ["SET", key, value] ++ ttl_command(opts[:ttl]), + timeout: opts[:timeout] + ) do + {:ok, "OK"} -> {:ok, value} + e -> e + end + end, + opts[:timeout] + ) end - def put_batch(conn, key_value_pairs, ttl) when is_list(key_value_pairs) do + def put_batch(conn, key_value_pairs, opts) when is_list(key_value_pairs) do list_of_commands = Enum.map(key_value_pairs, fn {key, value} -> - ["SET", key, value] ++ ttl_command(ttl) + ["SET", key, value] ++ ttl_command(opts[:ttl]) end) - :poolboy.transaction(conn, fn c -> - Redix.pipeline(c, list_of_commands) - end) + :poolboy.transaction( + conn, + fn c -> + Redix.pipeline(c, list_of_commands, timeout: opts[:timeout]) + end, + opts[:timeout] + ) end def delete(conn, keys) do @@ -73,6 +83,6 @@ defmodule Cacheman.Backend.Redis do end) end - def ttl_command(ttl: :infinity), do: [] - def ttl_command(ttl: ttl), do: ["PX", "#{ttl}"] + def ttl_command(:infinity), do: [] + def ttl_command(ttl), do: ["PX", "#{ttl}"] end diff --git a/mix.exs b/mix.exs index 2aaa326..e3f8b24 100644 --- a/mix.exs +++ b/mix.exs @@ -22,6 +22,7 @@ defmodule ExCacheman.MixProject do defp deps do [ {:redix, "~> 0.10.5"}, + {:mock, "~> 0.3.0", only: :test}, {:poolboy, "~> 1.5"} ] end diff --git a/mix.lock b/mix.lock index 329f17f..9f95ad2 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,6 @@ %{ + "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, + "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "redix": {:hex, :redix, "0.10.5", "2240a74cc0a236e0e0d8ebf2d20674151d59e9965342879d36cf856d93099f01", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "37774c9753a4b3bb7cb263b5996357ffc319129c70bb698f5cc02d676c704b81"}, "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"}, diff --git a/test/cacheman_test.exs b/test/cacheman_test.exs index 5f7ff00..fe873ce 100644 --- a/test/cacheman_test.exs +++ b/test/cacheman_test.exs @@ -1,5 +1,6 @@ defmodule CachemanTest do use ExUnit.Case + import Mock doctest Cacheman setup_all do @@ -72,6 +73,18 @@ defmodule CachemanTest do assert {:ok, nil} = Cacheman.get(:good, key2) end + test "put_batch with custom timeout" do + timeout = 10_000 + + with_mock Redix, [], + pipeline: fn _, _, opts -> + :timer.sleep(trunc(timeout * 0.7)) + assert opts[:timeout] == timeout + end do + assert Cacheman.put_batch(:good, [{"test_key", "test_value"}], timeout: timeout) + end + end + test "get_batch" do key1 = "test-#{:rand.uniform(10_000)}" key2 = "test-#{:rand.uniform(10_000)}" @@ -171,6 +184,18 @@ defmodule CachemanTest do assert {:ok, nil} = Cacheman.get(:good, key2) end + test "put_batch with custom timeout excided" do + timeout = 10_000 + + with_mock Redix, [], + pipeline: fn _, _, opts -> + assert opts[:timeout] == timeout + :timer.sleep(trunc(timeout * 1.2)) + end do + catch_exit(Cacheman.put_batch(:broken, [{"test_key", "test_value"}], timeout: timeout)) + end + end + test "get_batch" do key1 = "test-#{:rand.uniform(10_000)}" key2 = "test-#{:rand.uniform(10_000)}"