<?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: Ritikesh</title>
    <description>The latest articles on DEV Community by Ritikesh (@ritikesh).</description>
    <link>https://dev.to/ritikesh</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%2F67542%2F3ac680d0-be01-4029-9d33-020e22bfb579.jpg</url>
      <title>DEV Community: Ritikesh</title>
      <link>https://dev.to/ritikesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ritikesh"/>
    <language>en</language>
    <item>
      <title>Dynamic JWT authentication and secrets rotation in Rails Applications</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Sun, 27 Feb 2022 13:17:03 +0000</pubDate>
      <link>https://dev.to/ritikesh/dynamic-jwt-authentication-and-secrets-rotation-in-rails-applications-4pe9</link>
      <guid>https://dev.to/ritikesh/dynamic-jwt-authentication-and-secrets-rotation-in-rails-applications-4pe9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jwt.io"&gt;JWT&lt;/a&gt; is one of the most popular authentication &amp;amp; authorization techniques employed in modern applications. There are several articles and guides available on how to get started with JWT in any application or framework. In this post however, we will talk about some lesser talked about tricks of JWT, specifically in the context of large applications.&lt;/p&gt;

&lt;p&gt;Generally speaking, the larger the application, the more internal and external services it has to talk to. External services usually have their own way of authenticating and authorizing third party API calls. With internal systems however, organisations prefer to use JWT tokens because of their inherent flexibility and versatility. A sample JWT based handshake between 2 rails applications using &lt;a href="https://github.com/jwt/ruby-jwt"&gt;ruby-jwt&lt;/a&gt; would look like this -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# caller&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"auth_service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"main_application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"update_user"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# most common attributes, but not limited to these.&lt;/span&gt;

&lt;span class="c1"&gt;#defaults to HMAC&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jwt_secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main_application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# make API call to main_application with token&lt;/span&gt;

&lt;span class="c1"&gt;# callee&lt;/span&gt;
&lt;span class="c1"&gt;# common auth service&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jwt_secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auth_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# use payload to authorize the request resources being accessed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic JWT authentication
&lt;/h2&gt;

&lt;p&gt;The above is a simplified take of how one would authorize API requests. However, as mentioned earlier, larger applications generally talk to a lot of services. To avoid service-to-service dependencies and to maintain a healthy security posture, it is advisable to have unique secrets for each service the application talks to. To support authorizing the ever growing list of services, the authentication logic needs to be implicitly generic.&lt;/p&gt;

&lt;p&gt;This is where the flexibility of JWT tokens come into the picture. JWT payloads usually advise carrying an &lt;code&gt;issuer&lt;/code&gt; attribute, which points to the original issuer of the token. In this case, our third party services. We can use this attribute from the payload to identify which service has issued this token when making the API request.&lt;/p&gt;

&lt;p&gt;Coming from a traditional authentication system like encryption or hashing, one might argue - To identify the issuer from the payload, I would first need to decode the  payload for which I need the secret. But to get the secret, I need to know the issuer from the payload. DEADLOCK. &lt;/p&gt;

&lt;p&gt;This is where the versatility of JWT comes to the fore. One does not need the secret to decode a JWT payload. In fact, you can decode any JWT token on &lt;a href="https://jwt.io"&gt;JWT's website&lt;/a&gt; without ever needing the secret. However, it is advisable to ALWAYS verify the claims.&lt;/p&gt;

&lt;p&gt;Coming back to our requirement of identifying the service using the JWT payload, the &lt;a href="https://github.com/jwt/ruby-jwt#finding-a-key"&gt;ruby-jwt&lt;/a&gt; gem allows a block to be passed to the decode method, allowing access to the original payload. The return value of the block would then be used to verify the claim. Our earlier example can now be tweaked slightly to make it generic -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# caller logic does not change&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"auth_service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"main_application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"update_user"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# most common attributes, but not limited to these.&lt;/span&gt;

&lt;span class="c1"&gt;#defaults to HMAC&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jwt_secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main_application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# make API call to main_application with token&lt;/span&gt;

&lt;span class="c1"&gt;# callee&lt;/span&gt;
&lt;span class="c1"&gt;# common auth service&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jwt_secrets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'iss'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# use payload to authorize the request resources being accessed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rotating JWT secrets
&lt;/h2&gt;

&lt;p&gt;Securing applications is of the utmost importance in today's digital first world. Inspite of all the preventive measures organisations take, there is never a zero vulnerability guarantee. In such an environment, teams must always be prepared to respond to security incidents.&lt;/p&gt;

&lt;p&gt;In the event of security incidents involving secret/data leaks, the leaked tokens/secrets are first rotated to ensure bad actors are not able to fully leverage the exploit. A common problem with rotating secrets in large applications is that it is very hard to coordinate the changes across multiple systems at the same time.&lt;/p&gt;

&lt;p&gt;This can also be solved natively with ruby-jwt (this was &lt;a href="https://github.com/jwt/ruby-jwt/commit/c4aa448e78cfa2005796bbe6b5e060a7546fedc3"&gt;recently added to the library&lt;/a&gt;), which allows verifying claims against multiple secrets if the &lt;code&gt;finding-a-key&lt;/code&gt; block returns an Array. For the first deployment - the application would need to maintain the old and the new secrets in the secrets hash against the issuer. Sample below -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# multiple secrets are supported for each issuer&lt;/span&gt;
&lt;span class="n"&gt;secrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'auth_service'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'old_secret'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'new_secret'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'iss'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the downstream services completely switch to the new secret, the application can remove support for the older secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# clean up the older secret&lt;/span&gt;
&lt;span class="n"&gt;secrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'auth_service'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new_secret'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'iss'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows rotating secrets easily in production without impacting live systems.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>security</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Design a multitenant application on Rails 6 with horizontal sharding</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Fri, 25 Dec 2020 08:44:47 +0000</pubDate>
      <link>https://dev.to/ritikesh/design-a-multitenant-application-on-rails-6-with-horizontal-sharding-1eg2</link>
      <guid>https://dev.to/ritikesh/design-a-multitenant-application-on-rails-6-with-horizontal-sharding-1eg2</guid>
      <description>&lt;p&gt;One of the most common design patterns for multitenant architectures is to associate every tenant with a unique subdomain on your root domain. For eg. if your application runs on example.com, marvel as a tenant would access the system using marvel.example.com and so on. &lt;/p&gt;

&lt;p&gt;This pattern has its own advantages(easy/faster DNS resolution when running on a multi pod  setup) and disadvantages(DNS updates for every tenant creation). Instead of debating that, we will delve into how to implement this architecture in a Rails application &lt;a href="https://dev.to/ritikesh/multitenant-architecture-on-rails-6-1-27c7"&gt;using the new multi &amp;amp; horizontal DB setup provided by Rails 6.0/6.1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To begin with, we will need a &lt;code&gt;Tenant&lt;/code&gt; model. Since your tenants will be identified by subdomains, it makes sense to have a subdomain column in the table along with other application required attributes. Each tenant belongs to a &lt;code&gt;Shard&lt;/code&gt; and all data of that tenant would reside on that shard. So we will need a shard model as well.&lt;/p&gt;

&lt;p&gt;We can begin by setting up the required database configurations first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/database.yml&lt;/span&gt;

&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlite3&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;

&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary_db&lt;/span&gt;
  &lt;span class="na"&gt;default_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary_db_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;shard1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shard1_db&lt;/span&gt;
  &lt;span class="na"&gt;shard1_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shard1_db_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will define the required models as well accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/application_record.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="n"&gt;db_configs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;database_configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;

  &lt;span class="n"&gt;db_configs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_object&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# key = default, db_key = default&lt;/span&gt;
    &lt;span class="c1"&gt;# key = default_replica, db_key = default&lt;/span&gt;
    &lt;span class="n"&gt;db_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'_replica'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eql?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="ss"&gt;:writing&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:reading&lt;/span&gt;

    &lt;span class="n"&gt;db_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;
    &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;db_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;db_key&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# connects_to shards: {&lt;/span&gt;
  &lt;span class="c1"&gt;#   default: { writing: :default, reading: :default_replica },&lt;/span&gt;
  &lt;span class="c1"&gt;#   shard1: { writing: :shard1, reading: :shard1_replica }&lt;/span&gt;
  &lt;span class="c1"&gt;# }&lt;/span&gt;
  &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;shards: &lt;/span&gt;&lt;span class="n"&gt;db_configs&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/global_record.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GlobalRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :default_replica&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/tenant.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tenant&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActsAsCurrent&lt;/span&gt;

  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:subdomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="no"&gt;DOMAIN_REGEX&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# other DSL&lt;/span&gt;

  &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:set_shard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :create&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_shard&lt;/span&gt;
    &lt;span class="no"&gt;Shard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;tenant_id: &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;domain: &lt;/span&gt;&lt;span class="n"&gt;subdomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/shard.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Shard&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;GlobalRecord&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActsAsCurrent&lt;/span&gt;

  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="no"&gt;DOMAIN_REGEX&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:tenant_id&lt;/span&gt;

  &lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="ss"&gt;:set_current_shard&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_current_shard&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;APP_CONFIGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:current_shard&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;#shard1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With multitenant architectures, there will always be a global context and a tenant specific context. We isolate such models through abstract classes &lt;code&gt;ApplicationRecord&lt;/code&gt; and &lt;code&gt;GlobalRecord&lt;/code&gt;. They also take care of abstracting database connections and setting up the required isolations.&lt;/p&gt;

