<?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: Aneesh Makala</title>
    <description>The latest articles on DEV Community by Aneesh Makala (@makalaaneesh).</description>
    <link>https://dev.to/makalaaneesh</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%2F311801%2F0083ba79-2729-4884-848c-030c2e28d592.jpeg</url>
      <title>DEV Community: Aneesh Makala</title>
      <link>https://dev.to/makalaaneesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/makalaaneesh"/>
    <language>en</language>
    <item>
      <title>Vectorization in OLAP Databases</title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Sun, 24 Apr 2022 13:50:35 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/vectorization-in-olap-databases-100j</link>
      <guid>https://dev.to/makalaaneesh/vectorization-in-olap-databases-100j</guid>
      <description>&lt;p&gt;A recent evolution in OLAP (Online Analytical Processing) database systems has been to overhaul the SQL query engine from the traditional ones setup for OLTP (Online Transactional Processing) usecases. They have done this by either using vectorization, or just-in-time (JIT) compilation. In this article, I want to dive a little deep into why and how vectorization helps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Before we go into vectorization, it would help to understand how database query engines are typically implemented. Andy Pavlo does a brilliant job at explaining it &lt;a href="https://youtu.be/L5NhM7kw6Eg?list=PLSE8ODhjZXjbohkNBWQs_otTrBTrjyohi&amp;amp;t=300"&gt;here&lt;/a&gt;, but to summarize, they follow what is called a "pipeline" model. Each operator in the query tree essentially calls next on its child node, which calls next on its child, processes it, and returns it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8HnCwanL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ldrwa9uny0aoi6aov48p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8HnCwanL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ldrwa9uny0aoi6aov48p.png" alt="Image description" width="600" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.tutorialspoint.com/distributed_dbms/distributed_dbms_query_optimization_centralized_systems.htm"&gt;image&lt;/a&gt; above, for example, the SELECT operator for Dname=marketing (not the same as SELECT in SQL, mind you!) calls "next" on its child, the department table scan operator. That operator scans the table, maybe sequentially or using an index, and then returns a single row back. The SELECT operator then proceeds to apply the filter and then if there is a match, returns the tuple further up the tree. In this way, each tuple is fed from the leaf nodes up to the top of the tree in a "pipeline". It's worth noting that unlike the SELECT operator which essentially processes one tuple at a time in the pipeline, for some operators, like the join for example, all the rows from one of its child would need to be materialized(i.e. collected and saved in memory) before it can proceed. These are often referred to as "pipeline-breakers".&lt;/p&gt;

&lt;h2&gt;
  
  
  Vectorization
&lt;/h2&gt;

&lt;p&gt;Vectorization is very similar to the pipeline model, with one important difference in that each operator returns a batch of tuples as opposed to a single tuple. This is a popular technique that has been used in many popular OLAP engines like Presto, Redshift, and Snowflake. At first, it seems like a rather simple change, but it has significant performance implications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced Invocations
&lt;/h3&gt;

&lt;p&gt;A typical example of an OLAP query may be to analyze large amounts of data, and produce aggregations like sums or averages. While we take function calls for granted, the time to handle an invocation for each row, for each operator, for billions of rows, can add up pretty quickly. Vectorization means less number of invocations because of batching, meaning better performance overall. &lt;/p&gt;

&lt;h3&gt;
  
  
  SIMD
&lt;/h3&gt;

&lt;p&gt;Single Instruction Multiple Data (SIMD) is well, the real reason why vectorization helps. Modern processors allow you to perform the same CPU instruction on multiple data with the help of 128-512 bit sized registers. So, if you want to add two integer vectors of size 1000 (like in the &lt;a href="http://ftp.cvut.cz/kernel/people/geoff/cell/ps3-linux-docs/CellProgrammingTutorial/BasicsOfSIMDProgramming.html#:~:text=SIMD%20is%20short%20for%20Single,data%20is%20called%20scalar%20operations."&gt;image&lt;/a&gt; below), you'd need 1000 ADD instructions. With an 8-lane SIMD, i.e. a 256-bit register to store 8 ints (with each int = 32 bits), you'd need roughly 1000/8=125 operations. That's a huge improvement! (theoretically, at least)&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kRBypSe1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1f28j9p2s0f2iustuna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kRBypSe1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1f28j9p2s0f2iustuna.png" alt="Image description" width="439" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In C++, you can use &lt;a href="https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html"&gt;intrinsics&lt;/a&gt; which provide a way to leverage these registers on Intel hardware. These functions can be hard to grok (very much in-line with the readability struggle in optimal code). But, as a small example, &lt;code&gt;_mm256_add_epi32(a, b)&lt;/code&gt; basically adds two vectors a and b of 256 bits with 8 32-bit integers packed together! &lt;/p&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Now, let's walk through an actual example. Let's say you want to compute the average of 100 million integers (cause, why not).&lt;/p&gt;

