<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SiddhantSingh</title>
    <description>The latest articles on DEV Community by SiddhantSingh (@siddhantsingh).</description>
    <link>https://dev.to/siddhantsingh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F343482%2F98291b44-981a-42b0-a2b4-ed8b0e283f5f.jpeg</url>
      <title>DEV Community: SiddhantSingh</title>
      <link>https://dev.to/siddhantsingh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siddhantsingh"/>
    <language>en</language>
    <item>
      <title>Cache the data using Redis in Elixir/Phoenix</title>
      <dc:creator>SiddhantSingh</dc:creator>
      <pubDate>Sun, 01 Mar 2020 12:25:10 +0000</pubDate>
      <link>https://dev.to/siddhantsingh/cache-the-data-using-redis-in-elixir-phoenix-280d</link>
      <guid>https://dev.to/siddhantsingh/cache-the-data-using-redis-in-elixir-phoenix-280d</guid>
      <description>&lt;p&gt;Recently I stumble upon a problem where I had to cache the data which I was getting from the database. But for this, you need to have a little bit of understanding of genservers and supervisors. &lt;/p&gt;

&lt;p&gt;Let's start with Redis and why we need it? &lt;/p&gt;

&lt;p&gt;The distributed cache is a widely-adopted design pattern for building robust and scalable applications. By serving data from the cache, and instead of fetching it from slower devices or re-computing it, the application’s overall performance is improved. The cache also relieves&lt;br&gt;
some of the load on external data sources as well as improves the availability of the application during outages.&lt;/p&gt;

&lt;p&gt;Redis is the most popular distributed caching engine today. It is a production-proven and provides a host of capabilities that make it the ideal distributed caching layer for applications. Redis Cloud and RLEC provide additional capabilities that extend the abilities of open source&lt;br&gt;
Redis to make it even more suitable for caching purposes.&lt;/p&gt;

&lt;p&gt;Redix&lt;/p&gt;

&lt;p&gt;Redix provides the main API to interface with Redis. We will use this library to interact with redis server. But we'll get to this later. &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def start_link(url) do
   GenServer.start_link(__MODULE__, {url})
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;start_link here starts a process that connects to Redis. Each Elixir process started with this function maps to a client TCP connection to the specified Redis server.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def init({url}) do
  Logger.info("connect to url #{url}");
  case Redix.start_link(url) do
  {:ok, conn} -&amp;gt; {:ok, conn}
  {:error, err} -&amp;gt; {:error, err}
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So here start_link function will invoke the init function. Now just run this &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex -S mix phx.server
Database.start_link("redis://127.0.0.1:6379")

