<?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: Tiago Padrela Amaro</title>
    <description>The latest articles on DEV Community by Tiago Padrela Amaro (@tiagoamaro).</description>
    <link>https://dev.to/tiagoamaro</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%2F5637%2F1538066.jpeg</url>
      <title>DEV Community: Tiago Padrela Amaro</title>
      <link>https://dev.to/tiagoamaro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiagoamaro"/>
    <language>en</language>
    <item>
      <title>Rails url_for and strong parameters aren't fast</title>
      <dc:creator>Tiago Padrela Amaro</dc:creator>
      <pubDate>Sun, 15 Jul 2018 00:09:34 +0000</pubDate>
      <link>https://dev.to/tiagoamaro/rails-urlfor-and-strong-parameters-arent-fast-49e3</link>
      <guid>https://dev.to/tiagoamaro/rails-urlfor-and-strong-parameters-arent-fast-49e3</guid>
      <description>&lt;p&gt;This post was also &lt;a href="https://www.tiagoamaro.com.br/2018/07/14/rails-url-for/"&gt;published on my website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article I'll describe a Rails performance caveat that I've recently faced on a project: Rails strong parameters and its routing are not performant and should be avoided if you are generating many URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;The project needed a faceted filter which would generated thousands of URLs. Within a single faceted filter object, I had to generate an URL with the current URL merged with a given object's set of options.&lt;/p&gt;

&lt;p&gt;A common approach to this issue would be using Rails' &lt;code&gt;url_for&lt;/code&gt; and strong parameters. I ended up writing the following method for my view object:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="n"&gt;permitted_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DEFAULT_PERMITTED_PARAMS&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;permitted_params&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;Cool! That's concise, secure and it'll give me the current URL with all extra options I want to merge with it.&lt;/p&gt;

&lt;p&gt;As you can imagine, there's was a caveat: this was slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;p&gt;How slow? Pages were taking 500 milliseconds to 800 milliseconds to load, with a single user request and a heavy CPU load. I knew there was something wrong on that view.&lt;/p&gt;

&lt;p&gt;After profiling that the &lt;code&gt;current_path&lt;/code&gt; method was the one to blame, I ran a couple of benchmarks to test how the Rails way would compare to a pure Ruby stdlib approach. Using Ruby's stdlib &lt;code&gt;URI&lt;/code&gt;, the method could be written as:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="n"&gt;permitted_params&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;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;DEFAULT_PERMITTED_PARAMS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Avoid strong parameters &lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&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;original_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Get the current URL through the request&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;permitted_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_param&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the benchmark code:&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;# Inside the `app/controllers/benchmark_controller.rb` file&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&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;original_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ips&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;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ActionDispatch::Routing::UrlFor#url_for with query string"&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;url_for&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="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; http://localhost:3000/?id=42&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Ruby stdlib URI"&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_param&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; http://localhost:3000/?id=42&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare!&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;The results were quite interesting, given two different scenarios I often observed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users requests fit strong parameters, it didn't need to cleanup user input&lt;/li&gt;
&lt;li&gt;Users requests didn't fit strong parameters, it needed to cleanup user input&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Results with parameters that were permitted
&lt;/h3&gt;

&lt;p&gt;Example request to URL &lt;code&gt;http://lvh.me:3000/?id=42&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warming up --------------------------------------
ActionDispatch::Routing::UrlFor#url_for with query string
                       735.000  i/100ms
     Ruby stdlib URI     9.807k i/100ms
Calculating -------------------------------------
ActionDispatch::Routing::UrlFor#url_for with query string
                          7.449k (± 4.8%) i/s -     37.485k in   5.044535s
     Ruby stdlib URI    103.079k (± 4.9%) i/s -    519.771k in   5.054106s

Comparison:
     Ruby stdlib URI:   103079.2 i/s
ActionDispatch::Routing::UrlFor#url_for with query string:     7448.5 i/s - 13.84x  slower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Results with parameters that were not permitted
&lt;/h3&gt;

&lt;p&gt;Example request to URL &lt;code&gt;http://lvh.me:3000/?id=42&amp;amp;count=123&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warming up --------------------------------------
ActionDispatch::Routing::UrlFor#url_for with query string
                       309.000  i/100ms
     Ruby stdlib URI     9.325k i/100ms