&lt;p&gt;The basic pipeline model would involve two operators, the sequential scan operator, that reads each int one at a time, and an aggregation operator, that adds up all the integers and then computes the average. Pretty simple, here's some pseudo-code. The AggregationOperator has the SequentialScanOperator  as its child, which it calls iteratively, until completion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SequentialScanOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;#read int from memory/disk
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AggregationOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;childOperator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
      &lt;span class="n"&gt;total_sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;
      &lt;span class="n"&gt;total_len&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_sum&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;total_len&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On an Intel Xeon CPU (2.40 Ghz), this took 0.463 seconds for 100 million integers. &lt;/p&gt;

&lt;p&gt;Now, for the vectorized execution, the operators and the overall setup remains the same, except for the fact that now, each operator's next() method returns a batch of tuples instead of a single tuple. The machine was used for the experiment had AVX2 intrinsics supported (256-bit registers essentially), so we can pack 8 integers into a register. Therefore, let's say that each operator now processes in a batch of 8 tuples. The SequentialScanOperator will now return 8 tuples, and the AggregationOperator will now read 8 input tuples from its child at a time. Of course, since we want to aggregate all the values down to a single value, the output of the AggregationOperator will be a batch, but only of 1 tuple. Here's some pseudo-code (with intrinsics):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SequentialScanOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;xv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;xv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;#read int from memory/disk
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;xv&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AggregationOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# initialize a 256-bit vector with 0s
&lt;/span&gt;    &lt;span class="n"&gt;__m256i&lt;/span&gt; &lt;span class="n"&gt;aggVector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm256_setzero_si256&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;childOperator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="c1"&gt;# load inputs into a 256-bit temp vector
&lt;/span&gt;      &lt;span class="n"&gt;aggVectorTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm256_set_epi32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
         &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
         &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
         &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;input_tuple_vector&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="c1"&gt;# vectorized add temp vector to result vector
&lt;/span&gt;      &lt;span class="n"&gt;aggVector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm256_add_epi32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggVector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aggVectorTemp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;total_len&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;

    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="c1"&gt;# load it back to a (c++) array
&lt;/span&gt;    &lt;span class="n"&gt;_mm256_store_si256&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;__m256i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aggVector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;# final reduce of vector to a single int
&lt;/span&gt;    &lt;span class="n"&gt;total_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;total_sum&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;total_len&lt;/span&gt;&lt;span class="p"&gt;),]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the same machine, this code took 0.199 seconds. That's a 2.32x speedup! While that's great, a question that immediately comes to mind is - shouldn't we expect a speedup closer to 8, since we packed 8 integers together, and essentially performed a single instruction on them? And the answer is - not necessarily. In this example, the program is bound by memory, so the memory bandwidth usually becomes the bottleneck. With vectorization, we've sped up the computation, but we still have to read the data from memory (in the example above), or from disk. And memory is typically much slower compared to the CPU. Therefore, we don't see the gains we might expect. This &lt;a href="https://stackoverflow.com/questions/18159455/why-vectorizing-the-loop-does-not-have-performance-improvement/18159503#18159503"&gt;SO answer&lt;/a&gt; explains it further!&lt;/p&gt;

&lt;p&gt;P.S. The actual code that ran this example can be found &lt;a href="https://github.com/makalaaneesh/db_vectorization"&gt;here&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>database</category>
      <category>simd</category>
      <category>olap</category>
    </item>
    <item>
      <title>The journey from Supercomputing to Spark</title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Sat, 29 Jan 2022 19:43:19 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/the-journey-from-supercomputing-to-spark-4hdb</link>
      <guid>https://dev.to/makalaaneesh/the-journey-from-supercomputing-to-spark-4hdb</guid>
      <description>&lt;p&gt;In the world of computer science, there are so many tools and technologies out there, that it can get overwhelming rather quickly. Before you've completed learning how to use a tool, one of the big tech companies has open-sourced yet another cool project. The problem is: how do you keep up? One of the things that has helped me is to focus more on the "why" rather than the "how". It's impossible to know how to use every single tool out there, and so it's reasonable to learn how to use them when you need to, say on the job, or when you're working on a side project. But spending time understanding the "why", the motivation of the project goes a long way. it enables you to quickly understand the problems that it's solving, why other similar projects are built, how to compare and contrast them, and most importantly, how to pick the right tool for the job at hand. (Yes, I used to be a Python guy and build everything in python, but I've now realised that it's not about the language, it's about understanding the problem at hand, and finding the right tool that can solve the problem and fits well into your existing architecture 🥲 ).&lt;/p&gt;

