<?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: Guillaume Gautreau</title>
    <description>The latest articles on DEV Community by Guillaume Gautreau (@ghusse).</description>
    <link>https://dev.to/ghusse</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%2F225737%2F30033869-4ab4-4fa2-81f5-d8ddca8e5086.png</url>
      <title>DEV Community: Guillaume Gautreau</title>
      <link>https://dev.to/ghusse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ghusse"/>
    <language>en</language>
    <item>
      <title>Large documents in redis: is it worth compressing them (Part 2)</title>
      <dc:creator>Guillaume Gautreau</dc:creator>
      <pubDate>Wed, 14 Jul 2021 20:06:09 +0000</pubDate>
      <link>https://dev.to/ghusse/large-documents-in-redis-is-it-worth-compressing-them-part-2-4m7j</link>
      <guid>https://dev.to/ghusse/large-documents-in-redis-is-it-worth-compressing-them-part-2-4m7j</guid>
      <description>&lt;p&gt;In a previous post, we measured that compressing large JSON documents before sending them to redis was faster than sending them as is. I made the measurement on my own computer and a local redis database.&lt;/p&gt;

&lt;p&gt;Now that the principle has been validated, I needed to know if this result could be replicated on an environment that is closer to the production environment we have at &lt;a href="https://www.forestadmin.com" rel="noopener noreferrer"&gt;Forest Admin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On a server, I ran the same benchmark:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server performance is the same as in production (except for the load induced by other requests)&lt;/li&gt;
&lt;li&gt;The redis server is comparable to the one that is used in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⬇️ Download speed comparison
&lt;/h2&gt;

&lt;p&gt;In this first graph, we will compare the &lt;strong&gt;download performance from redis + decompression time&lt;/strong&gt; of the same JSON documents, using 3 different methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uncompressed JSON document&lt;/li&gt;
&lt;li&gt;Compressed brotli-1&lt;/li&gt;
&lt;li&gt;Compressed gzip-3&lt;/li&gt;
&lt;li&gt;Compressed deflate-3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These algorithms appeared to be the fastest in their family during my first tests. This second test on a &lt;em&gt;production-like&lt;/em&gt; environment revealed the same, that's why I decided not to publish the same level of info as &lt;a href="https://dev.to/ghusse/large-documents-in-redis-does-it-worth-compressing-them-part-1-59k9"&gt;the last time&lt;/a&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ue4cxfqjk9979hfj6uz.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ue4cxfqjk9979hfj6uz.png" title="Overall download duration with the different methods" alt="Overall download duration with the different methods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Performances are really similar to documents smaller than 4 MB, but the difference starts to be significative after this value.&lt;/p&gt;

&lt;p&gt;For larger documents, all compression methods have similar performance regarding the time it takes to download and decompress them.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⬆️ Upload speed comparison
&lt;/h2&gt;

&lt;p&gt;The same protocol has been applied, for all algorithms. As for the download performance comparison, we will compare here the same challengers that have been selected in the previous test.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fse34vg67ot8fn9ydafbr.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fse34vg67ot8fn9ydafbr.png" title="Overall upload duration with the different methods" alt="Overall upload duration with the different methods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When writing JSON documents larger than 4 MB, results show that it is worth compressing them. For documents with a size of 10 MB, the overall compression and upload time can almost be divided by 2 with this method, from less than 300 ms to 150 ms with brotli-1.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏅 Brotli-1: the winner in production-like environment
&lt;/h2&gt;

&lt;p&gt;This second test shows that the algorithm brotli-1 makes a significant difference regarding the upload and download performance of documents larger than 4 MB.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://www.forestadmin.com" rel="noopener noreferrer"&gt;Forest Admin&lt;/a&gt;, we need to store some JSON documents that are larger than 4 MB, and we will definitely try this solution in production, using a canary deployment.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;space saving ratio&lt;/strong&gt; of the brotli-1 algorithm has been measured to be &lt;strong&gt;more than 90%&lt;/strong&gt;, on the types of documents that we are storing. So, in addition to allowing faster data transfer, this solution will also &lt;strong&gt;save a lot of space&lt;/strong&gt; on our redis instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠ Be sure to test the algorithm on a production-like environment
&lt;/h2&gt;

