0

I am using GenServer in my module as below. In the init method, it creates a redis connection to the database. The put method will send values to be saved in redisdb. The handle_cast method will call the command on redis connection to do the database operation.

defmodule RedisClient do
  use GenServer
  require Logger

  # Client
  def start(url, pwd, hkey) do
    GenServer.start(__MODULE__, {url, pwd, hkey});
  end

  def init({url, pwd, hkey}) do
    Logger.info("connect to url #{url} #{pwd} #{hkey}");
    case Redix.start_link(url) do
      {:ok, conn} -> case Redix.command(conn, ["auth", pwd]) do
        {:ok, message} -> {:ok, conn, hkey}
        _ -> {:error}
      end
    end
  end

  def put(pid, field, value) do
    Logger.info("put #{field} #{value}")
    GenServer.cast(pid, {:hset, field, value})
  end

  def handle_cast({:hset, field, value}, {conn, hkey}) do
    Logger.info("write to database #{field} #{value}")
    result = Redix.command(conn, ["hset", hkey, field, value]);
    {:noreply, {conn, hkey}}
  end

end 

Below is the output from iex console. The database connection is established but the handle_cast is not called when I call put method. What is wrong with my implementation?

iex(tbc@192-168-1-7)85> {:ok, pid} = RedisClient.start("redis://localhost", "mypassword", "mykey")
{:ok, #PID<0.23038.0>}
iex(tbc@192-168-1-7)86> 
11:19:49.554 [info]  connect to url redis://localhost mypassword mykey
iex(tbc@192-168-1-7)87> RedisClient.put(pid, "field1", "value1")
:ok
iex(tbc@192-168-1-7)88> 
11:20:26.429 [info]  put field1 value1
Joey Yi Zhao
  • 37,514
  • 71
  • 268
  • 523
  • I think you need to return `{:ok, {conn, hkey}}` from `init/1`. – Dogbert Dec 30 '17 at 01:06
  • Start by using `start_link` - only use `start` if you know what you're doing, by default you want to have linked processes. I don't get the question completely - your REPL session shows the correct behaviour. What is wrong? – cdegroot Dec 30 '17 at 15:46
  • What is the different between `start_link` and `start`? The problem is `handle_cast` is not called and @Justin_Wood gave the correct answer for that. – Joey Yi Zhao Dec 30 '17 at 22:08
  • @ZhaoYi `start` will only start the new process. `start_link` will both start the new process and link it to the current one. The link means that the current process will be notified if the new process dies or crashes for any abnormal reason. You can read more [here](http://elixir-lang.github.io/getting-started/processes.html#links). – Justin Wood Dec 30 '17 at 22:17
  • @JustinWood Good to know, thanks – Joey Yi Zhao Dec 30 '17 at 22:24
  • Something completely different: you don't really need a GenServer here, I'd say. You can simply open the Redix connection and do puts and gets directly. – cdegroot Dec 31 '17 at 18:19

1 Answers1

0

Your GenServer is not actually running. If you were to run the following code, you will get the same result.

iex(1)> {:ok, pid} = RedisClient.start(url, pwd, hkey)
iex(2)> Process.alive?(pid)
false

The problem is that you are returning a three element tuple from your GenServer's init/1 callback. The third option is supposed to be a timeout. Your GenServer is crashing when it starts because the hkey is not holding a valid timeout value.

In your case, you will want to return {:ok, {conn, hkey}} instead.

Justin Wood
  • 9,941
  • 2
  • 33
  • 46