&lt;p&gt;So, let's take a 10000ft view of the journey all the way from supercomputing to spark, and see why some of the technologies were built, what their limitations were, and how they sparked the need for the next generation of tools to be built.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Back in the day&lt;/strong&gt;, the only approach for scaling was vertical. If you had a computationally intensive task, you would beef up the machine. In other words, supercomputing. Of course, that works well, to an extent. But that has its limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To take maximum advantage of the high-performance hardware, you have to write highly efficient code, which requires expertise and sometimes comes with sacrificed readability.&lt;/li&gt;
&lt;li&gt;Data size started becoming huge, which means that to complete within a reasonable amount of time, even with high-performant computers, a single machine wouldn't be sufficient.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enter, cluster computing&lt;/strong&gt;. A lot of cheap, unreliable machines that can be used to achieve reliable computing. Well, that seems reasonable! But of course, it's not paradise-land yet. With solutions to problems come more, and better problems. Yes, you could now leverage a cluster, but that means writing code that could achieve fault tolerance and debugging parallel programs which requires a lot of low-level programming expertise. As a software engineer, maybe all you want to do is compute the counts of words that appear in a large set of documents, but now you have to worry about parallelizing it, and making your code fault-tolerant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hello, map-reduce/hadoop.&lt;/strong&gt; The motivation here was to create a higher level of abstraction which hides all these complexities and allows the programmer to concentrate on the problem at hand. If you could model your problem into a map and a reduce procedure, you could make use of the map-reduce infrastructure that orchestrates the processing on distributed servers, managing all the communication between different nodes, in an reliable and fault-tolerant manner. In a distributed setting, network latencies can become the bottleneck, so map-reduce solved this with the help of a distributed file system called Hadoop Distributed File System (HDFS) wherein data is first partitioned on multiple nodes in a clusster and computation is moved to data, rather than the other way around. In other words, the nodes in the cluster only operate on the data that is present on their nodes, which avoids moving data around the cluster, thereby reducing latency. While its proponents thought it was a "paradigm-shift", it had its criticisms, notably from Michael Stonebraker (a specialist in database systems, and Turing Award winner). Map-reduce was attempting to solve the problem of processing big data at scale, so some of the criticisms were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why not use one of the then-existing high-performance parallel database systems instead of map-reduce? The database community had spent years optimizing and scaling data processing operations. Map-reduce, in fact, paled in comparison of execution times. &lt;/li&gt;
&lt;li&gt;The lessons learnt in database community, such as "schemas are good" or "high-level access languages like SQL are good" were not incorporated by map-reduce. &lt;/li&gt;
&lt;li&gt;It did not support indexes, or transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these were all valid points, the map-reduce community sure had counter arguments. First, it was not meant to be a DBMS, it was an algorithmic technique. Further, it was built to perform data processing on cheap, and possibly unreliable computers, in a reliable manner (as opposed to the parallel RDBMS systems, which assumed reliable hardware for the most part). &lt;br&gt;
Ignoring this criticisms for a moment, map-reduce also had other limitations. Firstly, map-reduce writes to disk at every step, which isn't very efficient. Further, It had a limited set of operators - map, reduce. Map-reduce was built around an acyclic data flow model, which was not conducive for the types of applications that were starting to be built - like machine learning algorithms and interactive data analysis. That is, you could not reuse a working set of data across multiple parallel operations. If you had to run multiple analytical queries on the same data using map-reduce, you would have to load it from disk every time, which again, isn't very efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing, Spark.&lt;/strong&gt; Spark is a cluster computing framework that solved these problems. It avoids writing to disk for intermediate operations, and does it in-memory instead. It does this using something called a Resilient Distributed Dataset (RDD), which is an immutable collection of data that is partitioned into chunks and distributed across the nodes in a Spark cluster. All spark functions operate on RDDs. There are a couple of key features of an RDD. One, RDDs can be cached in memory of the nodes, which makes it suitable for iterative tasks(like the data analysis usecase we discussed above). Two, Spark keeps a record of the lineage of an RDD transformations, which allows it to reconstruct the RDD if lost, thereby making the otherwise unreliable memory operations, fault tolerant. As a result, spark faster than map-reduce, and is today a popular choice for large-scale data analytics!&lt;/p&gt;

&lt;p&gt;So, where are we, today with big data infrastructures? It's interesting to note that we started with map-reduce trading-off traditional database wisdom for simplicity and scale, but eventually we are now proceeding to incorporate many of the database techniques and address Michael Stonebraker's criticisms. For example, Spark now uses Dataframes instead of RDDs, which have schemas. Spark's query engine has a planning and optimization layer, and file formats like Parquet have adopted columnar compression techniques used in analytical databases. I guess that was the only way to move forward - iteratively, rather than focusing on solving all problems at once. &lt;/p&gt;

&lt;p&gt;It's fascinating to fathom the path we've taken. We've come a long way from being able to process kilobytes on a single machine to petabytes on 1000s of machines. And, I'm excited to see how we advance further!&lt;/p&gt;

&lt;p&gt;Further Reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thenoisychannel.com/2009/04/15/dewitt-and-stonebraker-vs-mapreduce-round-2/"&gt;MapReduce vs Michael Stonebraker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Most of my learnings are from my masters course - &lt;a href="https://event.cwi.nl/lsde/2021/index.shtml"&gt;LSDE&lt;/a&gt; at VU, Amsterdam&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>spark</category>
      <category>database</category>
      <category>dataengineering</category>
      <category>bigdata</category>
    </item>
    <item>
      <title>Golang for Object Oriented People </title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Sun, 28 Mar 2021 13:01:51 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/golang-for-object-oriented-people-l7h</link>
      <guid>https://dev.to/makalaaneesh/golang-for-object-oriented-people-l7h</guid>
      <description>&lt;p&gt;To start off with, in golang, there are no classes, only structs.&lt;/p&gt;