&lt;p&gt;We can also leverage the &lt;a href="https://dev.to/ritikesh/simple-multitenancy-inrails-42b6"&gt;&lt;code&gt;BelongsToTenant&lt;/code&gt; pattern&lt;/a&gt; for all models that belong to a tenant and inherit from &lt;code&gt;ApplicationRecord&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/blob/60d5928a27c5db850f2b258a1484bf5ad7d1d302/activerecord/lib/active_record/core.rb#L235"&gt;All &lt;code&gt;ActiveRecord&lt;/code&gt; inherited models connect by default to a &lt;code&gt;default&lt;/code&gt; shard and a &lt;code&gt;writing&lt;/code&gt; role&lt;/a&gt; unless &lt;code&gt;connected_to&lt;/code&gt; another connection. Hence, when connecting to &lt;code&gt;GlobalRecord&lt;/code&gt; inherited models, we will not require any explicit connection handling. &lt;/p&gt;

&lt;p&gt;We can also define a proxy class to abstract out all application specific connection handling logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/proxies/database_proxy.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseProxy&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_shard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;_connect_to_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: :writing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="n"&gt;shard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_replica&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;_connect_to_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: :reading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="n"&gt;shard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_global_replica&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;_connect_to_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;klass: &lt;/span&gt;&lt;span class="no"&gt;GlobalRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;role: :reading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# for regular executions, since Global only connects to default shard,&lt;/span&gt;
    &lt;span class="c1"&gt;# no explicit connection switching is required.&lt;/span&gt;
    &lt;span class="c1"&gt;# def on_global(&amp;amp;block)&lt;/span&gt;
    &lt;span class="c1"&gt;#   _connect_to_(klass: GlobalRecord, role: :writing, &amp;amp;block)&lt;/span&gt;
    &lt;span class="c1"&gt;# end&lt;/span&gt;

    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_connect_to_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;klass: &lt;/span&gt;&lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;role: :writing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;shard: :default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connected_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="n"&gt;shard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup in place, we can now write both application and background middlewares that handle shard selection and tenant isolation on a per request or job basis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/middlewares/multitenancy.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Middlewares&lt;/span&gt;
  &lt;span class="c1"&gt;# selecting account based on subdomain&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Multitenancy&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_HOST'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="n"&gt;shard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Shard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;domain: &lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;shard&lt;/span&gt;

      &lt;span class="n"&gt;shard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_current&lt;/span&gt;
      &lt;span class="no"&gt;DatabaseProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_shard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="n"&gt;shard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;subdomain: &lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_current&lt;/span&gt;
        &lt;span class="vi"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'lib/middlewares/multitenancy'&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_after&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Middlewares&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Multitenancy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anybody who's building new products on the web, Ruby on Rails has never been better to kickstart your next big unicorn.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>programming</category>
      <category>webdev</category>
      <category>database</category>
    </item>
    <item>
      <title>Multitenant Architecture on Rails 6.1</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Thu, 24 Dec 2020 04:01:28 +0000</pubDate>
      <link>https://dev.to/ritikesh/multitenant-architecture-on-rails-6-1-27c7</link>
      <guid>https://dev.to/ritikesh/multitenant-architecture-on-rails-6-1-27c7</guid>
      <description>&lt;p&gt;Rails, the framework built on top of Ruby, just got its latest version(6.1) released. A lot of features and enhancements have gone into the latest version of Rails. You can read the &lt;a href="https://weblog.rubyonrails.org/2020/12/9/Rails-6-1-0-release/"&gt;official announcement&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;I will be focusing particularly on the &lt;a href="https://weblog.rubyonrails.org/2020/12/9/Rails-6-1-0-release/#multi-db-improvements"&gt;&lt;strong&gt;Multi-DB improvements&lt;/strong&gt;&lt;/a&gt; section, what changed and how we can leverage Rails' native multi DB handling techniques for building scalable multitenant applications. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://weblog.rubyonrails.org/2019/8/15/Rails-6-0-final-release"&gt;Rails 6.0&lt;/a&gt; was the first official rails version to support multiple databases. From the release notes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The new multiple database support makes it easy for a single application to connect to, well, multiple databases at the same time! You can either do this because you want to segment certain records into their own databases for scaling or isolation, or because you’re doing read/write splitting with replica databases for performance. Either way, there’s a new, simple API for making that happen without reaching inside the bowels of Active Record. The foundational work for multiple-database support was done by Eileen Uchitelle and Aaron Patterson.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This allowed application developers to be able to define multiple database connections for a single application. Before this, developers had to use one of the many third party gems for any kind of multi DB support in Rails. Even though the ruby/rails community is very vibrant, third party gems often come with maintenance overheads with respect to upgrades, breaking changes, bugs, performance issues, etc.&lt;/p&gt;

&lt;p&gt;With Rails 6.0, you could define your database.yml in such a way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/database.yml&lt;/span&gt;

&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlite3&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;

&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary_db&lt;/span&gt;
  &lt;span class="na"&gt;primary_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary_db_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;animals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;animals_db&lt;/span&gt;
  &lt;span class="na"&gt;animals_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;animals_db_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then define &lt;code&gt;ActiveRecord&lt;/code&gt; &lt;code&gt;Abstract&lt;/code&gt; classes that could connect to these databases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/application_record.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :primary_replica&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/animals_base.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnimalsBase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :animals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :animals_replica&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/user.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The abstract classes and models inheriting from them would both now have access to the &lt;code&gt;connected_to&lt;/code&gt; method which can be used to establish connection to the configured database connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# some_controller.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connected_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: :reading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do_something_thats_slow&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach worked great for primary-replica setup or setups where models had clear separation. i.e. a model always queried from a single database. However, with modern multi-tenant SaaS applications, horizontal sharding is almost a basic necessity. Depending on the tenant that's accessing the application, the application should be able to select which database it wants to query the data from. While how the application shards horizontally is DSL and can vary from a case to case basis, how it is able to connect to the underlying databases should be something that the framework should be able to handle. And so they did.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://weblog.rubyonrails.org/2020/12/9/Rails-6-1-0-release/#multi-db-improvements"&gt;&lt;strong&gt;Multi-DB improvements&lt;/strong&gt;&lt;/a&gt; released in 6.1, you can now define shard connections for your abstract classes as well. The example from above changes as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/database.yml&lt;/span&gt;

&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlite3&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;

&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary_db&lt;/span&gt;
  &lt;span class="na"&gt;primary_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary_db_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;animals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;animals_db&lt;/span&gt;
  &lt;span class="na"&gt;animals_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;animals_db_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;animals_shard1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;animals_db1&lt;/span&gt;
  &lt;span class="na"&gt;animals_shard1_replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;animals_db1_replica&lt;/span&gt;
    &lt;span class="na"&gt;replica&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/application_record.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :primary_replica&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/animals_base.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnimalsBase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;shards: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :animals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :animals_replica&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;shard1: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :animals_shard1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :animals_shard1_replica&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/cat.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;AnimalsBase&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to 6.0, we can then leverage the &lt;code&gt;connected_to&lt;/code&gt; method for switching(/establishing) connections to the configured databases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# some_controller.rb&lt;/span&gt;

&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;
&lt;span class="no"&gt;AnimalsBase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connected_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;shard: :shard1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;role: :reading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="c1"&gt;# reads all cats from animals_shard1_replica&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Native multi DB connection switching and handling would go a long way in helping developers move away from a lot of complex 3rd party gems in favor of out-of-the-box tools. I have already started leveraging this in one of my applications. I will be sharing more on how to build an effective sharding / connection switching strategy on top of what's natively available with Rails in my next post. Thanks for reading and happy holidays!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>programming</category>
      <category>webdev</category>
      <category>database</category>
    </item>
    <item>
      <title>Distributed request tracing in Rails</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Mon, 07 Sep 2020 17:31:23 +0000</pubDate>
      <link>https://dev.to/ritikesh/distributed-request-tracing-in-rails-1g6a</link>
      <guid>https://dev.to/ritikesh/distributed-request-tracing-in-rails-1g6a</guid>
      <description>&lt;p&gt;The &lt;a href="https://microservices.io/patterns/microservices.html"&gt;microservices&lt;/a&gt; pattern is a highly debated topic. The pros and cons are heatedly discussed over forums, blogs, podcasts, social media, and literally everywhere else. We'll skip that argument for another day. Let's dive into how we can enable better request tracing in a microservices architecture in a pure Ruby on Rails world. Distributed tracing / debugging is one of the biggest challenges in a microservice architecture.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://doc.scalingo.com/platform/app/x-request-id"&gt;X-Request-ID&lt;/a&gt; is a standard HTTP header. The header, as defined in the blog post, is :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A unique request ID, represented by a UUID, is generated at each HTTP request received by the platform routing servers. It is added to the request which is passed to your application containers.&lt;br&gt;
