<?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: Álvaro López Espinosa</title>
    <description>The latest articles on DEV Community by Álvaro López Espinosa (@alvaroloes).</description>
    <link>https://dev.to/alvaroloes</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%2F496567%2F5ba0b16e-3c2b-424e-84e7-1652301d6cd4.png</url>
      <title>DEV Community: Álvaro López Espinosa</title>
      <link>https://dev.to/alvaroloes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alvaroloes"/>
    <language>en</language>
    <item>
      <title>Booster Framework raises the bar in scalability</title>
      <dc:creator>Álvaro López Espinosa</dc:creator>
      <pubDate>Fri, 26 Jan 2024 11:50:19 +0000</pubDate>
      <link>https://dev.to/boostercloud/booster-framework-raises-the-bar-in-scalability-28l4</link>
      <guid>https://dev.to/boostercloud/booster-framework-raises-the-bar-in-scalability-28l4</guid>
      <description>&lt;p&gt;Thanks to the collaboration with our partners using &lt;a href="https://www.boosterframework.com/"&gt;Booster Framework&lt;/a&gt; in Azure, version 2.2 has completely &lt;strong&gt;redesigned how the event processing is handled&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It now uses a combination of the services &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/change-feed"&gt;Azure Cosmos DB change feed&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-about"&gt;Azure Event Hubs&lt;/a&gt; in a way that it can process virtually any number of events in a given time without affecting the rest of the application and still keeping &lt;strong&gt;very low processing times&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Before this change, the event processor was only managed by the “Azure Cosmos DB change feed”, which forced Booster to only use a single processing unit (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview"&gt;Azure Function App&lt;/a&gt;) for the event processing in most situations. While, with this approach, you can still process almost any number of events at a given time, you can experience long delays in situations where thousands of events are registered in a short period of time (in seconds). This is translated to read models that take tens of seconds to reflect the changes, event handlers that get executed long after the corresponding event is registered, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/boostercloud/booster/releases/tag/v2.2.0"&gt;Booster 2.2&lt;/a&gt; now uses Azure Cosmos DB change feed just to send the event to Azure Event Hubs, where it is &lt;strong&gt;distributed optimally among partitions and sent to the corresponding event processor&lt;/strong&gt;. The great advantage of this approach is that we can have many Azure Function Apps (not just one) processing events in parallel, &lt;strong&gt;reducing the latency to the minimum&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Despite this extra level of concurrency and parallelism, all the guarantees Booster offers about data consistency and the order of events are still kept.&lt;/p&gt;

&lt;p&gt;Of course, this change doesn’t affect the developer experience or the code of any current Booster application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Booster has always been focused on scalability (this is one of the reasons that made us take architectural decisions like using event-driven or CQRS), but this new version &lt;strong&gt;takes it to the level of the most demanding enterprises of the world&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We keep pushing hard to bring the best of the event-sourcing, CQRS, and serverless worlds to a framework that offers a neat developer experience focused on what matters. &lt;em&gt;Is there something you think can be improved?&lt;/em&gt; Go ahead and make a suggestion! Remember that &lt;strong&gt;&lt;a href="https://github.com/boostercloud/booster"&gt;Booster is open-source&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>eventdriven</category>
      <category>serverless</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why a DynamoDB call can take 5 minutes to complete</title>
      <dc:creator>Álvaro López Espinosa</dc:creator>
      <pubDate>Fri, 30 Apr 2021 14:21:48 +0000</pubDate>
      <link>https://dev.to/boostercloud/why-a-dynamodb-call-can-take-5-minutes-to-complete-52g2</link>
      <guid>https://dev.to/boostercloud/why-a-dynamodb-call-can-take-5-minutes-to-complete-52g2</guid>
      <description>&lt;p&gt;As part of the work I was doing developing &lt;a href="https://www.booster.cloud"&gt;Booster Framework&lt;/a&gt;, I was in charge of creating heavy load tests to ensure that its auto-scalability feature works and that the data integrity is preserved (under the AWS provider).&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;After many hours of work, load testing Booster with 3,000 requests per second was working pretty well. However, sometimes we were seeing this problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The final counter of processed events had to be 60,000 to consider the test successful, but sometimes &lt;strong&gt;it got stuck in a random number very close to the right one&lt;/strong&gt; (for example, 59,489)&lt;/li&gt;
&lt;li&gt;We didn't see any error anywhere.&lt;/li&gt;
&lt;li&gt;Suddenly, &lt;strong&gt;after 5 minutes or so, the right number appeared&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was unacceptable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching for the root cause
&lt;/h2&gt;

&lt;p&gt;After a lot of investigation, we were seeing that the 0.00001% of all the &lt;strong&gt;DynamoDB calls&lt;/strong&gt; done (it didn't matter if they were a “query”, “get” or “put”) &lt;strong&gt;were taking like 4 or 5 minutes to succeed&lt;/strong&gt;, while the usual time for them was between 5 and 20 milliseconds.&lt;/p&gt;

&lt;p&gt;– &lt;em&gt;"Aha! That's the problem!"&lt;/em&gt;, I thought.&lt;/p&gt;

&lt;p&gt;Yes but, &lt;em&gt;Why were those calls taking that long?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The AWS SDK has a &lt;em&gt;built-in retry mechanism&lt;/em&gt;. When you do a DynamoDB call, for example, it will automatically redo the request if it gets an error that’s considered “retriable” (like a temporary network error). &lt;/p&gt;

&lt;p&gt;It keeps doing retries using an &lt;a href="https://docs.aws.amazon.com/general/latest/gr/api-retries.html"&gt;exponential back-off algorithm&lt;/a&gt;, meaning that it will wait  50ms before doing the first retry. If it fails again, it will wait twice that time, 100ms, to do the next retry. If it fails again, it will wait 200ms, then 400ms, then 800ms, then 1.6 seconds, etc.&lt;/p&gt;

&lt;p&gt;– &lt;em&gt;"That's why calls take so long!!!"&lt;/em&gt;, I told to myself.&lt;br&gt;
– &lt;em&gt;"Nein, nein, nein!!!"&lt;/em&gt;, the facts replied to me&lt;/p&gt;

&lt;p&gt;It turned out that only &lt;em&gt;10 retries are attempted by default&lt;/em&gt;, and that’s only &lt;strong&gt;51 seconds&lt;/strong&gt; in total. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Where did the rest of the time (more than 4 minutes) come from?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To the point:&lt;/strong&gt; It turned out that those 0.00001% DynamoDB calls were done with an HTTP socket in a bad state (because these things happen randomly) and the requests never reached DynamoDB.&lt;/p&gt;

&lt;p&gt;The AWS SDK has a default timeout of 2 minutes, so the call was stuck that amount of time before returning an error (a "socket timeout error").&lt;/p&gt;

&lt;p&gt;This means that, in order for the first retry to occur, we need to wait 2 minutes. Then, it could be that the second retry also finds the socket in a bad state (it is being reused by thousands of requests), so we wait 2 more minutes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This explains why a simple DynamoDB call occasionally took 4 or 5 minutes.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Surprisingly, &lt;strong&gt;the solution was just to reduce the timeout to, for example, 5 seconds, instead of 2 minutes&lt;/strong&gt;. This way we fail much earlier, the AWS SDK can do the next retry (with a new socket connection) sooner, and the chances to succeed are much higher.&lt;/p&gt;

&lt;p&gt;This is the code to do this in the Typescript AWS SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamoDB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DynamoDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DocumentClient&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;DynamoDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DocumentClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;httpOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&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;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;a) Networking things can fail randomly for no apparent reasons&lt;/strong&gt;. Even if there are no bugs, just because of network conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b) Timeouts are extremely important&lt;/strong&gt;. Any request should have a timeout. If not, it can get stuck because of point &lt;strong&gt;a)&lt;/strong&gt; above and there is no way to stop it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;c) Retries are a must in any request&lt;/strong&gt;. Due to points &lt;strong&gt;a)&lt;/strong&gt; and &lt;strong&gt;b)&lt;/strong&gt; you need to have a retry policy. &lt;em&gt;Not having it is simply a bug&lt;/em&gt;, because any network request is expected to fail for no apparent reason. 
If you don’t have a retry policy, it is similar to having a switch in code with no default and you missed a case condition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;d) Load tests are the most useful thing&lt;/strong&gt; to find those kinds of errors that you never think of.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Extra tip!
&lt;/h2&gt;

&lt;p&gt;If you find yourself facing similar abnormal long waits when doing any AWS SDK call, try to &lt;strong&gt;set the parameter "maxRetries" to 0&lt;/strong&gt; and try again. This will expose the error that's causing the retries, so you will be able to act upon it.&lt;/p&gt;

&lt;p&gt;Of course, don't forget to enable the retries again after the error is fixed!!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>database</category>
      <category>booster</category>
    </item>
  </channel>
</rss>