&lt;p&gt;To add fuel to the fire, there is no type hierarchy, (i.e. no inheritance). This is probably the biggest difference as compared to other object oriented languages. Actually, &lt;a href="https://golang.org/doc/faq#Is_Go_an_object-oriented_language"&gt;it can't be truly considered an object-oriented language&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Ok, let's talk about type hierarchy.&lt;/p&gt;

&lt;p&gt;Not having a type hierarchy was an intentional design choice in golang. &lt;em&gt;Type hierarchies result in brittle code. The hierarchy must be designed early, often as the first step of designing the program, and early decisions can be difficult to change once the program is written. As a consequence, the model encourages early overdesign as the programmer tries to predict every possible use the software might require, adding layers of type and abstraction just in case. This is upside down. The way pieces of a system interact should adapt as it grows, not be fixed at the dawn of time.&lt;/em&gt; - &lt;a href="https://talks.golang.org/2012/splash.article#TOC_10"&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a quick look at &lt;a href="https://golang.org/doc/effective_go#embedding"&gt;type embedding&lt;/a&gt;. At first, it seems to be a mechanism for implementing inheritance, but upon close inspection, it becomes clear that it is not. It's simply a tool to borrow pieces of an implementation.&lt;/p&gt;

&lt;p&gt;For example, take a look at the below code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;Animal&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Firstly, you can't just instantiate a Dog struct by&lt;/span&gt;
&lt;span class="c"&gt;// passing in the field "Name".&lt;/span&gt;
&lt;span class="c"&gt;//     d := Dog{Name:"dog"}&lt;/span&gt;

&lt;span class="c"&gt;// This is the proper way to instantiate a Dog struct&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"dog"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="c"&gt;// Next, you might want to employ polymorphism by&lt;/span&gt;
&lt;span class="c"&gt;// creating a variable of type Animal, &lt;/span&gt;
&lt;span class="c"&gt;// and assigning the dog struct to it.&lt;/span&gt;
&lt;span class="c"&gt;// Again, doesn't work. Just because you "embedded" &lt;/span&gt;
&lt;span class="c"&gt;// the Animal struct inside the Dog struct &lt;/span&gt;
&lt;span class="c"&gt;// doesn't create a type hierarchy.&lt;/span&gt;
&lt;span class="c"&gt;//     var a Animal;&lt;/span&gt;
&lt;span class="c"&gt;//     a = Dog{Animal : Animal{Name:"dog"}}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's clear that embedding doesn't create a type hierarchy. Note that you can still access the &lt;code&gt;Name&lt;/code&gt; field by doing &lt;code&gt;d.Name&lt;/code&gt;. That's because embedded fields and methods are "promoted" to the outer struct.&lt;/p&gt;

&lt;p&gt;Ok, so if there is no type hierarchy, embedding seems like it creates one, but doesn't, how do you then group structs together to represent an abstraction? For example, how do you group together a cat and a dog as an "Animal" abstraction as you would in other OOP languages by creating an Animal super class and a Cat, Dog subclass? &lt;/p&gt;

&lt;p&gt;Answer: &lt;em&gt;You group structs not by their type, but by their behavior, i.e. interfaces.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Interfaces
&lt;/h2&gt;

&lt;p&gt;An interface is just a set of methods. Any type that implements these methods satisfies this interface implicitly. The idea is that you can create an abstraction of different concrete type values and work with the values through their common behavior, without having to define a type hierarchy. &lt;a href="https://www.ardanlabs.com/blog/2016/10/reducing-type-hierarchies.html"&gt;Here's&lt;/a&gt; a nice example that explains how.&lt;/p&gt;

&lt;p&gt;Further, Interfaces allow you to embody the &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle"&gt;dependency inversion principle&lt;/a&gt;. It is a recommended practice in go to &lt;a href="https://medium.com/@cep21/what-accept-interfaces-return-structs-means-in-go-2fe879e25ee8"&gt;accept interfaces and return structs&lt;/a&gt;. This way your code depends on abstractions rather than concrete implementations.&lt;/p&gt;

&lt;p&gt;Unlike interfaces in other languages, rather than declaring the interface in the package where you're implementing a concrete type that satisfies that interface, it is recommended to &lt;a href="https://github.com/golang/go/wiki/CodeReviewComments#interfaces"&gt;declare interfaces in the package where you're using that interface&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's an interesting and fundamental shift in how programmers &lt;br&gt;
should think - &lt;em&gt;You group structs not by their type, but by their behavior.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sure, but every OOP language has interfaces. How do I reuse code in golang? &lt;/p&gt;

&lt;h2&gt;
  
  
  Composition over inheritance
&lt;/h2&gt;

&lt;p&gt;You reuse code in golang using embedding and composition. There's a famous principle in object-oriented programming to &lt;a href="https://medium.com/applike/how-to-using-composition-over-inheritance-6681ed1b78e4"&gt;favour composition over inheritance&lt;/a&gt;. &lt;br&gt;
Here's a &lt;a href="https://medium.com/applike/how-to-using-composition-over-inheritance-6681ed1b78e4"&gt;nice article&lt;/a&gt; that describes how go embodies that principle.&lt;/p&gt;