&lt;p&gt;Be careful before using this solution in your own environment, because results can vary a lot with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the type of document&lt;/li&gt;
&lt;li&gt;the compression algorithm&lt;/li&gt;
&lt;li&gt;the compression level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For instance, &lt;code&gt;brotli&lt;/code&gt; with the default compression level is very effective in terms of space saving, but also &lt;strong&gt;very slow&lt;/strong&gt; during the compression phase.&lt;/p&gt;

&lt;p&gt;When testing in a production-like environment, some algorithms have also been measured as slower than the original solution, which consists of sending the plain JSON documents, as you can see below.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5etg5qv6j01o4hnzfqib.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5etg5qv6j01o4hnzfqib.png" title="Overall download duration with deflate" alt="Overall download duration with deflate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compressing documents with deflate and a compression level of 0 was faster than using no compression on my laptop, but became slower on a real server.&lt;/p&gt;

</description>
      <category>redis</category>
      <category>performance</category>
    </item>
    <item>
      <title>Large documents in redis: is it worth compressing them (Part 1)</title>
      <dc:creator>Guillaume Gautreau</dc:creator>
      <pubDate>Fri, 28 May 2021 15:36:21 +0000</pubDate>
      <link>https://dev.to/ghusse/large-documents-in-redis-does-it-worth-compressing-them-part-1-59k9</link>
      <guid>https://dev.to/ghusse/large-documents-in-redis-does-it-worth-compressing-them-part-1-59k9</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.forestadmin.com"&gt;Forest Admin&lt;/a&gt;, we build admin panels for which we need to compute and cache &lt;strong&gt;large JSON documents&lt;/strong&gt;. These documents are stored in &lt;strong&gt;redis&lt;/strong&gt; and retrieved from this storage in order to be as fast as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐘 The problem with large JSON documents: latency
&lt;/h2&gt;

&lt;p&gt;Some of these JSON document can weight more than 20MB. Storing and retrieving such large document introduce latency in our services, as the data needs to be uploaded or downloaded through the network.&lt;/p&gt;

&lt;p&gt;So, every time we need to retrieve these documents from the server, we download 20MB (in worst cases) from redis and parse the received json. &lt;/p&gt;

&lt;p&gt;Looking at performance logs in production, I could see that this operation of storing or retrieving these documents costed us some significant amount of time.&lt;/p&gt;

&lt;p&gt;I wanted to test if would worth to upload compressed json documents instead of plain json content in redis.&lt;/p&gt;

&lt;p&gt;That's why I created &lt;a href="https://github.com/ghusse/evaluate-redis-compression"&gt;a repository on github&lt;/a&gt; with some code to &lt;strong&gt;run benchmarks&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🕗 Protocol
&lt;/h2&gt;

&lt;p&gt;I want to evaluate the total time of pushing a document and retrieving it from redis, with 5 different implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;none&lt;/code&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; + push the result to redis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;brotli&lt;/code&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; + &lt;code&gt;brotli&lt;/code&gt; + push the buffer to redis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deflate&lt;/code&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; + &lt;code&gt;deflate&lt;/code&gt; + push the buffer to redis&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gzip&lt;/code&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; + &lt;code&gt;gzip&lt;/code&gt; + push the buffer to redis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://msgpack.org/index.html"&gt;&lt;code&gt;msgpack&lt;/code&gt;&lt;/a&gt;: &lt;code&gt;msgpack.pack()&lt;/code&gt; + push the buffer to redis&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each compression algorithm will be evaluated with every possible compression level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;brotli&lt;/code&gt;: 1 to 11&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deflate&lt;/code&gt;: -1 to 9&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gzip&lt;/code&gt;: -1 to 9&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Json documents will be retrieved from a redis server, based on a key pattern. For my own test, I will use &lt;strong&gt;real JSON documents&lt;/strong&gt; of various sizes that we handle in production.&lt;/p&gt;

&lt;p&gt;The benchmark is run against a redis server, and in my case, it'll be a &lt;strong&gt;local server running in docker&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Finally, these tests are run on my Macbook Pro 2,8 GHz Intel Core i7, on node 14.&lt;/p&gt;