iex(2)&amp;gt; Database.start_link("redis://127.0.0.1:6379")
[info] connect to url redis://127.0.0.1:6379
{:ok, #PID&amp;lt;0.448.0&amp;gt;}
iex(3)&amp;gt; {:ok, pid} =  Database.start_link("redis://127.0.0.1:6379") 
[info] connect to url redis://127.0.0.1:6379
{:ok, #PID&amp;lt;0.452.0&amp;gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now here you're doing a pattern matching. On the left side, it is a tuple that has the first argument as atom :ok and second has a variable. If you check the variable it would return a process id. Now you're connected to Redis server and you have a process running with that pid. &lt;/p&gt;

&lt;p&gt;Now to set a key-value command in Redix. You have to use some binaries if you're not using one of those binaries it will suggest to you use those binaries. Now you're confused here about the binaries let me tell you the Elixir Compiler expects certain parameters to pass in case if you miss something it would tell you to use those binaries.&lt;/p&gt;

&lt;p&gt;Okay let's get back to code so it would suggest you to something like this &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Redix.command!(conn, ["SET", "mykey", "foo"])
 "OK"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I wanted to set a key and value. But I wanted the value to be the data that I was fetching from the database. But here the problem is the data which I was getting database was a map. So I figured if I could somehow  set a normal key and value then I will resolve the other problem. &lt;/p&gt;

&lt;p&gt;So here what I did&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; def set(conn, key, value) do
   GenServer.call(conn, {:set, key, value})
 end

 def handle_call({:set, key, value}, _from, state ) do
   reply = Redix.command(state, ["SET", key, value])
   {:reply, {:ok, reply}, state}
 end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What I did here is I have a set function that is expecting a conn, key, and value which you want to set. The set function will invoke a handle_call function which is where the main logic happens and you will able to set a key and value. That's the beauty of genserver because it let you maintain the state. If you run this it would give you something like this&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(6)&amp;gt; Database.set(pid, "onetwo", "three")
# {:ok, {:ok, "OK"}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But now what I want is to fetch a database from the database for which I want to create a function which will take a pid as first argument and id which I wanted to fetch. &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def userone(pid, id) do
  GenServer.call(pid, {:userone, id})
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So I created a function that expects that argument and will invoke the handle_call function.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_call({:userone, id}, _from, state) do
  user = Accounts.get_user!(id)
  {:reply, user, state}
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now this will get the single user from the database. Just call this function and it would return a map. But our set function expects an only string, not a map. So we need to figure out the way to convert a map to a string. &lt;/p&gt;

&lt;p&gt;There are many libraries that can help you to convert the map to string but I used the Poison module which you have to add under your dependencies. &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_call({:set, key, value}, _from, state) when is_map(value) do
 value = Poison.encode!(value)
 reply = Redix.command(state, ["SET", key, value])
 {:reply, {:ok, reply}, state}
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So I did exactly that so I used an Elixir guard function and if the value is map go-ahead and converts that map to a string. Now if you see it will convert that map to string and will save it to Redis.&lt;/p&gt;

&lt;p&gt;Now if you want to see how can you get the data which you've just set. Don't worry there is a function for that. But first, let's see how it works in redix. &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Redix.command(conn, ["GET", "mykey"])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using this command I created another function that will just get the key which you've just set.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; # get the key function
 def get(pid, key) do
   GenServer.call(pid, {:get, key})
 end

 # handle call for get function
 def handle_call({:get, key}, _from, state) do
   reply = Redix.command(state, ["GET", key])
   {:reply, {:ok, reply}, state}
 end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is how it will look like in the end&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; # iex(6)&amp;gt; Database.set(pid, "onetwo", "three")
 # {:ok, {:ok, "OK"}}
 # iex(7)&amp;gt; Database.get(pid, "onetwo")
 # {:ok, {:ok, "three"}}
 # iex(8)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some useful information about redix connection:- &lt;/p&gt;

&lt;p&gt;Redix tries to be as resilient as possible: it tries to recover automatically from most network errors.&lt;/p&gt;

&lt;p&gt;If there's a network error when sending data to Redis or if the connection to Redis drops, Redix tries to reconnect. The first reconnection attempt will happen after a fixed time interval; if this attempt fails, reconnections are attempted until successful, and the time interval between reconnections is increased exponentially.&lt;/p&gt;

&lt;p&gt;Here how it will look like in the end. &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule Gorm.Database do
  use GenServer
  require Logger
  alias Gorm.Accounts

  # "redis://localhost:6379/3"

  #connect the redis server
  def start_link(url) do
    GenServer.start_link(__MODULE__, {url})
  end

 def init({url}) do
    Logger.info("connect to url #{url}");
    case Redix.start_link(url) do
    {:ok, conn} -&amp;gt; {:ok, conn}
    {:error, err} -&amp;gt; {:error, err}
    end
 end

 # checking the connection if it's connected to redis or not
def check(pid) do
   GenServer.call(pid, :check)
end

 # handle call for check function
def handle_call(:check, _from, state) do
  checking = Redix.command!(state, ["PING"])
  {:reply, checking, state}
end

# get the key function
def get(pid, key) do
  GenServer.call(pid, {:get, key})
end

# handle call for get function
def handle_call({:get, key}, _from, state) do
  reply = Redix.command(state, ["GET", key])
  {:reply, {:ok, reply}, state}
end

# set the key function
def set(conn, key, value) do
  GenServer.call(conn, {:set, key, value})
end

#handle call for set function
def handle_call({:set, key, value}, _from, state ) do
  state = Exq.enqueue(Exq, "q1", SetWorker, [key, value])
  {:reply, state, state}
end

def handle_call({:set, key, value}, _from, state) when is_map(value) do
  value = Poison.encode!(value)
  reply = Redix.command(state, ["SET", key, value])
  {:reply, {:ok, reply}, state}
end

 # Get the single user from the database
 def userone(pid, id) do
   GenServer.call(pid, {:userone, id})
 end

 # Get the list of user from the database
 def list(pid) do
   GenServer.call(pid, :list)
 end

 # handle call for the list function
 def handle_call(:list, _from, state) do
   my_models = Accounts.list_users()

  {:reply, my_models, state}
 end

 # handle call for the single user function
  def handle_call({:userone, id}, _from, state) do
    user = Accounts.get_user!(id)
    {:reply, user, state}
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want to see a full code and how I've implemented it please go to my github. &lt;/p&gt;

&lt;p&gt;github.com/siddhant3030/Micro/gorm&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>redis</category>
      <category>github</category>
    </item>
  </channel>
</rss>