</description>
      <category>go</category>
      <category>oop</category>
    </item>
    <item>
      <title>Why we need to represent a state transfer</title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Sun, 09 Aug 2020 12:41:53 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/why-we-need-to-represent-a-state-transfer-oco</link>
      <guid>https://dev.to/makalaaneesh/why-we-need-to-represent-a-state-transfer-oco</guid>
      <description>&lt;p&gt;REST(Representational State Transfer) has pretty much become the norm now. But, as with anything else, it's crucial to understand its motivation. Why did we come up with REST? What was the situation before REST? &lt;/p&gt;

&lt;p&gt;Well, picture this - This is the 90s. You've built software that interacts with the oracle database. All's well. You have the desktop application installed on all the computers of your company. It comes bundled with oracle client libraries and everything's running smoothly. &lt;/p&gt;

&lt;p&gt;Now, oracle releases a new version of their database that include some critical security bug fixes. You have no choice but to upgrade. You update your code to use the newer version of oracle. Maybe that merely involves a version change, or maybe there are some backward incompatible changes which force you to rewrite few lines of code. You take the database down, upgrade it, and then push out your software update. Now, you need to make sure that all the computers use the latest version of your software, otherwise they won't work (since you have upgraded the database). You make sure you install patches on all the machines, and only then, you can go back to sleep, hoping that you didn't break anything. It's ok, don't worry. I'm sure you've followed test driven development practices strictly.&lt;/p&gt;

&lt;p&gt;Well, if you look at this situation closely, you'll realise that there is a tight coupling between the client application and the server. The client is very aware of the version of the database used by the server. This is a clear breach of &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns"&gt;separation of concerns&lt;/a&gt;. So, what do we need? Hmm, it would be nice if client could just express what it needs via some standard protocol and get the information back in some particular format, without having to worry about how the server retrieved that information. &lt;/p&gt;

&lt;p&gt;Well, that's what REST is. It's the transferring of "representations" of resources. As opposed to directly dealing with the resource, you deal with "representations" of it. You use a "representation of a resource" to transfer resource state which lives on the server into application state on the client. You make a GET HTTP request with an &lt;code&gt;id&lt;/code&gt; (the representation of the resource that you want), and retrieve the complete state of the resource that the server is aware of.&lt;/p&gt;

&lt;p&gt;Such a separation of concerns is what made it possible to achieve high scale. For example, it doesn't matter if you're building an application for a mobile, a desktop or a tablet, you can use the same representations to send and receive data. &lt;/p&gt;

&lt;p&gt;Sources&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=1o7bB4hUPew&amp;amp;list=PLQnljOFTspQU6zO0drAYHFtkkyfNJw1IO&amp;amp;index=18"&gt;Hussein Nasser's video&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/10421579"&gt;Stack Overflow &amp;lt;3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rest</category>
      <category>webdev</category>
      <category>http</category>
    </item>
    <item>
      <title>Dynamic Programming for Pythonistas</title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Sun, 14 Jun 2020 07:34:08 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/dynamic-programming-for-pythonistas-21pl</link>
      <guid>https://dev.to/makalaaneesh/dynamic-programming-for-pythonistas-21pl</guid>
      <description>&lt;p&gt;Dynamic programming is an intimidating topic when it comes to interview preparation. I always fret it. But this time, I found an intuitive way of looking at it, thanks to Python.&lt;/p&gt;