&lt;h2&gt;
  
  
  📈  Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Brotli compression
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7haI3Wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1dixlifyhnffsea0ozh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7haI3Wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1dixlifyhnffsea0ozh.png" alt="Overall download duration with brotli" title="Overall download duration with brotli"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that a compressed document with brotli will always be faster to retrieve, and decompress than a large uncompressed document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WK3wbOVi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/33aoha56r8imaq90ancd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WK3wbOVi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/33aoha56r8imaq90ancd.png" alt="Overall upload duration with brotli" title="Overall upload duration with brotli"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These first results show that &lt;strong&gt;⚠ brotli is very slow&lt;/strong&gt; during compression, when used with quality levels of &lt;strong&gt;10&lt;/strong&gt; and &lt;strong&gt;11&lt;/strong&gt;. That's something worth noting as &lt;strong&gt;11 is the default value&lt;/strong&gt; in node 14.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;brotli with its default value&lt;/strong&gt; is actually slower than not doing anything for &lt;strong&gt;document upload&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I plotted another graph without these 2 values for brotli compression, to be able to compare quality levels.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gDyTozid--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00w071apo7g1n0cgw8i4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gDyTozid--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00w071apo7g1n0cgw8i4.png" alt="Overall upload duration with brotli (without 10 and 11)" title="Overall upload duration with brotli without quality levels of 10 and 11"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this 3&lt;sup&gt;rd&lt;/sup&gt; plot, we can see that any other quality level allows to have &lt;strong&gt;better upload time&lt;/strong&gt; of our documents to redis.&lt;/p&gt;

&lt;p&gt;We also can see that the minimal compression level has the best possible performance in the conditions of my experiments. It might change for instance with a remote redis server.&lt;/p&gt;

&lt;h3&gt;
  
  
  To summarize
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Download:

&lt;ul&gt;
&lt;li&gt;🏅 brotli-11 for download performance&lt;/li&gt;
&lt;li&gt;✅ every compression level has better performance&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Upload:

&lt;ul&gt;
&lt;li&gt;🏅 brotli-1 for upload performance&lt;/li&gt;
&lt;li&gt;✅ compression levels up to 9 are faster than no compression&lt;/li&gt;
&lt;li&gt;❌ brotli-10 and brotli-11 are &lt;strong&gt;slower&lt;/strong&gt; than no compression&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Potential winner:

&lt;ul&gt;
&lt;li&gt;🏅 &lt;strong&gt;brotli-1&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Deflate compression
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n4SCiA6_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1diomgay3ozgytmvfp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n4SCiA6_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1diomgay3ozgytmvfp5.png" alt="Overall download duration with deflate" title="Overall download duration with deflate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same results here for deflate than brotli: it's always faster to retrieve compressed documents from redis when they are compressed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5h7Lb0HO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3bw9uzj2saauoel2mb7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5h7Lb0HO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3bw9uzj2saauoel2mb7.png" alt="Overall upload duration with deflate" title="Overall upload duration with deflate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, it's also always faster to upload compressed documents to redis with deflate, whatever the compression level we choose.&lt;/p&gt;

&lt;p&gt;We can see that &lt;strong&gt;deflate 3&lt;/strong&gt; seems to be the faster when used to upload documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  To summarize
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Download:

&lt;ul&gt;
&lt;li&gt;✅ Results are very similar across compression levels, and we cannot spot a clear winner from the results&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Upload:

&lt;ul&gt;
&lt;li&gt;🏅 deflate-3 seems to be slightly faster than other compression levels&lt;/li&gt;
&lt;li&gt;✅ all compression levels are faster to use than nothing at all&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Potential winner:

&lt;ul&gt;
&lt;li&gt;🏅 &lt;strong&gt;deflate-3&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Gzip compression
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZAIjyqx0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dji8ecbwy20q3o4zfag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZAIjyqx0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dji8ecbwy20q3o4zfag.png" alt="Overall download duration with gzip" title="Overall download duration with gzip"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With gzip also, it's always faster to retrieve compressed documents from redis than uncompressed documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8br_VCTv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sq9pe6y18q83xz4cie32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8br_VCTv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sq9pe6y18q83xz4cie32.png" alt="Overall upload duration with gzip" title="Overall upload duration with gzip"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using gzip to upload documents is also always faster, but we can see a clear difference between stronger compression levels (8 &amp;amp; 9) that seem a little slower.&lt;/p&gt;

&lt;h3&gt;
  
  
  To summarize
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Download:

&lt;ul&gt;
&lt;li&gt;✅ Results are very similar across compression levels, and we cannot spot a clear winner from the results&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Upload:

&lt;ul&gt;
&lt;li&gt;🏅 gzip-3 seems to be slightly faster than other compression levels&lt;/li&gt;
&lt;li&gt;✅ all compression levels are faster to use than nothing at all&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Potential winner:

&lt;ul&gt;
&lt;li&gt;🏅 &lt;strong&gt;gzip-3&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  msgpack
&lt;/h2&gt;

&lt;p&gt;The library &lt;a href="https://msgpack.org/index.html"&gt;&lt;code&gt;msgpack&lt;/code&gt;&lt;/a&gt; allows to serialize javascript objects in a smaller form that the standard &lt;code&gt;JSON.stringify&lt;/code&gt;. It is documented as slower than JSON, but I wanted to measure if the time to serialize was compensated by a faster transfert.&lt;/p&gt;

&lt;p&gt;It also has the advantage to transform objects into a buffer in one operation, in contrary to the solution using a compression algorithm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;msgpack transforms an object into a buffer&lt;/li&gt;
&lt;li&gt;solutions with compression are requiring 2 steps: one for transformation to JSON, another for the compression of the resulting string.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dH3sYLL0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqbysisf6elbu6zaiac2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dH3sYLL0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqbysisf6elbu6zaiac2.png" alt="Overall download duration with msgpack" title="Overall download duration with msgpack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Results are not good for msgpack in my local environment: it seems &lt;strong&gt;always slower to use msgpack&lt;/strong&gt; than just sending plain JSON documents to my redis server.&lt;/p&gt;

&lt;p&gt;⚠ This library can still produce interesting results when the network gets slower. In my situation, it cannot be faster than solutions based on compression, because the compression level obtained with msgpack (around 60%) is a lot less interesting than the compression obtained with compression algorithms (more than 90%).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vUHMXa-N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n604o0a8ns80aeish865.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vUHMXa-N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n604o0a8ns80aeish865.png" alt="Overall upload duration with msgpack" title="Overall upload duration with msgpack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upload results are not good either, for msgpack.&lt;/p&gt;

&lt;h3&gt;
  
  
  To summarize
&lt;/h3&gt;

&lt;p&gt;❌ msgpack is slower than the original implementation both for upload &amp;amp; download&lt;/p&gt;

&lt;h2&gt;
  
  
  Final match: brotli-1 vs deflate-3 vs gzip-3
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T7wYWaD4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5arwwzdw51emn2zzfba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T7wYWaD4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5arwwzdw51emn2zzfba.png" alt="Overall download duration with best compression algorithms" title="Overall download duration with the best compression algorithms"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Results here are very similar between compression algorithms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PssAizJ2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwvvgkjj81wimlarye18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PssAizJ2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwvvgkjj81wimlarye18.png" alt="Overall upload duration with best compression algorithms" title="Overall download upload with the best compression algorithms"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regarding upload time, &lt;strong&gt;brotli-1&lt;/strong&gt; seems to have a clear advantage over other algorithms.&lt;/p&gt;

&lt;h2&gt;
  
  
  And the winner is: 🏅 &lt;strong&gt;brotli-1&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Next step: validate these results on an solution that is closer to the production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on an instance that has similar performance to production instances&lt;/li&gt;
&lt;li&gt;use a remote redis instance, similar to the one used in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These results will be shared in a part 2 of this series!&lt;/p&gt;

</description>
      <category>redis</category>
      <category>compression</category>
      <category>performance</category>
      <category>benchmark</category>
    </item>
    <item>
      <title>Git tip: get back to work after a revert on master </title>
      <dc:creator>Guillaume Gautreau</dc:creator>
      <pubDate>Sat, 24 Apr 2021 22:02:28 +0000</pubDate>
      <link>https://dev.to/ghusse/git-tip-get-back-to-work-after-a-revert-on-master-2gji</link>
      <guid>https://dev.to/ghusse/git-tip-get-back-to-work-after-a-revert-on-master-2gji</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8ozl4wOj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/903inki3qfkgkp8efgeq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8ozl4wOj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/903inki3qfkgkp8efgeq.jpg" alt="Git diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shit happens.&lt;/p&gt;

&lt;p&gt;Sometimes, a &lt;strong&gt;sneaky bug&lt;/strong&gt; hid itself into the beautiful change you worked on. It even flew below unit tests' radars and tiptoed without being noticed during manual tests.&lt;/p&gt;

&lt;p&gt;Now, this nasty &lt;strong&gt;bug&lt;/strong&gt; is live in production and &lt;strong&gt;EVERYONE notices it&lt;/strong&gt;, you have to &lt;strong&gt;revert&lt;/strong&gt; your changes from master. 😢 &lt;/p&gt;

