<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Pratik's Blog]]></title><description><![CDATA[Pratik's Blog]]></description><link>https://blog.pratikd.in</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1641881517104/HDqTd2fgh.png</url><title>Pratik&apos;s Blog</title><link>https://blog.pratikd.in</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 23 Feb 2024 07:42:10 GMT</lastBuildDate><atom:link href="https://blog.pratikd.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="next" href="https://blog.pratikd.in/rss.xml?page=1"/><item><title><![CDATA[Scripting Redis using Lua]]></title><description><![CDATA[<p>Redis is an awesome data structures server. It provides access to mutable data structures via a set of commands.</p><blockquote><p>Data structures store tied to simple a TCP server, how cool is that!!</p></blockquote><p>While Redis is fairly easy to use via a set of commands, issues start arising when we try to implement complex operations that comprise multiple Redis commands.</p><p>For example, let's consider an operation that performs the following steps:</p><ol><li>Pushes a list of integers to a Redis list <code>mylist</code>.</li><li>Update the value of key <code>sum</code> that stores the sum of all integers in list <code>mylist</code>.</li></ol><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641841840720/hVEckeMYd.png" alt="Visual representation of the operation that we want to perform" /></p><p>Lets try to implement this; Im using <a target="_blank" href="https://github.com/redis/redis-py">redis-py</a>, a Python Redis client.</p><pre><code class="lang-python"><span class="hljs-keyword">import</span> redisr = redis.Redis(host=<span class="hljs-string">'localhost'</span>, port=<span class="hljs-number">6379</span>, db=<span class="hljs-number">0</span>)new_list = [<span class="hljs-number">2</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>]<span class="hljs-comment"># Pushing the new_list to the redis list</span>r.lpush(<span class="hljs-string">'mylist'</span>, *new_list)<span class="hljs-comment"># Calculating the sum of the new_list</span>sum_of_new_list = sum(new_list)<span class="hljs-comment"># ... here we can have multiple application-level operations </span><span class="hljs-comment"># ... before calling the next Redis command</span><span class="hljs-comment"># Incrementing the valuof of the redis key "sum" by the sum_of_new_list</span>r.incrby(<span class="hljs-string">'sum'</span>, sum_of_new_list)</code></pre><p>While at first glance the above implementation looks fine, but it is vulnerable to the following problems:</p><ol><li>The atomicity. Ah, a good old problem.  Redis commands <code>r.lpush('mylist', *new_list)</code> and <code>r.incrby('sum', sum_of_new_list)</code> should be atomic, i.e either both of them should execute or none of them should execute. Atomicity is not guaranteed in the above implementation.</li><li>The application (python script in this case) is handling data-level logic.</li></ol><h3 id="heading-using-lua-script">Using Lua script</h3><p>This is where Lua script comes handy. Redis includes <em>server-side scripting</em> with the Lua programming language. This lets you perform a variety of operations inside Redis, which can both simplify your code and increase performance.</p><p><a target="_blank" href="https://redis.io/commands/eval">EVAL</a> command is used to evaluate scripts using the Lua interpreter built into Redis.</p><p>The first argument of <a target="_blank" href="https://redis.io/commands/eval">EVAL</a> is a Lua 5.1 script. The script does not need to define a Lua function (and should not). It is just a Lua program that will run in the context of the Redis server.</p><p>The second argument of <a target="_blank" href="https://redis.io/commands/eval">EVAL</a> is the number of arguments that follows the script (starting from the third argument) that represent Redis key names. The arguments can be accessed by Lua using the <code>KEYS</code> global variable in the form of a one-based array (so <code>KEYS[1]</code>, <code>KEYS[2]</code>, ...).</p><p>All the additional arguments should not represent key names and can be accessed by Lua using the <code>ARGV</code> global variable, very similar to what happens with keys (so <code>ARGV[1]</code>, <code>ARGV[2]</code>, ...).</p><p>We can call Redis commands from a Lua script using the function <code>redis.call()</code>.</p><p>Lets try to write a command using the above information:</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641842005825/huGtQQrza.png" alt="The EVAL command" /></p><p>Redis return values are converted into Lua data types when Lua calls a Redis command using <code>call()</code>. Similarly, Lua data types are converted into the Redis protocol when calling a Redis command and when a Lua script returns a value so that scripts can control what <a target="_blank" href="https://redis.io/commands/eval">EVAL</a> will return to the client.</p><p>Redis uses the same Lua interpreter to run all the commands. Also, Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed.</p><h3 id="heading-loading-lua-scripts-into-redis">Loading Lua scripts into Redis</h3><p>Instead of providing Lua script every time to Redis, we can load the Lua script once using <code>SCRIPT LOAD</code> command and then subsequently run it using the <code>EVALSHA</code> command.</p><p>The first and the only argument of <code>SCRIPT LOAD</code> is a Lua 5.1 script. The script does not need to define a Lua function (and should not). It is just a Lua program that will run in the context of the Redis server.</p><p>After executing <code>SCRIPT LOAD</code> command, a SHA1 digest of the script will be returned. We can call the script by providing SHA1 digest to the EVALSHA command along with needed <code>KEYS</code> and <code>ARGV</code> </p><p>Lets try to write a command using the above information.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641842080872/RNkWY4jIM.png" alt="Using Lua script after loading" /></p><p>Powerful, isnt it? The script is guaranteed to stay in the script cache forever (unless <code>SCRIPT FLUSH</code> is called).</p><p>Now that we have understood how to use Lua scripts with Redis, lets try to implement the same operation using Lua Scripts. Im using <a target="_blank" href="https://github.com/redis/redis-py">redis-py</a>, a python redis client.</p><p>First off let's create the <code>script.lua</code> file that will hold the Lua Script.</p><pre><code class="lang-lua">-- script.lualocal sum_of_new_list = 0-- Pushing the new_list to the redis list -- Also calculating the sum of the new_listfor key,value in ipairs(ARGV) do    sum_of_new_list = sum_of_new_list + tonumber(value)    redis.call('LPUSH', KEYS[1], value)end-- Incrementing the value of the redis key "sum" by the sum_of_new_listlocal total_sum = redis.call('INCRBY', KEYS[2], sum_of_new_list)-- Finally returning the total_sumreturn total_sum</code></pre><p>Now lets implement <code>main.py</code> where we will import <code>script.lua</code> and run it on the Lua interpreter of Redis.</p><pre><code class="lang-python"><span class="hljs-comment"># main.py</span><span class="hljs-keyword">import</span> redisr = redis.Redis(host=<span class="hljs-string">'localhost'</span>, port=<span class="hljs-number">6379</span>, db=<span class="hljs-number">0</span>)<span class="hljs-comment"># Reading script.lua file into a string</span>f = open(<span class="hljs-string">"script.lua"</span>, <span class="hljs-string">"r"</span>)lua = f.read()new_list = [<span class="hljs-number">2</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>]<span class="hljs-comment"># Registering the script on the redis server</span>operation = r.register_script(lua)<span class="hljs-comment"># Executing the script</span>op_return = operation(keys=[<span class="hljs-string">'mylist'</span>, <span class="hljs-string">'sum'</span>], args=new_list)print(op_return)</code></pre><p>Awesome!, we have successfully implemented the needed operation using Lua script. The application-level logic (<code>main.py</code>) and data-level logic (<code>script.lua</code>) are separated. Our Lua script is Atomic and ensures that keys <code>mylist</code> and <code>sum</code> are always in sync.</p><p>Well, thats it for now! Peace 🍻</p>]]></description><link>https://blog.pratikd.in/scripting-redis-using-lua</link><guid isPermaLink="true">https://blog.pratikd.in/scripting-redis-using-lua</guid><dc:creator><![CDATA[Pratik Daigavane]]></dc:creator><pubDate>Mon, 10 Jan 2022 20:09:21 GMT</pubDate><cover_image>https://cdn.hashnode.com/res/hashnode/image/upload/v1641845445178/q2bNf6SSE.png</cover_image></item><item><title><![CDATA[Self-Host Commento using Nginx and Docker]]></title><description><![CDATA[<p><a target="_blank" href="https://commento.io/">Commento</a> is an open-source commenting system that you can embed in your website to allow your readers to add comments. Its reasonably fast lightweight, supports markdown, import from Disqus, voting, automated spam detection, moderation tools, sticky comments, thread locking, OAuth login, single sign-on, and email notifications.</p><p>Although it is open-source, the cloud version is not offered free of cost. This blog will guide you to self-host commento using Nginx and Docker.</p><p>Before moving forward, lets compare Commento with other products such as Disqus, Facebook Comments, etc.</p><h2 id="heading-why-commento">Why Commento?</h2><p>Most other products in this space do not respect your privacy; showing adverts is their primary business model and that nearly always comes at the users cost. There is no free lunch. Commento is also orders of magnitude lighter than alternatives  while Disqus and Facebook take megabytes of download to load, Commento is just 11 kB.</p><h2 id="heading-prerequisites">Prerequisites</h2><p>Before proceeding make sure you keep following things ready:</p><ul><li><p>An Ubuntu host with Public IP.</p></li><li><p>If your domain name is <strong>example.com</strong>, it would be a good idea to use subdomain <strong>commento.example.com</strong>. So add <strong>DNS A Record</strong> which points the subdomain to the hosts IP Address.</p></li><li><p>A terminal session into your target host for running Commento.</p><blockquote><p><em>example.com is just a placeholder replace it with your domain name</em></p></blockquote></li></ul><h2 id="heading-nginx-configuration">Nginx Configuration</h2><p>We will use <a target="_blank" href="https://www.nginx.com/">Nginx</a> as a <a target="_blank" href="https://www.nginx.com/resources/glossary/reverse-proxy-server/">reverse proxy</a> which will pass traffic from <code>commento.example.com</code> to <code>localhost:8080</code> where commento will be running.</p><p>First off install Nginx using apt.</p><pre><code><span class="hljs-attribute">sudo</span> apt install nginx</code></pre><p>We will also generate SSL certificate for <strong>commento.example.com. </strong>This is where <a target="_blank" href="https://letsencrypt.org/">Lets Encrypt</a> comes handy.</p><p>Install <a target="_blank" href="https://certbot.eff.org/">certbot</a> and its nginx plugin</p><pre><code>sudo apt install python3<span class="hljs-operator">-</span>certbot python3<span class="hljs-operator">-</span>certbot<span class="hljs-operator">-</span>nginx</code></pre><p>Add the following server block configuration to <strong>/etc/nginx/sites-enabled/default</strong></p><pre><code><span class="hljs-section">server</span> {    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;    <span class="hljs-comment"># Replace this with your domain</span>    <span class="hljs-attribute">server_name</span> commento.example.com;    <span class="hljs-attribute">location</span> / {        <span class="hljs-attribute">proxy_pass</span> http://127.0.0.1:8080;    }}</code></pre><h2 id="heading-installing-ssl-certificate">Installing SSL Certificate</h2><p>Now that we have configured our reverse proxy, lets add SSL certificate to it</p><pre><code>sudo certbot <span class="hljs-operator">-</span><span class="hljs-operator">-</span>nginx</code></pre><p>Enter your email, subdomain and other details which are asked.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618067414683/GXPJUdN3t.png" alt /></p><p>Certbot will create a SSL certificate and then it will automatically integrate it with Nginx.</p><p>I recommend running the following line, which will add a cron job to the default crontab. This will ensure that the SSL certificate is automatically renewed.</p><pre><code>echo <span class="hljs-string">"0 0,12 * * * root python -c 'import random; import time; time.sleep(random.random() * 3600)' &amp;&amp; certbot renew -q"</span> <span class="hljs-operator">|</span> sudo tee <span class="hljs-operator">-</span>a <span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>crontab <span class="hljs-operator">&gt;</span> <span class="hljs-operator">/</span>dev<span class="hljs-operator">/</span>null</code></pre><p>And thats it! You have successfully configured Nginx with SSL Certificate. Now lets install Commento using docker.</p><h2 id="heading-installing-commento">Installing Commento</h2><p>We will be using <a target="_blank" href="https://docs.docker.com/engine/install/">docker</a> and <a target="_blank" href="https://docs.docker.com/compose/install/">docker-compose</a> to install Commento. If you dont have docker and docker-compose installed, first install them and then follow these steps</p><blockquote><p><em>Commento uses <a target="_blank" href="https://www.postgresql.org/">PostgreSQL</a> as its database, for simplicity we will also use docker-compose to run postgres. In production environment you may use managed database service such as <a target="_blank" href="https://aws.amazon.com/rds/">AWS RDS</a>.</em></p></blockquote><p>Create a <code>docker-compose.yml</code> file and add the following lines</p><pre><code><span class="hljs-attribute">version</span>: <span class="hljs-string">'3'</span><span class="hljs-attribute">services</span>:  <span class="hljs-attribute">server</span>:    <span class="hljs-attribute">image</span>: registry.gitlab.com/commento/commento    <span class="hljs-attribute">ports</span>:      - <span class="hljs-number">8080</span>:<span class="hljs-number">8080</span>    <span class="hljs-attribute">environment</span>:      <span class="hljs-attribute">COMMENTO_ORIGIN</span>: <span class="hljs-attribute">https</span>:<span class="hljs-comment">//commento.example.me</span>      <span class="hljs-attribute">COMMENTO_PORT</span>: <span class="hljs-number">8080</span>      <span class="hljs-attribute">COMMENTO_POSTGRES</span>: <span class="hljs-attribute">postgres</span>:<span class="hljs-comment">//postgres:postgres@db:5432/commento?sslmode=disable      </span>    <span class="hljs-attribute">depends_on</span>:      - db  <span class="hljs-attribute">db</span>:    <span class="hljs-attribute">image</span>: postgres    <span class="hljs-attribute">environment</span>:      <span class="hljs-attribute">POSTGRES_DB</span>: commento      <span class="hljs-attribute">POSTGRES_USER</span>: postgres      <span class="hljs-attribute">POSTGRES_PASSWORD</span>: postgres    <span class="hljs-attribute">volumes</span>:      - <span class="hljs-attribute">postgres_data_volume</span>:/var/lib/postgresql/data<span class="hljs-attribute">volumes</span>:  <span class="hljs-attribute">postgres_data_volume</span>:</code></pre><p>The above configuration pulls and sets up Commento and PostgreSQL in two separate containers with a persistent database volume. Modify the values of <code>COMMENTO_ORIGIN</code>, <code>COMMENTO_PORT</code>, <code>COMMENTO_POSTGRES</code> and other environment variables as per you need.</p><p>Save the <code>docker-compose.yml</code> file somewhere and run the following command to start the services.</p><pre><code>docker<span class="hljs-operator">-</span>compose up</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618067416715/EkILUgzrT.png" alt /></p><p>This will start both services and will map commento to port <code>8080</code> of the host.</p><p>Now visit <code>https://commento.example.com</code> and you will be greeted with Commento's login page.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618067419390/2K_nDEgOH.png" alt /></p><p>At this point, you have successfully self-hosted commento. If you want to configure SMTP Server and setup OAuth move ahead.</p><h2 id="heading-configure-smtp-server">Configure SMTP Server</h2><p>Commento will use <a target="_blank" href="https://sendgrid.com/blog/what-is-an-smtp-server/">SMTP Server</a> to send mails to notify users and moderators about relevant events. To configure SMTP Server you need to provide the credentials and other information using environment variables listed below</p><pre><code><span class="hljs-attr">COMMENTO_SMTP_HOST</span>=smtp.gmail.com<span class="hljs-attr">COMMENTO_SMTP_PORT</span>=<span class="hljs-number">587</span><span class="hljs-attr">COMMENTO_SMTP_USERNAME</span>=example@gmail.com<span class="hljs-attr">COMMENTO_SMTP_PASSWORD</span>=hunter2<span class="hljs-attr">COMMENTO_SMTP_FROM_ADDRESS</span>=<span class="hljs-literal">no</span>-reply@example.com</code></pre><blockquote><p><em>All these variables should be appended in <code>docker-compose.yml</code> file under <code>environment</code> section of <code>server</code> service.</em></p></blockquote><h2 id="heading-setup-oauth">Setup OAuth</h2><p>Setting up OAuth is very easy with commento. All you need to do is provide credentials in environment variables. We will configure Google and Github OAuth.</p><h3 id="heading-google-oauth">Google OAuth</h3><p>First off obtain the <strong>API Key</strong> and <strong>Secret </strong>by creating a new project at <a target="_blank" href="https://console.developers.google.com/">Google API Console</a>. Then add those values to following environment variables.</p><pre><code><span class="hljs-attribute">COMMENTO_GOOGLE_KEY</span>=<span class="hljs-number">961031300431</span>-<span class="hljs-number">0</span>fe<span class="hljs-number">76</span>kc<span class="hljs-number">72</span>xvo<span class="hljs-number">0</span>otts<span class="hljs-number">78</span>ug<span class="hljs-number">2</span>aqmi<span class="hljs-number">4</span>is<span class="hljs-number">067</span>.apps.googleusercontent.com COMMENTO_GOOGLE_SECRET=XmaKz<span class="hljs-number">7</span>gRkWw<span class="hljs-number">3</span>MQgoAHmApuwb</code></pre><p>While creating a project at Google API Console you will be asked for Callback URL, So replace <code>example.com</code> with your domain name and provide this URL <code>[https://commento.example.com/api/oauth/google/callback</code>](https://commento.example.com/api/oauth/google/callback)</p><h3 id="heading-github-oauth">Github OAuth</h3><p>Create a new OAuth app in <a target="_blank" href="https://github.com/settings/developers">GitHub developer settings</a> to generate a set of credentials.</p><pre><code><span class="hljs-attribute">COMMENTO_GITHUB_KEY</span>=uk<span class="hljs-number">3</span>juvzyyejgxhbym<span class="hljs-number">1</span>sqkn<span class="hljs-number">3</span>t COMMENTO_GITHUB_SECRET=<span class="hljs-number">2</span>fbdc<span class="hljs-number">6</span>bdbb<span class="hljs-number">7</span>c<span class="hljs-number">02119</span>fd<span class="hljs-number">8</span>fa<span class="hljs-number">70</span>b<span class="hljs-number">7</span>bdcfa<span class="hljs-number">7</span>af<span class="hljs-number">8</span>e<span class="hljs-number">2</span>c</code></pre><p>Callback URL: <code>[https://commento.example.com/api/oauth/github/callback</code>](https://commento.example.com/api/oauth/github/callback)</p><h2 id="heading-conclusion">Conclusion</h2><p>Congratulations, you have successfully deployed Commento with SSL. We have also configured SMTP Server and OAuth. If you want to dive deeper in commento, you can refer the <a target="_blank" href="https://docs.commento.io/">documentation</a></p>]]></description><link>https://blog.pratikd.in/self-host-commento-using-nginx-and-docker-8fb65bbba6f5</link><guid isPermaLink="true">https://blog.pratikd.in/self-host-commento-using-nginx-and-docker-8fb65bbba6f5</guid><dc:creator><![CDATA[Pratik Daigavane]]></dc:creator><pubDate>Sun, 09 Aug 2020 08:01:00 GMT</pubDate><cover_image>https://cdn.hashnode.com/res/hashnode/image/upload/v1618067421021/TdEckctQZ.png</cover_image></item></channel></rss>