&lt;p&gt;First, let's understand the motivation for dynamic programming. Let's say you have a problem to solve. You realise that if you solve a subproblem(which is a smaller instance of the same problem), you can use the solution to that subproblem to solve the main problem. That's the crux of &lt;a href="https://www.khanacademy.org/computing/computer-science/algorithms/recursive-algorithms/a/properties-of-recursive-algorithms#:~:text=Here%20is%20the%20basic%20idea,to%20solve%20the%20original%20problem."&gt;recursive algorithms&lt;/a&gt;. For example, you want to calculate the factorial of &lt;code&gt;n&lt;/code&gt;. Well, if only you had the factorial of &lt;code&gt;n-1&lt;/code&gt;, you could just multiply &lt;code&gt;n&lt;/code&gt; with that value, and you have your solution.  💯Alright now, let's focus on how to get the factorial  of &lt;code&gt;n-1&lt;/code&gt;. Well, if only you had the factorial of &lt;code&gt;n-2&lt;/code&gt;, you could simply multiple &lt;code&gt;n-1&lt;/code&gt; with that value, and you have the solution. 💯💯. Now, how do we get the factorial of &lt;code&gt;n-2&lt;/code&gt;? If only you had... Ok, I'm going to stop. You get the point.. Here's the recursive solution for it -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So far, so good. Now, what's dynamic programming? It is &lt;strong&gt;merely&lt;/strong&gt; an optimization over recursive solutions that becomes relevant when you have multiple calls to the recursive function for the same inputs. For example, let's take a look at the fibonnaci problem. The fibonacci formula is &lt;code&gt;fib(n) = fib(n-1) + fib(n-2)&lt;/code&gt;. Now, &lt;code&gt;fib(5) = fib(4) + fib(3)&lt;/code&gt; and &lt;code&gt;fib(6) = fib(5) + fib(4)&lt;/code&gt;. Both the calculations require the value of &lt;code&gt;fib(4)&lt;/code&gt;, and in the naive recursive implementation, they will be calculated twice. Hmm, that's not very efficient. Let's see if we can do better. Let's simply cache the solutions so that we don't recompute it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;solution_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# retrieve from cache if present
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solution_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;solution_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# save to cache before returning
&lt;/span&gt;    &lt;span class="n"&gt;solution_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's basically the essence of dynamic programming. Store the results of the subproblems, so that we don't have to recompute it later. Well, now we're more efficient, but we've sacrificed code readability. There's so much going on in the &lt;code&gt;fib&lt;/code&gt; function. Our earlier code was so much simpler. Let's take it up a notch by making the code cleaner with a &lt;a href="https://www.programiz.com/python-programming/decorator"&gt;decorator&lt;/a&gt; in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cache_solution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;solution_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cached_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# retrieve from cache if present
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solution_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;solution_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;func_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# save to cache before returning
&lt;/span&gt;        &lt;span class="n"&gt;solution_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func_result&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func_result&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cached_func&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cache_solution&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What we've done here is separated the concerns of solving the main problem, i.e. calculating fibonacci number, and that of caching the solution of the subproblems. This makes it easier to come up with dynamic programming solutions. First, we can concentrate primarily on coming up with the recursive solution. After that, if we need to optimise it, we simply need to write a decorator!&lt;br&gt;
As a result, the code is cleaner, and more extensible. How?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;fib&lt;/code&gt; function follows the single responsibility principle of only dealing with the solution of calculating  the Fibonacci number. If we need to alter the logic, it's much easier to do it.&lt;/li&gt;
&lt;li&gt;We've created a reusable solution for caching the solutions of subproblems. Maybe you have another function &lt;code&gt;foo&lt;/code&gt; whose solutions you need to cache. Just decorate it and you're done!&lt;/li&gt;
&lt;li&gt;Now, let's say you want to use an external cache (maybe, redis). You may want to do this because you want a central cache for all your applications, or you want to persist cache to disk, or you want to employ a cache eviction policy. Now, all you have to do is make a change to the &lt;code&gt;cache_solution&lt;/code&gt; decorator, to save the solution to redis (as opposed to a dictionary in local memory) and you're done. You can now use redis to cache the solutions for all the functions that use this decorator!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>dynamicprogramming</category>
      <category>decorator</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Assimilating Auth</title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Sun, 10 May 2020 14:23:13 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/assimilating-auth-508c</link>
      <guid>https://dev.to/makalaaneesh/assimilating-auth-508c</guid>
      <description>&lt;h2&gt;
  
  
  Firstly, what the hell is Auth?
&lt;/h2&gt;

&lt;p&gt;Well, auth could mean "authentication" or "authorization". &lt;br&gt;
Authentication is the process of verifying that you are who you say you are. Typically, this would mean validating your credentials such as your username and password.&lt;br&gt;
Authorization, on the other hand is the process of determining if you (the authenticated user) have permission for a resource that you are trying to access.&lt;br&gt;
To be explicit, henceforth, let's use authN and authZ to refer to authentication and authorization respectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  When we login to a website, how does the server know I'm authenticated for every subsequent request I make?
&lt;/h2&gt;

&lt;p&gt;Well, there exists a naive mechanism of authentication called Basic Authentication, which can be used by a HTTP user agent to provide a username and a password when making a request. If you use this mechanism, you'll have to include the username and password in every subsequent request.&lt;br&gt;
But there are other more sophisticated mechanisms of authentication used by websites. For example, in session based authentication, after the first login, the server creates a session for the user, and returns a &lt;code&gt;session_id&lt;/code&gt; as a cookie back to the user-agent.  As long as the user is logged in, the cookie would be sent along with every subsequent request. The server can just compare the &lt;code&gt;session_id&lt;/code&gt; it receives with the &lt;code&gt;session_id&lt;/code&gt; that it had created to verify the user's identity.&lt;br&gt;
There also exists a token-based mechanism of authentication, where the user first provides the username and password in exchange for a &lt;code&gt;token&lt;/code&gt;.  For every subsequent request, the client will send the &lt;code&gt;token&lt;/code&gt; for the server to perform authentication.&lt;br&gt;
More reading - &lt;a href="https://stackoverflow.com/questions/51341562/alternatives-to-basic-authentication-when-logout-is-required"&gt;https://stackoverflow.com/questions/51341562/alternatives-to-basic-authentication-when-logout-is-required&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hang on. Session-based and token-based sound like the same thing. What's the difference?
&lt;/h2&gt;

&lt;p&gt;The biggest difference is that in token-based authentication, the user's state is not stored on the server. The payload(including the token) that is sent is self-sufficient for validation by the server(by the means of something known as digital signatures). This makes it easy to scale your application.&lt;br&gt;
More reading - &lt;a href="https://blog.angular-university.io/angular-jwt/"&gt;https://blog.angular-university.io/angular-jwt/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, cool. Jumping to API development, I usually have to use an API key and a secret. What's that all about?
&lt;/h2&gt;