&lt;h2&gt;
  
  
  ↩ The revert
&lt;/h2&gt;

&lt;p&gt;Ok, this is the time where you revert your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Assuming that you have to create a PR&lt;/span&gt;
&lt;span class="c"&gt;# for the revert&lt;/span&gt;
git checkout master
git pull
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; fix/revert-superb-change
git revert HASH-OF-MERGE-COMMIT
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin fix/revert-superb-change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your PR gets approved, your revert just cancelled everything that was in your cool change.&lt;/p&gt;

&lt;h2&gt;
  
  
  👷 Work on a fix
&lt;/h2&gt;

&lt;p&gt;At this point, the easiest thing to do is to just make a fix on the branch containing all your changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feat/superb-change
&lt;span class="c"&gt;# Work on a fix&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"fix: sneaky bug"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  😨 OMG, if I merge master on my branch, I lose almost all my work
&lt;/h2&gt;

&lt;p&gt;That's it, if you want to prepare your branch to be merged again on master, you'll face another problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Master contains a commit that &lt;strong&gt;removes&lt;/strong&gt; the work from your branch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you merge master into your feature branch as usual, it will actually remove a large proportion of your changes on your branch. 🤯 &lt;/p&gt;

&lt;h2&gt;
  
  
  🚒 Merge mastery to the rescue
&lt;/h2&gt;

&lt;p&gt;This is the trick: you can &lt;strong&gt;tell git&lt;/strong&gt; that a particular commit &lt;strong&gt;had been merged&lt;/strong&gt; &lt;strong&gt;without&lt;/strong&gt; actually &lt;strong&gt;merging it&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feat/superb-change

&lt;span class="c"&gt;# This will allow you to apply all &lt;/span&gt;
&lt;span class="c"&gt;# changes between your first merge &lt;/span&gt;
&lt;span class="c"&gt;# and the revert, if any&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# ⚠ It's important to carefully choose&lt;/span&gt;
&lt;span class="c"&gt;# the commit JUST BEFORE the revert commit&lt;/span&gt;
git merge HASH-OF-COMMIT-JUST-BEFORE-REVERT

&lt;span class="c"&gt;# This is how you tell git to merge&lt;/span&gt;
&lt;span class="c"&gt;# without really merging the revert&lt;/span&gt;
git merge HASH-OF-REVERT-COMMIT &lt;span class="nt"&gt;--strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The option &lt;code&gt;--strategy=ours&lt;/code&gt; tells git to keep all our &lt;strong&gt;current changes&lt;/strong&gt; when merging. &lt;/p&gt;

&lt;p&gt;It means that it will only &lt;strong&gt;record&lt;/strong&gt; the merge &lt;strong&gt;without changing anything&lt;/strong&gt; in your branch.&lt;/p&gt;

&lt;p&gt;It's important to note that you should first merge all changes made &lt;strong&gt;before the revert&lt;/strong&gt; in order to correctly apply them. This way, only the revert will be merged without changes on your code.&lt;/p&gt;

&lt;p&gt;Once everything had been done, you can proceed as usual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Will merge all changes made after the revert&lt;/span&gt;
git merge master
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, your branch is ready to be merged into master, with all your changes!&lt;/p&gt;




&lt;p&gt;&lt;small&gt;Thanks to &lt;a href="https://gist.github.com/bryanbraun"&gt;@bryanbraun&lt;/a&gt; for his awesome &lt;a href="https://gist.github.com/bryanbraun/8c93e154a93a08794291df1fcdce6918"&gt;git diagram template&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Extract-Transform-Load with RxJS: save time and memory with backpressure</title>
      <dc:creator>Guillaume Gautreau</dc:creator>
      <pubDate>Tue, 20 Apr 2021 13:22:20 +0000</pubDate>
      <link>https://dev.to/ghusse/extract-transform-load-with-rxjs-save-time-and-memory-with-backpressure-jaa</link>
      <guid>https://dev.to/ghusse/extract-transform-load-with-rxjs-save-time-and-memory-with-backpressure-jaa</guid>
      <description>&lt;p&gt;Let's say that you have to &lt;strong&gt;extract 100M objects&lt;/strong&gt; from a database, make some &lt;strong&gt;transformations&lt;/strong&gt; on them and then &lt;strong&gt;load&lt;/strong&gt; them into &lt;strong&gt;another storage system&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Problems will arise as soon as writing into the second DB will become &lt;strong&gt;slower&lt;/strong&gt; than reading from the first. Depending on the implementation, you could face one of these issues: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extracted data stacks up in your memory, and your program crashes because of the &lt;strong&gt;memory usage&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;you send &lt;em&gt;too many requests&lt;/em&gt; in parallel to your target database;&lt;/li&gt;