If the X-Request-ID header is already defined by the client, it won’t be overridden except if it doesn’t respect the following format:&lt;br&gt;
20-128 alphanumerical characters and the symbols +, =, / and -.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key point to focus here is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the X-Request-ID header is already defined by the client, it won’t be overridden&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will use the same header to our advantage when making calls to all our external microservices.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/rails/rails/blob/451c3c0a1b1fedd2611278522435f01be4172ef3/actionpack/lib/action_dispatch/http/request.rb#L307"&gt;&lt;code&gt;ActionDispatch::Request&lt;/code&gt;&lt;/a&gt; module in rails makes the &lt;code&gt;uuid&lt;/code&gt; method available on the &lt;code&gt;request&lt;/code&gt; object. We can use this in our controllers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_thread_data&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_thread_data&lt;/span&gt;
    &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then leverage this &lt;code&gt;Thread&lt;/code&gt; context from the Proxy classes making requests to our microservices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceProxy&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:handler&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;
    &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;
    &lt;span class="vi"&gt;@method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt;
    &lt;span class="vi"&gt;@url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
    &lt;span class="vi"&gt;@handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_request&lt;/span&gt;
    &lt;span class="n"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;method: &lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;payload: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="ss"&gt;headers: &lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;read_timeout: &lt;/span&gt;&lt;span class="no"&gt;CircuitConstants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:read_timeout&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;open_timeout: &lt;/span&gt;&lt;span class="no"&gt;CircuitConstants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:open_timeout&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;circuit&lt;/span&gt;
    &lt;span class="no"&gt;Circuitbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CircuitConstants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;headers&lt;/span&gt;
    &lt;span class="vi"&gt;@headers_with_request_id&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@headers&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'X-Request-Id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="vi"&gt;@headers&lt;/span&gt;  
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All modern web frameworks will respect this header and use it to set the request level UUID. In Rails, this is handled by the &lt;a href="https://api.rubyonrails.org/classes/ActionDispatch/RequestId.html"&gt;&lt;code&gt;ActionDispatch::RequestId&lt;/code&gt;&lt;/a&gt; middleware.&lt;/p&gt;

&lt;p&gt;We should also set the application level tagged logging to make use of these request uuids:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After implementing the above, logs will be tagged to the request uuid and will start looking like the log snippet below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mgx_oFAY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/upiox3phqv87blqi31v6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mgx_oFAY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/upiox3phqv87blqi31v6.png" alt="logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the above setup, all requests flowing through all the microservices will have the same &lt;code&gt;request-id&lt;/code&gt; set, enabling easy request tracing and in-turn, all application issues, easily debuggable.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Serving private content from S3 using CloudFront</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Thu, 03 Sep 2020 12:52:13 +0000</pubDate>
      <link>https://dev.to/ritikesh/serving-private-content-from-s3-using-cloudfront-de2</link>
      <guid>https://dev.to/ritikesh/serving-private-content-from-s3-using-cloudfront-de2</guid>
      <description>&lt;p&gt;Freshworks’ IT service management tool, Freshservice, enables organizations to simplify their IT operations. Freshservice provides &lt;a href="https://freshservice.com/solutions/it"&gt;ITIL-ready&lt;/a&gt; components that help administrators manage incidents, problems, changes and releases, and the asset management component helps organizations exercise control over their IT assets.&lt;/p&gt;

&lt;p&gt;What happens when your application’s core S3 bucket is marked as dangerous by &lt;a href="https://safebrowsing.google.com/"&gt;Google’s safe browsing&lt;/a&gt; feature and your web application is displayed as suspicious when accessed on Chrome/Firefox? We will talk about how we reacted to a similar incident that happened with us, and how we reduced the impact it had on our customers. We will also delve into design changes made to further reduce the impact of such events in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Attachment Processing
&lt;/h3&gt;

&lt;p&gt;Freshservice is powered by Ruby on Rails. &lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt; &lt;a href="https://www.phusionpassenger.com/"&gt;passenger-backed&lt;/a&gt; servers are hosted on the EC2 instances of AWS. The servers are hosted in four AWS data centers — US East (US), Europe Central (EUC), India (IND), and Australia (AU).&lt;/p&gt;

&lt;p&gt;Freshservice uses AWS’s S3 (Simple Storage Service) for file storage. S3 + &lt;a href="https://github.com/thoughtbot/paperclip"&gt;paperclip&lt;/a&gt; forms the crux of our attachment storage and processing engine. Customers use this service for various purposes like uploading their logo or favicon for branding, enabling their end users upload avatars, attaching supporting files or images on tickets, uploading the signed contract in the contracts module, etc. Upon upload, the files are scanned for viruses and uploaded to a unique path in our S3 bucket. To ensure our customers’ data is never lost, we also have a DR (Disaster Recovery) setup in place**. The attachments bucket is replicated in near real-time to another bucket in a different region within the same geography.&lt;/p&gt;

&lt;p&gt;Attachments are protected by a set of standard security measures. The first check, for example, is tenancy — customers can only access their own attachments. Attachments are always fetched using pre-signed URLs that expire within a short interval, typically 5 minutes, but this interval may vary on a per use-case basis. The URLs generated are of the format:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://s3.amazonaws.com/bucket_name/path_to_file.extension?signature"&gt;https://s3.amazonaws.com/bucket_name/path_to_file.extension?signature&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EB-6b2m1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083522/CloudFront1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EB-6b2m1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083522/CloudFront1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;AU and IND regions currently do not have a secondary region for us to have a DR setup. US and EUC have a DR setup in place.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Unexpected service outage
&lt;/h3&gt;

&lt;p&gt;To ensure secure browsing for its users on the internet, Google safe browsing scans websites for dangerous &amp;amp; deceptive content. When a site is found to be in breach of its guidelines, they are marked unsafe by Google Safe Browsing. A security warning is then displayed on browsers when a user tries to visit the site in question.&lt;/p&gt;