&lt;p&gt;These are used for authN..&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, why do I need an API key and secret in the first place? Why can't I just use my username and password which I'm already using anyway?
&lt;/h2&gt;

&lt;p&gt;Good question. One reason is to make it more complicated a task for an attacker. If the attacker is resorting to a brute-force attack, they would have to search a much larger space (the search space for a key would be much more random as compared to usernames). Also, using a key instead of a username prevents a leaked key from tracing back to the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, but if we're choosing a separate key, why do I need a key and a secret, both? Why wouldn't a single unique code serve as username and password both?
&lt;/h2&gt;

&lt;p&gt;You must first understand the difference between symmetric and asymmetric cryptographies. In symmetric cryptography, a single key is shared between both parties. The problem with this type of cryptography is that of key exchange. How do you &lt;strong&gt;safely&lt;/strong&gt; exchange keys with which you plan to subsequently exchange information in a safe manner? What if the key exchange process is itself compromised?!&lt;/p&gt;

&lt;p&gt;Enter, asymmetric cryptography, where each party has a public and a private key pair. A message encrypted with a public key can only be decrypted by its private key (and vice versa). So, if I want to send you a message that only YOU can read, I simply encrypt with your public key. You receive the message, and decrypt it with your private key! That's where the secret comes in handy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait. If I'm using HTTPS, my requests should already be encrypted right? Why do I need a secret, then?
&lt;/h2&gt;

&lt;p&gt;You're right. It's an additional layer of security on top of HTTPS. It may sound trivial, but more layers of security, the better.&lt;br&gt;
More reading - &lt;a href="https://stackoverflow.com/a/11561986/4434664"&gt;https://stackoverflow.com/a/11561986/4434664&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OAuth? That comes up a lot.
&lt;/h2&gt;

&lt;p&gt;Ahaa. We've been talking about authN till now. OAuth is all about authZ.&lt;br&gt;
Let me explain the motivation behind OAuth. &lt;br&gt;
Let's say you have a photo printing web application. Your users upload a picture, and you print it out, and deliver it to them. Now, your users want the option of directly selecting an image from their google drive as opposed to downloading it and uploading it. (Users are lazy, aren't they?) &lt;br&gt;
Now, one option is to blatantly ask the user for their google drive username and password. Obviously, that's not acceptable. &lt;/p&gt;

&lt;p&gt;Fine, another option is to ask users to share the photo with you, using a sharable link. That might work for this use-case, but it might not work for other use-cases. For example, what if you want the user to share the entire list of their contacts, so that you can broadcast some message of sorts? Now, as a simple user, I don't have the capability of retrieving all that information from google drive. That's only accessible via an API. Ok so, they could create an application on google drive, and then share the API key and secret with you. But that's still too tedious for the user.&lt;/p&gt;

&lt;p&gt;Enter, OAuth. You must have encountered some screen like this at some point.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZB4q4jkR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developers.google.com/identity/protocols/images/oauth2/device/approval.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZB4q4jkR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developers.google.com/identity/protocols/images/oauth2/device/approval.png" alt="OAuth screen" title="OAuth"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's OAuth in action. It's a standard created for services to access each other on behalf of the user. User is authenticated with your photo printing application. User is also authenticated with google drive. Now, you simply ask google drive for permission on behalf of the user. Google drive asks the user if they want to give you(the photo printing app) permission via the screen shown above, which you accept(you better accept...). Google drive returns an authZ token to you, using which you make subsequent requests to Google drive for retrieving photos, contacts, etc on behalf of the user.&lt;br&gt;
More reading - &lt;a href="https://developer.okta.com/blog/2017/06/21/what-the-heck-is-oauth"&gt;https://developer.okta.com/blog/2017/06/21/what-the-heck-is-oauth&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That makes sense! Final question - If I'm building an API, should I use key and secret, or should I use OAuth?
&lt;/h2&gt;

&lt;p&gt;If the users of your API are all developers, you're better off using API keys and secrets.&lt;br&gt;
You only need to use OAuth when you want to enable your user to use some third-party client application to use their data stored on your application without having to give their username/password to the third-party client application.&lt;br&gt;
One other advantage of using OAuth tokens over API keys and secrets is that they also provide the feature of authZ. Tokens can be tied to scopes. You can create a token that can only read contacts. You can create another token that can not only access contacts, but also modify and delete them! Also, OAuth tokens have an expiry feature, making it more secure!&lt;br&gt;
More reading - &lt;a href="https://zapier.com/engineering/apikey-oauth-jwt/"&gt;https://zapier.com/engineering/apikey-oauth-jwt/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>authentication</category>
      <category>authorization</category>
      <category>oauth</category>
      <category>api</category>
    </item>
    <item>
      <title>Updating the mapping of an elasticsearch index</title>
      <dc:creator>Aneesh Makala</dc:creator>
      <pubDate>Tue, 21 Apr 2020 14:11:00 +0000</pubDate>
      <link>https://dev.to/makalaaneesh/updating-the-mapping-of-an-elasticsearch-index-3h9n</link>
      <guid>https://dev.to/makalaaneesh/updating-the-mapping-of-an-elasticsearch-index-3h9n</guid>
      <description>&lt;p&gt;Okay, so you've set up elasticsearch. You've indexed your data. Search is super fast. All's good. But, suddenly, you have a requirement for which you need to change the mapping of your index. Maybe you need to use a different analyser, or maybe it's as simple as adding a new field to your document, which requires you to add the associated static mapping.&lt;/p&gt;