&lt;li&gt;your program is &lt;strong&gt;slow&lt;/strong&gt; because you process each page of data in sequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At &lt;a href="https://www.forestadmin.com"&gt;Forest Admin&lt;/a&gt;, we recently faced this issue to move data from a Postgresql database to ElasticSearch.&lt;/p&gt;

&lt;p&gt;These problems can be addressed by processing data in streams that support &lt;strong&gt;backpressure&lt;/strong&gt;. It allows the stream to process data &lt;strong&gt;at the pace of the slowest&lt;/strong&gt; asynchronous processing in the chain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rxjs.dev/"&gt;RxJS&lt;/a&gt; is a great streaming library, but it does not natively support backpressure, and it's not easy to find examples. So, I decided to share one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's illustrate with an example
&lt;/h2&gt;

&lt;p&gt;Let's fake the extract method just for the purpose of this article:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Just fake an async network access that&lt;/span&gt;
  &lt;span class="c1"&gt;// resolves after 200ms&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;)&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="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="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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="nx"&gt;index&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="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Random label &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Random title &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&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;The load method, could be asynchronous but that's not useful in this example.&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;function&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;i&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;And now, let's fake the load method:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="c1"&gt;// Let's fake an async network access that takes&lt;/span&gt;
  &lt;span class="c1"&gt;// max 150ms to write all the items&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
    &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&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;h2&gt;
  
  
  Example of backpressure in RxJS
&lt;/h2&gt;

&lt;p&gt;The backpressure is ensured by the &lt;code&gt;BehaviorSubject&lt;/code&gt; named &lt;code&gt;drain&lt;/code&gt; in the example below. You'll see that the code allow to push data concurrently on the target database, with a &lt;strong&gt;limit of 5&lt;/strong&gt; requests in parallel.&lt;/p&gt;

&lt;p&gt;Input data is also loaded with concurrency, but this time the pace is regulated by the &lt;code&gt;drain&lt;/code&gt; subject. Every time a page is sent to the target database, we allow another one to be extracted.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mergeMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;extractTransformLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONCURRENCY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PAGE_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// This allows us to load a fixed number&lt;/span&gt;
  &lt;span class="c1"&gt;// of pages from the beginning&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;drain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONCURRENCY&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="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;drain&lt;/span&gt;
    &lt;span class="c1"&gt;// This is necessary because the observable&lt;/span&gt;
    &lt;span class="c1"&gt;// streams arrays. This allows us to push&lt;/span&gt;
    &lt;span class="c1"&gt;// a fixed number of pages to load from &lt;/span&gt;
    &lt;span class="c1"&gt;// the beginning&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mergeMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// Values inside the arrays don't really matter&lt;/span&gt;
    &lt;span class="c1"&gt;// we only use values indices to generate page&lt;/span&gt;
    &lt;span class="c1"&gt;// numbers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;index&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="c1"&gt;// EXTRACT&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mergeMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAGE_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="c1"&gt;// Terminate if it was an empty page = the last page&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;results&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;drain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&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;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// TRANSFORM and LOAD&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mergeMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CONCURRENCY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// Just make sure to not keep results in memory&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// When a page has been processed, allow to extract&lt;/span&gt;
    &lt;span class="c1"&gt;// a new one&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tap&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="nx"&gt;drain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&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;toPromise&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;In the example above, we initialized the concurrency to 5, meaning that 5 requests can be sent to the target database at the same time. In order to reduce the time waiting for new data, the &lt;code&gt;BehaviorSubject&lt;/code&gt; named &lt;code&gt;drain&lt;/code&gt; ensures to load twice as much pages of data.&lt;/p&gt;

&lt;p&gt;In this example, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory will contain 10 pages of data at the maximum;&lt;/li&gt;
&lt;li&gt;the processing will be as fast as possible with the maximum concurrency that we defined;&lt;/li&gt;
&lt;li&gt;only 5 queries can be made in parallel to the target database.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>rxjs</category>
      <category>streams</category>
    </item>
  </channel>
</rss>