Calculating -------------------------------------
ActionDispatch::Routing::UrlFor#url_for with query string
                          3.051k (± 8.4%) i/s -     15.450k in   5.101804s
     Ruby stdlib URI     81.424k (± 3.8%) i/s -    410.300k in   5.046415s

Comparison:
     Ruby stdlib URI:    81423.9 i/s
ActionDispatch::Routing::UrlFor#url_for with query string:     3050.7 i/s - 26.69x  slower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Analyzing results and conclusion
&lt;/h2&gt;

&lt;p&gt;Given that &lt;code&gt;url_for&lt;/code&gt; + strong parameters had a 3k iteration per second (on its worst case scenario) and that some rendered pages had more than 1500 anchor tags, that easily could consume 500 milliseconds of processing time, only to render faceted filtering!&lt;/p&gt;

&lt;p&gt;Rails ease and magic comes with a performance cost. Its router methods are easy to use, but keep in mind you will need to move away from the Rails way if you want faster responses.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>performance</category>
    </item>
    <item>
      <title>Postgres Locks and SKIP LOCKED - Updating Records in Parallel</title>
      <dc:creator>Tiago Padrela Amaro</dc:creator>
      <pubDate>Thu, 11 Jan 2018 16:56:37 +0000</pubDate>
      <link>https://dev.to/tiagoamaro/postgres-locks-and-skip-locked---updating-records-in-parallel-28hj</link>
      <guid>https://dev.to/tiagoamaro/postgres-locks-and-skip-locked---updating-records-in-parallel-28hj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted on my &lt;a href="https://www.tiagoamaro.com.br/2018/01/10/postgres-locks/"&gt;personal website&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this post, I'll show how I solved the following challenge: avoid database deadlocks while updating Postgres records&lt;br&gt;
in parallel using Postgres' locking clause.&lt;/p&gt;

&lt;p&gt;In an application I was developing, I had the following data structure: a product with many offers, where offers needed&lt;br&gt;
to be ranked by their price grouped per product. With that logic, I thought on possible approaches to calculate ranking:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate the ranking whenever the offer price was updated&lt;/li&gt;
&lt;li&gt;Create background workers at the application level to handle the offer ranking update&lt;/li&gt;
&lt;li&gt;Delegate ranking calculation to the database (using triggers)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the &lt;a href="https://en.wikipedia.org/wiki/KISS_principle"&gt;KISS principle&lt;/a&gt; in mind, I tried the first implementation.&lt;/p&gt;
&lt;h2&gt;
  
  
  First Approach: Updating Ranking Through a Callback
&lt;/h2&gt;

&lt;p&gt;This first approach was the naive one: creating an operation to update an offer's ranking whenever its price was updated.&lt;br&gt;
This works fine if your operations are executed in serial, but if you update several records in parallel, you'll face some&lt;br&gt;
issues with your database locking system.&lt;/p&gt;

&lt;p&gt;In my case, I had the Puma server executing multiple calls to fetch prices in real time.&lt;/p&gt;

&lt;p&gt;With multiple processes updating prices simultaneously, updating offers prices got me into a deadlock scenario,&lt;br&gt;
where the same database row was being updated multiple times on different transactions. In this case, wrapping the ranking&lt;br&gt;
update operation into a transaction would also not work, since multiple transactions would still fall in deadlocks. &lt;/p&gt;

&lt;p&gt;Since this scenario looked like a "queue", a background queueing logic could solve the deadlock issue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Second Approach: Background Workers
&lt;/h2&gt;

&lt;p&gt;The second approach wasn't even implemented, since background workers should always be executed in parallel, as its goal is&lt;br&gt;
to speed up or defer costly jobs.&lt;/p&gt;

&lt;p&gt;Updating a single attribute isn't expensive, and it's serial execution nature is the real problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  Third Approach: Database Triggers
&lt;/h2&gt;

&lt;p&gt;Since deadlocks were the problem, deferring the issue to the database seemed to be the most logical approach.&lt;/p&gt;