&lt;p&gt;If you find yourself in such a situation, here are a few approaches you can take - &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approach 1&lt;/strong&gt; - with downtime; index from external data source.

&lt;ul&gt;
&lt;li&gt;This assumes that you have an external data source such as a database from which you can index data all over again, as if you were doing it for the first time.&lt;/li&gt;
&lt;li&gt;When to use?

&lt;ul&gt;
&lt;li&gt;This approach only makes sense for testing purposes in local or in staging. This should not be used in a production environment because downtime isn't really desirable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Steps

&lt;ul&gt;
&lt;li&gt;Delete the index using the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html"&gt;Delete API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create the index, and set the new mapping using the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html"&gt;PUT Mapping API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Index documents from external data source. You could do this using the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html"&gt;Bulk API&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 2&lt;/strong&gt; - without downtime; index from external data source

&lt;ul&gt;
&lt;li&gt;When to use?

&lt;ul&gt;
&lt;li&gt;You could use this approach in production, but if you have a large number of documents, indexing from an external data source like a DB can be a time-consuming process.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Steps

&lt;ul&gt;
&lt;li&gt;If not done already, create an &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html"&gt;alias&lt;/a&gt; &lt;code&gt;index_alias&lt;/code&gt; for your existing index  (&lt;code&gt;old_index&lt;/code&gt;) and change your code to use the alias instead of &lt;code&gt;old_index&lt;/code&gt; directly.&lt;/li&gt;
&lt;li&gt;Create a new index &lt;code&gt;new_index&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Index documents from external data source. You could do this using the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html"&gt;Bulk API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Move the alias &lt;code&gt;index_alias&lt;/code&gt; from &lt;code&gt;old_index&lt;/code&gt; to &lt;code&gt;new_index&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Caveats

&lt;ul&gt;
&lt;li&gt;While the downtime is essentially zero, there could still be &lt;a href="https://blog.codecentric.de/en/2014/09/elasticsearch-zero-downtime-reindexing-problems-solutions/"&gt;consistency issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Indexing from an external data source like a DB can be a time-consuming process if you have a large number of documents.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 3&lt;/strong&gt; - without downtime; index from elasticsearch

&lt;ul&gt;
&lt;li&gt;When to use?

&lt;ul&gt;
&lt;li&gt;Can be used in production when you want to &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html#updating-field-mappings"&gt;change the mapping of an existing field&lt;/a&gt;. If you are merely adding a field mapping, prefer Approach 4&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Steps

&lt;ul&gt;
&lt;li&gt;If not done already, create an &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html"&gt;alias&lt;/a&gt; &lt;code&gt;index_alias&lt;/code&gt; for your existing index  (&lt;code&gt;old_index&lt;/code&gt;) and change your code to use the alias instead of &lt;code&gt;old_index&lt;/code&gt; directly.&lt;/li&gt;
&lt;li&gt;Create a new index &lt;code&gt;new_index&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use elasticsearch &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.4/docs-reindex.html"&gt;reindex&lt;/a&gt; API to copy docs from &lt;code&gt;old_index&lt;/code&gt; to &lt;code&gt;new_index&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Move the alias &lt;code&gt;index_alias&lt;/code&gt; from &lt;code&gt;old_index&lt;/code&gt; to &lt;code&gt;new_index&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Caveats

&lt;ul&gt;
&lt;li&gt;While the downtime is essentially zero, there could still be &lt;a href="https://blog.codecentric.de/en/2014/09/elasticsearch-zero-downtime-reindexing-problems-solutions/"&gt;consistency issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 4&lt;/strong&gt; - without downtime; update existing index

&lt;ul&gt;
&lt;li&gt;When to use?

&lt;ul&gt;
&lt;li&gt;Can be used in production when you want to merely add a new field mapping.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Steps

&lt;ul&gt;
&lt;li&gt;Update mappings of index online using &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html"&gt;PUT mapping API&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html"&gt;_update_by_query&lt;/a&gt; API with params

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;conflicts=proceed&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;In the context of just picking up an online mapping change, documents which have been updated during the process, and therefore have a version conflict, would have picked up the new mapping anyway. Hence, version conflicts can be ignored.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wait_for_completion=false&lt;/code&gt; so that it runs as a background task&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refresh&lt;/code&gt; so that all shards of the index are updated when the request completes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Caveats

&lt;ul&gt;
&lt;li&gt;Can't be used if you want to &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html#updating-field-mappings"&gt;change the mapping of an existing field&lt;/a&gt;. Use Approach 3 instead.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

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