&lt;p&gt;Sometime in the middle of January 2020, Google Safe Browsing decided to mark our attachments bucket’s root URL ( &lt;strong&gt;&lt;a href="https://s3.amazonaws.com/bucket_name"&gt;https://s3.amazonaws.com/bucket_name&lt;/a&gt;&lt;/strong&gt; ) as unsafe. This rendered the web application unusable as most pages included some form of attachment on the page. This affected all our customers hosted in that region.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ZEEr9Em--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083523/Cloudfront2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ZEEr9Em--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083523/Cloudfront2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  First Response
&lt;/h3&gt;

&lt;p&gt;As a first line of defense, to ensure customers are able to login into the system and use it, we temporarily disabled attachment-backed branding like logo and favicon. We also disabled loading user avatars to ensure customers are able to view and process at least the tickets that did not carry any attachments. Unfortunately, a vast majority of tickets had attachments and there was no easy way to support them.&lt;/p&gt;

&lt;p&gt;We considered creating a new bucket where all new attachments would be uploaded while syncing older attachment data into this new bucket using &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html"&gt;S3 sync&lt;/a&gt;. This turned out to be a non-starter due to the size of our bucket, as the sync would end up taking days if not weeks. This would be unacceptable for us as our customers would be unable to support their end users without essential information like screen shares or contract documents while the sync completes. We therefore decided to use our DR bucket which was already in sync with the original bucket. The challenge was that the DR bucket was hosted in a different region and accessing it across two AWS regions would introduce an additional network latency overhead to both read and write operations. We were, therefore, uncomfortable going all in with this as our new bucket. Instead, we decided to move the reads alone to the DR bucket, temporarily, until Google addressed the blocking of our bucket caused by false positives in their system.&lt;/p&gt;

&lt;p&gt;We quickly prepared code changes to start reading from the DR bucket and deployed them. We also reverted all the previously made temporary changes related to customer branding and user avatars. The application was now fully in use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0ygHJk_Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083524/Cloudfront3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0ygHJk_Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083524/Cloudfront3.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After multiple attempts in reaching out to Google to sort the problem from their end, we finally had some luck and were able to get them to mark the bucket URLs as safe. They even guaranteed that this wouldn’t happen in the future.&lt;/p&gt;

&lt;p&gt;But we were skeptical enough to decide we did not want to rely on this guarantee. We decided to keep the fallback available in production so we could easily switch back if we were blocked inadvertently again. To achieve this, we moved the "reads-from-dr" logic under a flag stored in &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt;. To avoid hitting Redis for each request, we wrapped this around &lt;a href="https://www.freshworks.com/optimizing-cache-with-memoizeuntil-blog/"&gt;MemoizeUntil&lt;/a&gt; with 1-minute refreshes. This would make us future-proof if such an event reoccurred. All we would need to do is to flip the flag in Redis to fall back to reading from the DR bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VATjkz44--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083525/Cloudfront4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VATjkz44--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083525/Cloudfront4.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Second incident
&lt;/h3&gt;

&lt;p&gt;Cut forward to the middle of April 2020 and Google marked the bucket as insecure once again. This time though, we could control the impact duration by toggling the flag in redis and customers were able to access their portal shortly after the problem was detected. We also realised that although this solution worked, there were a few issues, notably:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We support both inline and regular attachments. Regular attachments can afford to have delays in loading as they are not expected to appear immediately. But inline attachments have to appear realtime. The "read-from-dr" solution that we had was "near-real time", not real time. Customers on faster networks would have noticed broken image uploads when, actually, the upload was successful in the backend. This is because the time taken to process and respond to the upload request would be shorter than the S3 sync. Hence the image would not be found when read from the DR bucket right after it was uploaded.&lt;/li&gt;
&lt;li&gt;If there happened to be a delay in S3 sync during this period, we would be caught completely off guard and wouldn’t have an alternative solution in place. This would make us too dependent on AWS than we would like to.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Final Solution
&lt;/h3&gt;

&lt;p&gt;At first, we considered building a reverse proxy solution as a gateway barrier for our S3 accesses. This proxy would be responsible for maintaining all our secure access processes while also reducing the blast radius in case of future incidents. We wanted to host our proxy under attachments.freshservice.com. With our sharded multi-tenant architecture, which serves each tenant under a unique subdomain on our root freshservice.com domain, this isolation would be rather straightforward. Each tenant would have its own subdomain, similar to their existing freshservice subdomain. for attachment processing under the root proxy URL — attachment.freshservice.com&lt;/p&gt;

&lt;p&gt;This approach would solve all our problems but add another piece of infrastructure requiring additional provisioning, maintenance and monitoring. We were reluctant to add more infrastructure overhead for this problem and were looking for a readily available service that would solve this for us. That’s when we came across AWS Cloudfront’s &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html"&gt;signed private access feature&lt;/a&gt;. Secure access of attachments through pre-signed auto-expiring URLs was a core design principle in our current setup, giving security the highest priority. The signed private access feature from AWS Cloudfront allowed us to retain that design principle while giving us the leisure of not having to maintain another infrastructure component. We were immediately convinced and decided to go ahead with Cloudfront + S3 as our attachments service provider. We used &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html"&gt;trusted signers&lt;/a&gt; as our default signing strategy for generating signed private URLs. The new URL format for CDN enabled accesses would look like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://subdomain.attachments.freshservice.com/path_to_file.extension?signature"&gt;https://subdomain.attachments.freshservice.com/path_to_file.extension?signature&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, with data localisation clauses from different governments, we could not leverage the default features of a CDN like cloudfront. Hence, we had to disable both default and edge caching on the distribution so that the customer’s data would reside in the same region as that of the origin. We can do this by setting minimum, maximum and default TTL to 0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KjWkWJid--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083526/cloudfront5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KjWkWJid--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083526/cloudfront5.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This would, however, lead to exorbitant CDN access costs. Moreover, we also noticed that during both the aforementioned incidents, there were some suspicious sign ups misusing freshservice for spamming. Google never gave us a valid reason for marking the site as unsafe in both the cases. They just mentioned that this would not happen in the future and that their algorithms were learning and getting better.&lt;/p&gt;

&lt;p&gt;Hence, as a caution and to keep costs in check, we came up with a unique strategy to enable the "CDN+S3" approach only for suspicious-looking accounts while keeping the remaining accounts on the default S3 accesses. An account is deemed suspicious depending on various anti-spam measures that we have. Depending on the spam score of an account, its attachments will be served from either S3 directly or through CDN + S3. This is internally controlled via &lt;a href="https://www.martinfowler.com/articles/feature-toggles.html"&gt;feature flags&lt;/a&gt;. We also kept default switches to both disable or enable CDN accesses at application level as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zlt-Arae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083527/cloudfront6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zlt-Arae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/09/03083527/cloudfront6.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This would enable us to control the blast radius to each tenant and ensure we are able to serve our customers without any service interruptions.&lt;/p&gt;

&lt;p&gt;Incidents like these, unavoidable to a certain extent, help us display our truly customer-first values that are ingrained in every individual within the organisation. After ensuring that the customers could resume their operations, we did what we do best — make informed engineering decisions to ensure that our customers do not get impacted again in future.&lt;/p&gt;

&lt;p&gt;(This post was co-authored by &lt;a href="https://in.linkedin.com/in/valarpirai"&gt;Valarpiraichandran A&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>rails</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Four Action Mailer features you should know about</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Thu, 13 Aug 2020 08:30:43 +0000</pubDate>
      <link>https://dev.to/ritikesh/four-action-mailer-features-you-should-know-about-30fa</link>
      <guid>https://dev.to/ritikesh/four-action-mailer-features-you-should-know-about-30fa</guid>
      <description>&lt;p&gt;&lt;em&gt;ActionMailer is the default email library that comes with Rails. It has a ton of hidden features that aren’t spoken about or discussed as much as some of its counterparts like ActiveRecord or ActiveSupport. In this article, we will cover some of those features, and understand how to scale and debug email sending better with ActionMailer.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Interceptors
&lt;/h3&gt;

&lt;p&gt;According to the &lt;a href="https://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails"&gt;official Rails documentation &lt;/a&gt;, "Interceptors allow you to make modifications to emails before they are handed off to the delivery agents. An interceptor class must implement the &lt;code&gt;:delivering_email(message)&lt;/code&gt; method, which will be called before the email is sent."&lt;/p&gt;

&lt;p&gt;This can be a very powerful hook to help you extract some generic processes or rule out of your mailer or notifier classes. We leverage interceptors in Freshservice to be able to set default mailboxes and the &lt;code&gt;From&lt;/code&gt; address that our customers intended to use for sending out emails to their recipients. The following code snippet demonstrates how you can leverage Interceptors to achieve the same:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;set_smtp_settings&lt;/code&gt; method retrieves the current tenant's configured mailbox from the thread and assigns it to the mail’s &lt;code&gt;smtp_settings&lt;/code&gt; attribute. The mail class internally uses the ‘smtp_settings’ attribute to connect to the mailbox for delivering the email.&lt;/p&gt;

&lt;p&gt;This method also takes care of setting all product-specific custom headers required for product functionalities and for the platform scaling. To avoid IP reputation abuse through spamming and a potential service disruption to our premium customers, depending on the state of the current tenant, we also append headers to ensure that the right IP is selected when delivering the corresponding emails.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fix_encodings&lt;/code&gt; method, as the name suggests, fixes any encoding issues that arise out of unsupported user entered data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observers
&lt;/h3&gt;

&lt;p&gt;Right back from the &lt;a href="https://guides.rubyonrails.org/action_mailer_basics.html#observing-emails"&gt;official Rails documentation &lt;/a&gt;, “Observers give you access to the email message after it has been sent. An observer class must implement the &lt;code&gt;:delivered_email(message)&lt;/code&gt; method, which will be called after the email is sent.” Like interceptors, observers offer hooks that can be leveraged for generic processes.&lt;/p&gt;

&lt;p&gt;For email debugging purposes, we wanted to print an email’s &lt;code&gt;message-id&lt;/code&gt; header tagged along with the default &lt;code&gt;ActionMailer&lt;/code&gt; log that prints &lt;code&gt;Sent email to #{recipients_list}&lt;/code&gt;. This log is printed by the default &lt;code&gt;LogSubscriber&lt;/code&gt; from &lt;code&gt;ActionMailer&lt;/code&gt;. But this &lt;code&gt;LogSubscriber&lt;/code&gt; did not have access to the mail object. Hence we couldn’t leverage the &lt;code&gt;LogSubscriber&lt;/code&gt; for this.&lt;/p&gt;

&lt;p&gt;We then decided to use Observers to achieve this. We also had to override the default LogSubscriber’s deliver method to avoid duplicate &lt;code&gt;Sent email to #{recipients_list}&lt;/code&gt; log messages.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We can also log other headers from the mail object for analytics or logging purposes.&lt;/p&gt;

&lt;p&gt;Alternatively, this can be achieved through &lt;code&gt;ActiveSupport::Notification&lt;/code&gt; as well by subscribing to the &lt;code&gt;deliver.action_mailer&lt;/code&gt; from &lt;code&gt;ActionMailer&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Perform Deliveries (or not)
&lt;/h3&gt;

&lt;p&gt;According to the &lt;a href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration"&gt;Official Rails documentation &lt;/a&gt;, perform deliveries determine "whether deliveries are actually carried out when the deliver method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing. If this value is false, deliveries array will not be populated even if the &lt;code&gt;delivery_method&lt;/code&gt; is &lt;code&gt;:test&lt;/code&gt;."&lt;/p&gt;

&lt;p&gt;This option is useful for test environments or your CI pipelines to avoid consuming your email quotas provided by email service providers. This can also be leveraged in development mode to avoid spamming your mailbox with too many emails when developing or testing a feature. The mail content is printed out by default on &lt;code&gt;STDOUT&lt;/code&gt;. You can even make this configurable using a thread variable or file in tmp directory. Another use case for setting this to false dynamically is to avoid sending spam emails depending on the tenant that's sending the email or the content of the email.&lt;/p&gt;

&lt;p&gt;Here's how you do this using the interceptor method defined above:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Setting this at mail level ensures that other emails are not impacted. This is achievable through ActionMailer callbacks as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Callbacks — Have better control over your emails
&lt;/h3&gt;

&lt;p&gt;With Rails 4.0, ActionMailer was introduced to ActiveSupport callback hooks &lt;code&gt;before_action&lt;/code&gt; similar to ActionController. According to the &lt;a href="https://github.com/rails/rails/blob/4-0-stable/actionmailer/CHANGELOG.md#rails-400-june-25-2013"&gt;changelog&lt;/a&gt;: &lt;em&gt;Allow callbacks to be defined in mailers similar to ActionController::Base. You can configure default settings, headers, attachments, delivery settings or change delivery using before_filter, after_filter, etc. Justin S. Leitgeb&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This was a great addition to the framework as it allowed for great possibilities. Some of the items discussed in the previous sections are easily achievable via callbacks.&lt;/p&gt;

&lt;p&gt;Freshservice is a cloud based SaaS product that implements a &lt;a href="https://dev.to/ritikesh/simple-multitenancy-inrails-42b6"&gt;multitenant&lt;/a&gt; architecture at its core. As a product, we take pride in making it easy to sign up and get started on from the very first day. But this ease comes with a pain of handling excessive spam. The &lt;a href="https://dev.to/ritikesh/simple-multitenancy-inrails-42b6"&gt;multitenant&lt;/a&gt; architecture becomes the victim here as one bad fish can make the entire pond dirty. To ensure our customers aren't impacted by spammed signups, we have several spam filters and blockers at different levels within the system. For example, upon signup, we do a spam lookup for the tenant based on historical pattern and data and set a spam score for it. Depending on the score, access to certain features or channels are blocked. Email being one of the primary channels, we wanted to safeguard our email reputation and avoid emails delivered from Freshservice being marked as spam. This essentially meant that we had to block email sending from the application for spammy tenants. While not enqueuing jobs for these tenants was the easy way to do this, it was getting incredibly hard to ensure that these checks were followed every time we were enqueuing a background job to deliver an email. We missed a few times and that's when we wanted to add another layer of protection — one at &lt;code&gt;Action Mailer&lt;/code&gt; level.&lt;/p&gt;

&lt;p&gt;The simplest solution was to set &lt;code&gt;perform_deliveries&lt;/code&gt; to false from the interceptor or through a &lt;code&gt;before_action&lt;/code&gt; defined in a base ApplicationMailer like below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;But we were still processing the mail templates and the entire job just to not deliver the emails. We weren't satisfied and wanted something better. That's when we uncovered a hidden gem that's rarely talked about. We &lt;a href="https://github.com/rails/rails/blob/master/actionpack/lib/abstract_controller/callbacks.rb#L34"&gt;came across that setting &lt;/a&gt;, where the &lt;code&gt;response_body&lt;/code&gt; in a &lt;code&gt;before_action&lt;/code&gt; callback aborts the mailer processing right away.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Rails comes preloaded with a bunch of really powerful frameworks loaded with tons of features. We are constantly evolving our codebase and in turn our product to leverage whatever Rails has to offer to better serve our customers.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Load/Eager Paths in Rails and why you should Care</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Mon, 06 Jul 2020 11:28:43 +0000</pubDate>
      <link>https://dev.to/ritikesh/load-eager-paths-in-rails-and-why-you-should-care-1me6</link>
      <guid>https://dev.to/ritikesh/load-eager-paths-in-rails-and-why-you-should-care-1me6</guid>
      <description>&lt;p&gt;Ruby has a way of letting it know where to find classes and modules and other constants - &lt;code&gt;$LOAD_PATH&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Rails adds some of its default directories like &lt;code&gt;app/controllers/*&lt;/code&gt;, &lt;code&gt;app/models/*&lt;/code&gt;, etc. to this path. It also allows you to append to this path by adding the directory or pattern to the magical configuration &lt;code&gt;autoload_paths&lt;/code&gt; in &lt;code&gt;application.rb&lt;/code&gt; or any of the environment files like &lt;code&gt;test.rb&lt;/code&gt; or &lt;code&gt;production.rb&lt;/code&gt;. This setting is often leveraged by gems to add files pertaining to the gem to the &lt;code&gt;$LOAD_PATH&lt;/code&gt; for rails/ruby to be able to load them when required or instructed to - autoload vs eager load. There are tons of articles covering the autoload vs eager load part. &lt;/p&gt;

&lt;p&gt;Instead I would like to talk about a small issue I noticed when abusing the loadpath, sort of, and why following Rails' conventions become necessary.&lt;/p&gt;

&lt;p&gt;Say, you have declared a model in the path &lt;code&gt;app/models/solution/article.rb&lt;/code&gt;, and kept &lt;code&gt;app/models&lt;/code&gt; in &lt;code&gt;autoload_path&lt;/code&gt;, Rails through &lt;a href="https://apidock.com/rails/ActiveSupport/Inflector/safe_constantize"&gt;ActiveSupport#safe_constantize&lt;/a&gt; will be able to load &lt;code&gt;Solution::Article&lt;/code&gt; class. This is because &lt;code&gt;app/models/solution/&lt;/code&gt; like all other subdirectories are part of the &lt;code&gt;$LOAD_PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When trying to load a top level class called &lt;code&gt;Article&lt;/code&gt;, Rails will not be able to find it, because it simply does not exist.&lt;/p&gt;

&lt;p&gt;Now, say, if you've added &lt;code&gt;app/models/**/*&lt;/code&gt; in the &lt;code&gt;autoload_path&lt;/code&gt; instead. This would flatten the paths so that everything is available globally. Now when &lt;code&gt;Solution::Article&lt;/code&gt; is tried to be load, it's available like before. But if &lt;code&gt;Article&lt;/code&gt; is attempted to be loaded, even that's available as &lt;code&gt;article.rb&lt;/code&gt; is in the &lt;code&gt;$LOAD_PATH&lt;/code&gt; and Rails will throw up an error saying &lt;code&gt;expected app/models/solution/article.rb to define the class Article&lt;/code&gt;. 🤦‍♂️&lt;/p&gt;

&lt;p&gt;Hence, it is always advisable to avoid using patterns like &lt;code&gt;**/*&lt;/code&gt; for flattening load paths to make class definitions available. They tend to mess up how Rails resolves classes and constants and with Rails moving towards the faster and cleaner &lt;a href="https://guides.rubyonrails.org/autoloading_and_reloading_constants.html"&gt;Zeitwerk&lt;/a&gt; library for auto/eager/reloading of classes and modules, it becomes imperative to follow clean class/namespace definition practises.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Quick hacks to lighten your database loads with minimal code</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Thu, 02 Jul 2020 07:42:09 +0000</pubDate>
      <link>https://dev.to/ritikesh/quick-hacks-to-lighten-your-database-loads-with-minimal-code-5327</link>
      <guid>https://dev.to/ritikesh/quick-hacks-to-lighten-your-database-loads-with-minimal-code-5327</guid>
      <description>&lt;p&gt;[&lt;em&gt;Graduating from startup to scaleup means having to constantly evolve your applications to keep pace with your customers’ growth. In this Freshworks Engineering series, Rails@Scale, we talk about some of the techniques we employ to scale up our products.&lt;/em&gt;]&lt;/p&gt;

&lt;p&gt;Databases are a core component in most applications. But database loads can be a silent performance killer, causing sluggish application response times and leading to unsatisfied customers. While most database accesses are necessary, some can be avoided. The most obvious example would be ensuring that your application effectively leverages cached data. However, there can be oversights. We identified such missed opportunities and made minimal code changes to reduce a large number of queries fired to the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leveraging the delegation pattern for cached accesses
&lt;/h3&gt;

&lt;p&gt;Delegation is a common software development pattern from the object-oriented programming world. It uses object composition to achieve code reusability similar to the inheritance worlds.&lt;/p&gt;

&lt;p&gt;There are some very &lt;a href="https://www.rubyguides.com/2018/10/delegate-methods-in-ruby/"&gt;detailed&lt;/a&gt; &lt;a href="https://lorefnon.me/2016/01/16/useful-delegation-patterns-for-rails.html"&gt;articles&lt;/a&gt; on delegation patterns and how to apply those principles in Ruby/Rails applications. In this blog, we talk about how you can optimize performance with little changes to your existing delegation code.&lt;/p&gt;

&lt;p&gt;Freshservice, like other Freshworks products, is a multi-tenant SaaS web application. Each tenant is unique and can have configurations that are specific to them. We store some of these configurations in Redis and some of them in the good old RDBMS. The choice of storage is based on factors such as the requirement of ACID properties or constraints or relationships.&lt;/p&gt;

&lt;p&gt;The RDBMS-based configuration table is backed by ActiveRecord Model called TenantConfigs and directly related to our tenant model with the has_one relationship. To reduce the load on the database and for faster access to frequently read data, we use memcached as an LRU cache. The TenantConfigs objects are also cached per tenant for faster access.&lt;/p&gt;

&lt;p&gt;The tenant model has a handful of attributes delegated to the TenantConfigs association. We noticed that at least one of these attributes were accessed as part of every web request that was made to the application. The delegation was a good old Ruby delegation defined as:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Every time we did tenant.locale, Rails would fire a DB query to fetch the locale information from the tenant_configs table. Since we were already caching this information, we wanted to leverage it for faster access and reduce the load on the database. To achieve this, we simply replaced the delegatee from tenant_configs to tenant_configs_from_cache.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The delegate method comes from ActiveSupport and requires the delegatee to be a valid method within the scope of the defining class. Hence, there is no change in how the drop-in replacement works.&lt;/p&gt;

&lt;p&gt;As expected, the changes gave us a significant drop in the number of queries fired for the tenant_configs model, from almost 1,500 queries per minute (QPM) to near zero. This was achieved by merely appending two words of code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YOiTlqMQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/07/02071839/delegate_to_cache.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YOiTlqMQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/07/02071839/delegate_to_cache.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  A reactive approach to Rails’ uniqueness validations
&lt;/h3&gt;

&lt;p&gt;Ruby on Rails allows you to enforce attribute or functional uniqueness at the ORM level through the magical  &lt;a href="https://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of"&gt;validates_uniqueness_of&lt;/a&gt; validation in ActiveRecord. This makes ActiveRecord query check if the database already contains a record for the said attribute(s).&lt;/p&gt;

&lt;p&gt;If it does, the validation fails and Rails doesn’t save the record. This is in line with Rails’ way of doing validations and works seamlessly. However, if you google validates_uniqueness_of, you’ll find numerous articles such as &lt;a href="https://phrase.com/blog/posts/pitfalls-uniqueness-validation-for-uniqueness-using-rails-activerecord/"&gt;this&lt;/a&gt; talking about why this validation is not entirely reliable. Briefly speaking, the main pitfalls are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They are not reliable. Even with the uniqueness checks, there could still be race conditions and an attempt could be made to insert duplicate values in unique columns;&lt;/li&gt;
&lt;li&gt;The above scenario would not be handled cleanly when relied upon validates_uniqueness_of and would throw up a 500, even if you used the safer &lt;code&gt;.save&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Depending on the scale, generates way too many SELECT 1 queries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We were lucky enough to not face the race condition issue in production. However, with growing scale, the number of &lt;code&gt;exists&lt;/code&gt;/&lt;code&gt;select 1&lt;/code&gt; queries to our databases was increasing. Hence we decided to move away from uniqueness validations (&lt;code&gt;validates_uniqueness_of&lt;/code&gt;) for some of our core models.&lt;/p&gt;

&lt;p&gt;Since our database already had unique indexes, all we had to do was remove the validates_uniqueness_of calls in the models. But this approach has a problem. When trying to save duplicates, the database would issue a rollback and Rails would raise an &lt;code&gt;ActiveRecord::RecordNotUnique&lt;/code&gt; exception.&lt;/p&gt;

&lt;p&gt;Normally, you would expect only methods ending with a &lt;code&gt;!&lt;/code&gt; like &lt;code&gt;save!&lt;/code&gt; to raise exceptions. However, this exception gets raised even when using the regular ‘save’ as well. If we were to proceed with the removal of validates_uniqueness_of from our models, our controllers would have to handle this exception specifically.&lt;/p&gt;

&lt;p&gt;To solve this problem and to maintain consistency with our current code flows, we ended up writing a gem (&lt;a href="https://github.com/freshdesk/record_not_unique"&gt;record_not_unique&lt;/a&gt;) to capture ActiveRecord::RecordNotUnique exceptions at the model level and add a validation error on the associated attributes(s).&lt;/p&gt;

&lt;p&gt;The exception would be captured only for exception-safe methods such as &lt;code&gt;save&lt;/code&gt; and &lt;code&gt;update_columns&lt;/code&gt;. &lt;code&gt;save!&lt;/code&gt; would continue to throw exceptions. After replacing &lt;code&gt;validates_uniqueness_of&lt;/code&gt; with &lt;code&gt;handle_record_not_unique&lt;/code&gt;, we measured the results using our log aggregator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/ritikesh/465b69314bce378a9bedaa17d2ffa8e5"&gt;https://gist.github.com/ritikesh/465b69314bce378a9bedaa17d2ffa8e5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the noticeable results were as follows:&lt;/p&gt;

&lt;p&gt;Downtrend on &lt;code&gt;Select 1&lt;/code&gt; queries on the tickets table after changes were deployed to production(1). From 200,000+ queries over the week to zero.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vuJLQsjd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/07/02073053/tickets.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vuJLQsjd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/07/02073053/tickets.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Downtrend on &lt;code&gt;Select 1&lt;/code&gt; queries on the users table after changes were deployed to production(1). From 57,000+ queries over the week to zero.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ang4uTkE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/07/02073055/users.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ang4uTkE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/07/02073055/users.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For other lesser 'written-to' modules such as groups, &lt;code&gt;select 1&lt;/code&gt; queries per week per table reduced by about 20,000.&lt;/p&gt;

&lt;p&gt;This was a significant load reduction on our databases without making any changes to our existing code flows.&lt;/p&gt;

&lt;p&gt;Bill Gates rightly said, "Measuring programming progress by lines of code is like measuring aircraft building progress by weight". The gains we were able to achieve with the above two techniques with minimal lines (words) of code is testament to that.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>beginners</category>
      <category>database</category>
      <category>rails</category>
    </item>
    <item>
      <title>On demand debug logging with memoize_until</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Fri, 26 Jun 2020 09:36:00 +0000</pubDate>
      <link>https://dev.to/ritikesh/optimizing-your-cache-with-memoizeuntil-408f</link>
      <guid>https://dev.to/ritikesh/optimizing-your-cache-with-memoizeuntil-408f</guid>
      <description>&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Caching is a commonly used software optimization technique and is employed in all forms of software development, be it web, or mobile, or even desktop. A cache stores the results of an operation for later use. For example, your web browser would use a cache to load this blog faster should you visit again in the future, enabled by the storage of static resources such as .js, .css, and images in your browser’s memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KVBITY1Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/06/25105234/caching.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KVBITY1Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog-assets.freshworks.com/freshworks/wp-content/uploads/2020/06/25105234/caching.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most common reasons for using cache are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ttl (time to live) — cache data automatically expiring after a specified time interval&lt;/li&gt;
&lt;li&gt;Consistency — The data is always the same when read from different processes — multiple app servers or background processes is a norm in today’s cloud-first architectures.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows the cache to be fresh — frequently invalidated and refreshed because of the ttl — and Consistent — because it’s a single source of truth. Though caching is a powerful tool, it, usually, is a separate process running on another server accessed by network calls. Cache systems are invariably fast but network calls add bottlenecks to the overall response time. With multiple processes making simultaneous calls over the same network — in a closed vpc setup — the cache would need to scale along with your components to keep up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memoization
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Memoization"&gt;Memoization&lt;/a&gt; is a specific type of caching pattern that is used as a software optimization technique. It involves remembering, or in other words, caching a complex operation’s output in-memory on the machine that’s executing the code. Memoization finds its root word in “memorandum”, which means “to be remembered.”&lt;/p&gt;

&lt;p&gt;Memoization has an advantage in having the data cached in-memory on your machine, thereby avoiding network latencies. But you would rarely find memoization, multi-process consistency, and expiration used together.&lt;/p&gt;

&lt;p&gt;We wanted a library that could memoize expensive operations like network calls to the database or file stores like S3. The library should support expiring the memoized values from time-to-time and also be consistent across our multiprocess stateless architecture. &lt;a href="https://rubygems.org/search?utf8=%E2%9C%93&amp;amp;query=memoize"&gt;Looking up&lt;/a&gt; “memoize” in rubygems gave us multiple options like &lt;a href="https://rubygems.org/gems/memoize_ttl"&gt;memoize_ttl&lt;/a&gt;, &lt;a href="https://rubygems.org/gems/persistent_memoize"&gt;persistent_memoize&lt;/a&gt;, &lt;a href="https://github.com/HParker/ruby_memoize_method"&gt;memoize_method&lt;/a&gt;, etc. — They either lacked on the consistency front, or the expiration front, or on both. Some added the bottlenecks of writing to disk, which might increase your IOPs and latencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  MemoizeUntil
&lt;/h3&gt;

&lt;p&gt;Introducing &lt;a href="https://github.com/freshdesk/memoize_until"&gt;memoize_until&lt;/a&gt;. A powerful yet simple memoization library that focuses on the dynamic nature and consistency of all caching systems in a multi-process environment and brings them to the memoization world.&lt;br&gt;
MemoizeUntil memoizes(remembers) values until the beginning of a predetermined time metric — this can be minute, hour, day and even a week.&lt;br&gt;
To begin with, install the gem:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
or simply add it to your Gemfile&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The public API defines methods for each time interval that is supported by the library. In a Rails environment, MemoizeUntil checks for a YML file defined in &lt;a href="https://github.com/freshdesk/memoize_until/blob/master/examples/memoize_until.yml"&gt;config/memoize_until.yml&lt;/a&gt; and initialises the application with this set. This becomes a single place to track all your predefined memoization keys(also referred to as a “purpose”) for each interval. Run time purposes are also supported and can be extended through the &lt;code&gt;add_to&lt;/code&gt; API.

&lt;p&gt;The &lt;a href="https://github.com/freshdesk/memoize_until/blob/master/lib/memoize_until/store.rb"&gt;store&lt;/a&gt; class is responsible for the core memoization logic. MemoizeUntil class creates and maintains a factory constant of store objects for each interval during initialization. These objects are initialized with a nested hash with each purpose as a unique first level key. Upon invocation, MemoizeUntil looks up the factory constant for the interval through the public method definition with the same name, and calls fetch on that store object. The fetch method computes the current moment — if the interval is day, it simply calculates the moment as today’s date — and uses that moment as a subkey in the nested hash. If the interval has a value assigned to it, that value is returned. If the key does not exist, the store clears all previously memoized data to avoid memory bloat, and the original block passed to the method is called and the result is stored for the given moment. MemoizeUntil also handles nils, i.e. nils are also memoized.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Auto fetching of data at the beginning of the pre-specified time metric guarantees consistency across processes.

&lt;h3&gt;
  
  
  How we use it
&lt;/h3&gt;

&lt;p&gt;We built MemoizeUntil as a general purpose library and are using it as a general optimisation pattern across our products and platform services. Most common applications of it involve memoizing configurations such as spam thresholds, API limits for services, etc. There are some specific use-cases as well, some of which we will cover in future blog posts.&lt;/p&gt;

&lt;p&gt;One such use-case is the need for switching to debug logging on the fly. We default to info-level logging in production to save on log sizes and costs associated with serving those logs to third party and in-house services for debugging and metric purposes. Like with all software, there are issues reported by our customers that are reproducible only in their environments and accounts. To debug such issues, we often need debug logs, which are not available because of the default “info” log level set in our production app. Typically, this would require a production deployment to change the log level and then another deployment to revert back to the older level.&lt;/p&gt;

&lt;p&gt;To avoid this friction with deployment dependencies, we wrote middleware for both our app and background workers, which checks for a redis key and changes the log level if the key is set. Sample app middleware is as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
To avoid cache calls for every web request, we wanted to memoize this setting in local memory but also frequently check the cache store if it has been updated. This seemed like a perfect use case for using MemoizeUntil. The cached data required refreshing, but not instantly.&lt;br&gt;
The &lt;a href="https://github.com/freshdesk/memoize_until"&gt;readme&lt;/a&gt; covers additional use cases like how to extend MemoizeUntil for runtime keys and values — and more.

&lt;h3&gt;
  
  
  Not another cache store
&lt;/h3&gt;

&lt;p&gt;MemoizeUntil is not a replacement for a cache store, it’s merely an optimization technique to reduce network calls to your cache store or database through memoization by guaranteeing consistency. Since everything is stored in-memory, memory constraints on the remote servers also need to be considered — although, thanks to the cloud, this isn’t as big a concern as it once used to be. Also, unlike truly standard memoization libraries that memoize method calls for each unique set of parameters, we have taken a slightly modified approach with purposes. These purposes can be application level(predefined purposes) or tenant level(runtime purposes).&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>rails</category>
    </item>
    <item>
      <title>Optimizing string interpolations in Ruby</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Mon, 15 Jun 2020 04:52:27 +0000</pubDate>
      <link>https://dev.to/ritikesh/optimizing-string-interpolations-in-ruby-14h5</link>
      <guid>https://dev.to/ritikesh/optimizing-string-interpolations-in-ruby-14h5</guid>
      <description>&lt;p&gt;&lt;em&gt;[Ruby on Rails is a great web application framework for startups to see their ideas evolve to products quickly. It’s for this reason that most products at Freshworks are built using Ruby on Rails. Moving from startup to scale-up means having to constantly evolve your applications so they can scale up to keep pace with your customers’ growth. In this new Freshworks Engineering series, Rails@Scale, we will talk about some of the techniques and patterns that we employ in our products to tune their performance and help them scale up.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Freshservice is a cloud-based IT help desk and service management solution that enables organizations to simplify IT operations. Freshservice provides ITIL-ready components that help administrators manage Assets, Incidents, Problems, Change, and Releases. The Asset Management component helps organizations exercise control over their IT assets.&lt;/p&gt;

&lt;p&gt;Freshservice is a SaaS product powered by Ruby on Rails. It is backed by a MySQL database for persistence storage and uses Memcached and Redis extensively for caching and config storage purposes. Multi-tenancy is at the core of a SaaS product and tenant isolation is an important factor when dealing with data. To ensure that, the keys representing data in the cache stores are always suffixed with each tenant’s unique ID.&lt;/p&gt;

&lt;p&gt;We benchmarked the idea of changing the way we build cache keys for getting and setting data on both Memcached and Redis. We noticed that for every cached object that belonged to some entity (or entities) like a tenant or a user, we were creating hash objects to build a unique key representing that entity in the cache store.&lt;/p&gt;

&lt;p&gt;This is a fairly common practice of building a string in Ruby (the cache key in our case):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The above snippet creates a new hash object unnecessarily. Since the end goal is to have an interpolated string, we can skip the hash creation part entirely. With the alternate approach, we could just interpolate the required constant with the dynamic value. This value could be anything that responds to &lt;code&gt;to_s&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We introduced a &lt;code&gt;#key&lt;/code&gt; method on the module, which would take a symbol and a value as params and returns an interpolated string. The resulting key that was generated was the same with both approaches, hence the existing cached objects wouldn’t be affected by this change.&lt;/p&gt;

&lt;p&gt;We ran a benchmarking exercise to check if there was any performance impact of this activity. The benchmarking results were as follows:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Benchmarks we ran
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The above benchmarks were run on our current production versions of Ruby/Rails (&lt;code&gt;2.3.7&lt;/code&gt; / &lt;code&gt;4.2.11.1&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Currently, we’re supporting up to 2 dynamic values via 2 different methods (&lt;code&gt;MemcacheKeys#key&lt;/code&gt;, &lt;code&gt;MemcacheKeys#multi_key&lt;/code&gt;). Making the method accept a dynamic number of values would create an array on each invocation via the Ruby splat operation and would defeat the whole purpose.&lt;/p&gt;

&lt;p&gt;After shipping out the key generation changes over the span of a month and a half, we have noticed significant improvements in object allocations and GC frequencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s99pcpX8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AzgaF5lssYVIaZez1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s99pcpX8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AzgaF5lssYVIaZez1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KvABOnAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AGcgNCRdg5hjUCN1F.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KvABOnAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AGcgNCRdg5hjUCN1F.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above approach also reduces class constant footprints. We had previously included the module in numerous classes and modules with an &lt;code&gt;include MemcacheKeys&lt;/code&gt; just to access a constant for building the required key. Even though constants are included via reference and not duplicated, the class still has to maintain a reference to it. You can check the &lt;code&gt;module#constants&lt;/code&gt; method. After the changes, key constants no longer need to be referenced in multiple places.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0oa0a7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AycQL2ZmD56IIE85N.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0oa0a7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AycQL2ZmD56IIE85N.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M8wO8wAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2ArZbvcRhPTU2h5Q12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M8wO8wAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2ArZbvcRhPTU2h5Q12.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>ruby</category>
      <category>rails</category>
      <category>performance</category>
    </item>
    <item>
      <title>How to support utf8 characters in a utf8 mysql table</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Wed, 20 Nov 2019 07:08:38 +0000</pubDate>
      <link>https://dev.to/ritikesh/how-to-support-utf8-characters-in-a-utf8-mysql-table-2a37</link>
      <guid>https://dev.to/ritikesh/how-to-support-utf8-characters-in-a-utf8-mysql-table-2a37</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H16T3q87--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A2-D4lknmGkHGXfFp5xo6uQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H16T3q87--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A2-D4lknmGkHGXfFp5xo6uQ.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on&lt;/em&gt; &lt;a href="https://www.freshworks.com/company/supporting-utf8-characters-in-a-utf8-mysql-table-blog/"&gt;&lt;em&gt;Freshworks’ official Blog&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on November 15, 2019&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Freshworks’ IT service management tool Freshservice enables organizations to simplify IT operations. Freshservice provides ITIL-ready components that help administrators manage assets, incidents, problems, change, and releases, and theasset management component helps organizations exercise control over their IT assets.&lt;/p&gt;

&lt;p&gt;Freshservice is powered by Ruby on Rails.&lt;/p&gt;

&lt;p&gt;Nginx passenger-backed servers are hosted on the EC2 instances of AWS. The servers are hosted on four data centers — US East (US), Europe Central (EUC), India (IND), and Australia (AU).&lt;/p&gt;

&lt;h3&gt;
  
  
  UTF8 in MySQL
&lt;/h3&gt;

&lt;p&gt;Our databases and the underlying tables were all created with utf8 encoding to ensure that a majority of characters are supported at the database level as well. However, MySQL’s “utf8” encoding only supports three bytes per character. The real UTF-8 encoding needs up to four bytes per character. This bug was never fixed. A workaround was released in 2010: a new character set called “utf8mb4”. For more on the MySQL and UTF8 story, you can read “&lt;a href="https://medium.com/@adamhooper/in-mysql-never-use-utf8-use-utf8mb4-11761243e434"&gt;In MySQL never use utf8&lt;/a&gt;” blog post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Emojis in Freshservice — the Rails 3 way
&lt;/h3&gt;

&lt;p&gt;Emojis are valid UTF8 characters and require 4 bytes to be stored and retrieved. However, with the above-specified limitation from MySQL’s native UTF8 encoding, we could not natively support all emojis. To process them, we were relying on an open-source gem called &lt;a href="https://github.com/gmac/gemoji-parser"&gt;gemoji-parser&lt;/a&gt;. The gem allowed serialization of the emoji content by tokenizing emojis into an associated textual representation and de-tokenizing them later. To safely create tickets that contained emoji content, we parsed the email contents to detect emojis tokenized every ticket’s description and notes’ content before saving. We did not de-tokenize emojis when displaying ticket information (for performance reasons and because customers didn’t mind it); therefore, the tickets contained the emojis in their textual representation. For example, &lt;strong&gt;“😀”&lt;/strong&gt; was rendered as &lt;strong&gt;“😀”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While things worked with our set up on Rails 3, there were some known challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The gem was severely outdated. A lot of emojis didn’t work well with the gem and were saved in an unreadable format (such as, ‘\xF0\x9F\x98\x81…’), causing the ticket’s layout to be broken. This required us to fix it manually by going over the ticket’s description and cleaning up unreadable text/HTML parts. Patching or updating the gem regularly with new emojis wasn’t a feasible long term solution.&lt;/li&gt;
&lt;li&gt;There was a performance overhead to tokenize email content, ticket descriptions, and note content.&lt;/li&gt;
&lt;li&gt;There was a possible future performance overhead if we rendered emojis as-is by de-tokenizing the saved subject and description for every ticket in the ticket list and details page views.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Emojis in Rails 4
&lt;/h3&gt;

&lt;p&gt;The Rails 4 framework had internally updated the MySQL session variables and enforced Rails-MySQL to run in &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict"&gt;strict mode&lt;/a&gt;. For information on why Rails enforced this, see “&lt;a href="https://github.com/rails/rails/pull/6069"&gt;Use strict mode in mysql&lt;/a&gt;”. The tables were created with a utf8 charset and Unicode collation and MySQL raised errors (Incorrect string value: ‘\xF0\x9F\x98\x81…’ for column description at row 1) when any invalid content (in this case, an emoji) was inserted into the tables. This was an indispensable concern for the Freshservice team because any content containing emojis would be discarded by the system. We decided to explore the possible solutions to this problem.&lt;/p&gt;

&lt;p&gt;There were a couple of ways to address this. The first option was to convert all the rich-text fields into “serializable fields” in Rails. Rails internally uses YAML for serializing data and the default YAML parsers were capable of serializing emojis as well. However, this had a few concerns. Changing the fields to “serialized” would require us to run a data correction script for all our existing records as they would not be parseable as YAML. Secondly, we were simply moving the serialisation-deserialisation overhead from database writes to database reads, which was much worse.&lt;/p&gt;

&lt;p&gt;The other option was to make Rails and MySQL run completely with the utf8mb4 encoding set. However, we have hundreds of tables across shards on multiple data centers and migrating all the tables wasn’t feasible considering the scale at which Freshservice operates. Also, utf8mb4 (4 bytes) columns occupy more space than the traditional utf8 (3 bytes) columns, and database indexes have a length limit of 767 bytes. This meant that any table having indexes with varchar(255) columns would cause problems because the index that previously occupied 255*3 = 765 bytes would now occupy 255*4 = 1020 bytes. This implied that we would have to change the columns’ length in the index (not a feasible solution for all our use cases) or update the innodb &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix"&gt;large_prefix&lt;/a&gt; value and reindex all our data. Updating indexes, either way, would have taken far too much time and wasn’t worth the effort.&lt;/p&gt;

&lt;p&gt;We decided to test a more reactive approach. We evaluated running Rails in utf8mb4 mode without migrating any database columns. This meant that we could set Rails level encoding to utf8mb4 and migrate only specific columns of some of our tables to accept utf8mb4 content and extend the behavior to other modules or tables in the future.&lt;/p&gt;

&lt;p&gt;To set Rails to run in utf8mb4, we must update the encoding key in database.yml as follows:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Most of our apps’ emoji content comes from two sources: email and mobile. While mobile as a channel is still under utilised, the same cannot be said for email. The email channel is primarily used to create tickets and add notes to existing tickets. Emails are also used to create solution articles (as a draft) and add notes to other modules such as Change, Problem, and Release. For the first cut, we decided to support emojis for tickets and solution articles (higher chances of adding rich content). We ran a benchmarking exercise to check if there was any performance impact of this activity. The benchmarking results were as follows:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As one can see, there was very little difference in terms of performance (~ +0.5ms/ticket for a ~20KB string as the ticket’s description) between the various scenarios. Even though benchmarks are just a means of approximation, this approximation was good enough for us to continue.&lt;/p&gt;

&lt;h3&gt;
  
  
  The migration
&lt;/h3&gt;

&lt;p&gt;We knew that the migration would take a while because it involved tickets and notes (some of our biggest tables in MySQL), and chose to do it during the holiday season.&lt;/p&gt;

&lt;p&gt;On Christmas eve, we began altering the tables in the EUC data center first. We used &lt;a href="https://github.com/soundcloud/lhm"&gt;LHM&lt;/a&gt; (Large Hadron Migration — a tool that allows us to perform database migrations online while the system is live, without locking the tables) because the production environment had significantly large data sizes. We also ensured that all our databases had significant space available as LHM duplicates entire tables during the migration and we were dealing with large tables. We finished EUC, IND, and AU data centers around Christmas eve and started planning for the US data center migration, which was the most complex one. Before beginning with the US data center migration, we enabled utf8mb4 on Rails in all the previously migrated data centers and tested to ensure that there were no production-only surprises(😉) in store for us. After this, we began running migrations in the US data center on the weekend before the new year (as traffic was at its lowest) and finished the entire process around midnight of Jan 02.&lt;/p&gt;

&lt;p&gt;Considering the load and size of the data involved, we had to continuously monitor the migrations and handle scenarios that were expected to happen only in production. For instance, LHM usually does a full scan of data from min(PKEY) — max(PKEY). Our database is based on a &lt;a href="https://freshdesk.com/product-updates/how-freshdesk-scaled-part-one-blog/"&gt;sharded multitenant architecture&lt;/a&gt; where a tenant and all its data reside in a single database shard. Each tenant has unique IDs for all entities across all shards. Some of the new shards had some stale database entries with min(PKEY) as 1 and max(PKEY) in billions. When we began noticing that some of our shards were taking longer to process, we investigated the issue and found the stale entries causing unnecessary scans for millions of records that weren’t even present in the system. We did a full clean up of such stale entries and this helped to reduce the LHM scan durations significantly.&lt;/p&gt;

&lt;p&gt;After a 10-day effort, emojis support was successfully rolled out on all data centers for tickets (subject and description), notes (body), and solution articles (title and description).&lt;/p&gt;

&lt;p&gt;Post deployment, there was a visible impact on garbage collection (GC) and object allocations, as a result of removing the costly tokenizations for every save or update.&lt;/p&gt;

&lt;h3&gt;
  
  
  GC analysis
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KtYZ6HFk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AlK4DhbVGx1TUF7MT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KtYZ6HFk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AlK4DhbVGx1TUF7MT.png" alt=""&gt;&lt;/a&gt;&lt;strong&gt;GC analysis&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Object allocation analysis
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yoVRJONB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AKoSSSCgbk8CPn5wf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yoVRJONB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AKoSSSCgbk8CPn5wf.png" alt=""&gt;&lt;/a&gt;&lt;strong&gt;Object allocation analysis&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Understanding benchmarks: benchmarks should just be used for references. We should not rely too much on them, especially when working with large scale migrations. Our benchmarking exercise indicated an increase in the processing time but ultimately it was not the case. There was a significant boost in performance due to the reduction in GC cycles and object allocations.&lt;/li&gt;
&lt;li&gt;Understanding production migrations: They are very complex and should be done with great care, especially when dealing with customer data. There is always that one surprise awaiting to show up only in production.&lt;/li&gt;
&lt;li&gt;Solving problems the hard (read clean) way comes with its own advantages.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  PS:
&lt;/h3&gt;

&lt;p&gt;Spread the love; start creating tickets on Freshservice with emojis.😎&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Why just cache when you can memoize(with expiration and guaranteed consistency)</title>
      <dc:creator>Ritikesh</dc:creator>
      <pubDate>Sat, 09 Mar 2019 21:23:36 +0000</pubDate>
      <link>https://dev.to/ritikesh/why-just-cache-when-you-can-memoizewith-expiration-and-guaranteed-consistency-3p5d</link>
      <guid>https://dev.to/ritikesh/why-just-cache-when-you-can-memoizewith-expiration-and-guaranteed-consistency-3p5d</guid>
      <description>&lt;h3&gt;
  
  
  Why just cache when you can memoize(with expiration and consistency)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A04bLT3Nk4Xttioq7GxKZAQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A04bLT3Nk4Xttioq7GxKZAQ.png"&gt;&lt;/a&gt;Google images, WP rocket&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memoization is a specific type of caching that is used as a software optimisation technique.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Caching is a commonly used software optimisation technique and is employed in all forms of software development, be it web or mobile or even desktop. A cache stores the results of an operation for later use. For example, your web browser will most likely use a cache to load this blog faster if you visit it again in the future.&lt;/p&gt;

&lt;p&gt;So, when I talk about memoization, I am talking about remembering or caching a complex operations’ output in-memory. Memoization finds its root word in “memorandum”, which means “to be remembered.”&lt;/p&gt;

&lt;p&gt;While caching is powerful, it usually is another process running on some other server bound by the network calls. Cache systems are invariably fast but network calls add bottlenecks to the overall response times. Add multiple processes making simultaneous calls over the same network — in a closed vpc setup — and the cache would need to scale as your components to keep up. Memoization has an advantage in this aspect where the data is cached in-memory, thereby avoiding the network latencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F522%2F1%2A404VB9nQdXHAC7LwlhKalA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F522%2F1%2A404VB9nQdXHAC7LwlhKalA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most powerful aspects of preferring to use cache are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ttl&lt;/strong&gt; (time to live) — cache data automatically expiring after a pre-specified time interval&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The data is always same when read from different processes — multiple app servers or background processes is a norm in today’s cloud-first architectures.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows the cache to be fresh — frequently invalidated and refreshed because of the ttl — and consistent — because it’s a single source of truth. However, the same is not true for memoization and you would barely find memoization, multi-process consistency and expiration used together.&lt;/p&gt;

&lt;p&gt;In this blog however, you’ll see how and when to wield these simple but powerful techniques together, to optimise your own programs and make them run much faster in some cases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Introducing &lt;a href="https://github.com/ritikesh/memoize_until" rel="noopener noreferrer"&gt;memoize_until&lt;/a&gt;. A powerful yet simple memoization technique that focusses on the dynamic nature and consistency of all caching systems in a multi-process environment and brings that to the memoization world.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;MemoizeUntil memoizes(remembers) values until the beginning of a predetermined time metric — this can be minute, hour, day and even a week. The store upon expiry auto-purges previous data — to avoid memory bloat — and refreshes the data by requesting the origin. Since the process auto-fetches data at the beginning of the pre-defined time metric, it is guaranteed to be consistent across processes.&lt;/p&gt;

&lt;p&gt;To begin with, simply, install the package via npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;memoize_until
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then require the module and initialise it with your use-cases and use it where required.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MemoizeUntil&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;memoize_until&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;MemoizeUntil&lt;/span&gt;

&lt;span class="nx"&gt;MemoizeUntil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
 &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;MemoizeUntil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;min&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SomeComplexOperation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For a simple example, let’s consider your production-ready app has a public facing API and you want to implement a FUP(fair usage policy) and hence set appropriate rate limiting. But you could almost foresee some of your customers complaining and wanting an increased API limit every now and then. This requires your API limit to be dynamic.&lt;/p&gt;

&lt;p&gt;Traditionally, developers would save this as a configuration in the configuration database and load it once per request. But over time, such configurations have moved on to be retained in cache stores like redis which are traditionally very fast but the network latencies remain. To avoid cache calls for every web request, you would want to memoize the API limit locally and make use of it for every request but also frequently check the cache store if it has been updated. This seems like a perfect use-case for using memoize_until. The cached data needs refreshing, but not instantly. Sample usage can be found in this gist:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/ritikesh/memoize_until#memoizeuntil" rel="noopener noreferrer"&gt;&lt;strong&gt;readme&lt;/strong&gt;&lt;/a&gt; covers extra documentation like how to extend memoize_until for truly dynamic behaviours — dynamic keys and values — and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; memoize_until is not a replacement for a cache store, it’s merely an optimisation technique to reduce network calls to your cache store or database through memoization by guaranteeing consistency. Since everything is stored in-memory, memory constraints on the remote servers also needs to be considered — although, thanks to the cloud, this isn’t as big a concern as it once used to be.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>showdev</category>
      <category>performance</category>
      <category>caching</category>
    </item>
  </channel>
</rss>