&lt;p&gt;Using triggers at the offers' table seemed the right approach, but the deadlocks would also occur on triggers if records&lt;br&gt;
were not selected with the right locking mechanism. So, my research began on which mechanism would be the right one to&lt;br&gt;
approach this issue.&lt;/p&gt;

&lt;p&gt;Going through Postgres' documentation, the &lt;a href="https://www.postgresql.org/docs/10/static/sql-select.html#SQL-FOR-UPDATE-SHARE"&gt;&lt;code&gt;SKIP LOCKED&lt;/code&gt; locking clause&lt;/a&gt;&lt;br&gt;
seemed to solve the issue with the following caveat:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With SKIP LOCKED, any selected rows that cannot be immediately locked are skipped. Skipping locked rows provides an&lt;br&gt;
inconsistent view of the data, so this is not suitable for general purpose work, but can be used to avoid lock contention&lt;br&gt;
with multiple consumers accessing a queue-like table.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is an acceptable trade-off, since offer ranking is updated as a "queue-like table".&lt;/p&gt;

&lt;p&gt;With this, I started porting my update operation to a Postgres trigger.&lt;/p&gt;
&lt;h2&gt;
  
  
  Postgres Implementation
&lt;/h2&gt;

&lt;p&gt;With the "one to many" relationship between offers and products, and the &lt;code&gt;product_id&lt;/code&gt; being the foreign key for the products&lt;br&gt;
table, I wrote the following trigger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;calculate_offer_product_ranking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt;
    &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;
    &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;offer_scope&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;
      &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;
      &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;SKIP&lt;/span&gt; &lt;span class="n"&gt;LOCKED&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ranked_offers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;
          &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ranking&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;offer_scope&lt;/span&gt;
      &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;offer_scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;
  &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;product_price_ranking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ranked_offers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ranking&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ranked_offers&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ranked_offers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;calculate_offer_product_ranking_trigger&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;calculate_offer_product_ranking&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you're not familiar with the &lt;code&gt;WITH&lt;/code&gt; clause, nor Postgres window functions (&lt;code&gt;PARTITION BY&lt;/code&gt;), I would highly recommend&lt;br&gt;
you reading their docs, as these functions are quite useful when grouping data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/static/queries-with.html"&gt;&lt;code&gt;WITH&lt;/code&gt; queries docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/static/tutorial-window.html"&gt;Window functions docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This challenge reminded me that not all business rules will go to your application layer, as your database is a powerful&lt;br&gt;
tool. Databases are incredible, reliable and optimized pieces of software, being able of more than "just storing data".&lt;/p&gt;

&lt;p&gt;With this database trigger calculating offers' ranking, I could guarantee ranking without creating any workarounds on my&lt;br&gt;
application layer to execute a task native to the database: atomicity and consistency. &lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
    </item>
    <item>
      <title>What made you switch your main programming language?</title>
      <dc:creator>Tiago Padrela Amaro</dc:creator>
      <pubDate>Fri, 22 Sep 2017 14:40:32 +0000</pubDate>
      <link>https://dev.to/tiagoamaro/what-made-you-switch-your-main-programming-language</link>
      <guid>https://dev.to/tiagoamaro/what-made-you-switch-your-main-programming-language</guid>
      <description>&lt;p&gt;This post's goal is to share experience, telling a bit of your story and helping people who are in doubt.&lt;/p&gt;

&lt;p&gt;What made you switch your main programming language?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>Hi, I'm Tiago Amaro</title>
      <dc:creator>Tiago Padrela Amaro</dc:creator>
      <pubDate>Sun, 12 Mar 2017 23:56:57 +0000</pubDate>
      <link>https://dev.to/tiagoamaro/hi-im-tiago-amaro</link>
      <guid>https://dev.to/tiagoamaro/hi-im-tiago-amaro</guid>
      <description>&lt;p&gt;I have been coding for 10 years.&lt;/p&gt;

&lt;p&gt;You can find me on Twitter as &lt;a href="https://twitter.com/tiagopadrela" rel="noopener noreferrer"&gt;@tiagopadrela&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I live in Rio de Janeiro.&lt;/p&gt;

&lt;p&gt;I work for my own&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: Ruby and Javascript.&lt;/p&gt;

&lt;p&gt;I am currently learning more about Marketing and self employment.&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
