<?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: Asad Raheem</title>
    <description>The latest articles on DEV Community by Asad Raheem (@tehmas).</description>
    <link>https://dev.to/tehmas</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%2F230481%2Fb6535b39-e795-472c-a946-521a4a04c743.jpeg</url>
      <title>DEV Community: Asad Raheem</title>
      <link>https://dev.to/tehmas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tehmas"/>
    <language>en</language>
    <item>
      <title>How I spent 10 months identifying &amp; reducing technical debt?</title>
      <dc:creator>Asad Raheem</dc:creator>
      <pubDate>Thu, 16 Jul 2020 17:46:05 +0000</pubDate>
      <link>https://dev.to/tehmas/how-i-spent-10-months-identifying-reducing-technical-debt-4e5l</link>
      <guid>https://dev.to/tehmas/how-i-spent-10-months-identifying-reducing-technical-debt-4e5l</guid>
      <description>&lt;p&gt;Edit: Oh, I forgot to mention. Following these simple approaches, I have been able to improve performance by up to 70% and increased scalability to a whole new level by utilizing event-driven architecture.&lt;/p&gt;

&lt;p&gt;It's in October 2019. Finally, I have the time to start lowering the technical debt ratio. But where to start? How to identify? How to prioritize? Which tenant's experience to focus on?&lt;/p&gt;

&lt;h1&gt;
  
  
  Identification
&lt;/h1&gt;

&lt;p&gt;The ultimate goal is to improve user experience by improving performance and scalability. It makes sense to prioritize features that are being used the most by the customers. One approach would be to conduct surveys but that doesn't seem practical. Where can I get such data? Telemetry (Application Insights).&lt;/p&gt;

&lt;p&gt;Since my Cloud Computing Platform (Azure) had a cap for querying data using its portal, I had to write a Python script for downloading and processing 3 months of data for tenants individually and collectively. I obtained the 50th, 90th, and 99th percentiles and the average for each service's response time. After filtering the data using the obtained stats, services were prioritized and selected for improvement accordingly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Analyzing &amp;amp; Improving
&lt;/h1&gt;

&lt;p&gt;I analyzed each selected and related services. Re-designing and revamping each service entirely was not a practical idea. Therefore, I proceeded with the following approaches:&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing DB Round Trips
&lt;/h2&gt;

&lt;p&gt;A database round trip is very costly. I made sure minimum roundtrips were being made by either fetching all required data in a single round-trip or using Redis Cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing Queries
&lt;/h2&gt;

&lt;p&gt;The ORM I was using generates optimized queries but in some cases, the query required a full table scan or was fetching too much data. I introduced non-clustered indexes where reading occurred more frequently than writing. I also split the queries accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stored Procedures
&lt;/h2&gt;

&lt;p&gt;Sometimes, it's not possible to reduce database roundtrips when several tables need to be accessed or operated on. Although from my perspective stored procedures increase maintainability, I used them in such situations. Slow performance services were now high-performance ones. I also changed the parameters of existing stored procedures. Instead of calling the stored procedure repeatedly for different Ids, I passed all the Ids in one go in a comma-separated string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sync to Async
&lt;/h2&gt;

&lt;p&gt;Communicating synchronously with external resources blocks the thread. Wherever possible, I transitioned to asynchronous APIs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk Operations
&lt;/h2&gt;

&lt;p&gt;I utilized a third-party library to bulk insert/update large number of records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Services
&lt;/h2&gt;

&lt;p&gt;Some services were returning too much unrelated data. This was halting the front-end application to render its components even though not much information was required to be shown on them. I broke such services and tried to use the parts of the original response model to have less impact on the front-end application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency Issues
&lt;/h2&gt;

&lt;p&gt;It turns out customers sometimes use a feature in unexpected conditions. I needed to resolve concurrency issues without affecting scalability and performance especially for power-user features. I utilized the serverless compute service (Function App) to cater to this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-Triggered Tasks
&lt;/h2&gt;

&lt;p&gt;A user shouldn't necessarily need to wait for the entire service to complete. Sometimes the job is complex and unavoidably takes time due to several stages involved. I broke such services into several phases and utilized serverless compute service (Function App) along with real-time messages (Azure SignalR) so that the user doesn't need to wait on the same web page. This also reduced load on the server and provided better scalability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transition to New Technologies
&lt;/h2&gt;

&lt;p&gt;The ORM I was using seemed to be significantly slower than a newer one. I also had to utilize new features such as dependency injection in Function Apps. Wherever easily possible, I transitioned to new technologies for better performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain-Driven Design
&lt;/h2&gt;

&lt;p&gt;Initializing a huge data model context takes time. This is even more problematic on the consumption plan due to the cold-start. I started to divide the context domain wise so that only a small set of related tables are in it. This started a transition to microservices approach from a monolithic one but this would require incremental steps to be rolled over the entire product. Nonetheless, I now have a framework set up for doing so.&lt;/p&gt;

&lt;p&gt;I was also involved in developing new features in the meantime. It was fun and I'm still reducing technical-debt whenever I get a chance. This experience has improved my API designing and coding skills. &lt;/p&gt;

&lt;p&gt;I hope this helps.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>beginners</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why Redis Cache times out in Azure Function App on Consumption Plan? - A Journey</title>
      <dc:creator>Asad Raheem</dc:creator>
      <pubDate>Tue, 14 Jul 2020 16:08:06 +0000</pubDate>
      <link>https://dev.to/tehmas/why-redis-cache-times-out-in-azure-function-app-on-consumption-plan-a-journey-4o4j</link>
      <guid>https://dev.to/tehmas/why-redis-cache-times-out-in-azure-function-app-on-consumption-plan-a-journey-4o4j</guid>
      <description>&lt;p&gt;I decided to move a power-user feature to an Azure Function App. Redis Cache was extensively being used. In a controlled environment, it resulted in better scalability and performance.&lt;/p&gt;

&lt;p&gt;The problem?&lt;/p&gt;

&lt;p&gt;Redis time-out exceptions were being thrown on production. Always? No. Sometimes? Yes and that was even a bigger problem as it was difficult to trace the root cause.&lt;/p&gt;

&lt;p&gt;I was following the approach mentioned in Microsoft &lt;a href="https://docs.microsoft.com/en-us/azure/azure-cache-for-redis/cache-dotnet-how-to-use-azure-redis-cache#connect-to-the-cache"&gt;documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lazyConnection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cacheConnection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CacheConnection"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;ToString&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;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheConnection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&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;lazyConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&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;h1&gt;
  
  
  First Hunch
&lt;/h1&gt;

&lt;p&gt;Redis Server Load might have exceeded the plan. To my surprise, that was not the case. Redis was hardly ever exceeding 10% server load.&lt;/p&gt;

&lt;h1&gt;
  
  
  Second Hunch
&lt;/h1&gt;

&lt;p&gt;Redis server is &lt;a href="https://docs.microsoft.com/en-us/azure/azure-cache-for-redis/cache-faq#prior-cache-offering-faqs"&gt;single-threaded&lt;/a&gt;. Object size might be too large in the cache.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Avoid using certain Redis commands that take a long time to complete, unless you fully understand the impact of these commands. For example, do not run the KEYS command in production. Depending on the number of keys, it could take a long time to return. Redis is a single-threaded server and it processes commands one at a time. If you have other commands issued after KEYS, they will not be processed until Redis processes the KEYS command. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was also not the case.&lt;/p&gt;

&lt;h1&gt;
  
  
  Third Hunch
&lt;/h1&gt;

&lt;p&gt;Another feature synchronously accessing Redis for a large object might be causing this issue but it just didn't make sense. Such features weren't being frequently used.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fourth Hunch
&lt;/h1&gt;

&lt;p&gt;Noisy neighbors. Azure Redis Cache Standard Tier C0 plan was being used. It turns out C0 plans &lt;a href="https://docs.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices"&gt;aren't meant for production use&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Basic tier is a single node system with no data replication and no SLA. Also, use at least a C1 cache. C0 caches are meant for simple dev/test scenarios since they have a shared CPU core, little memory, and are prone to "noisy neighbor" issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upgraded the plan and waited patiently. The issue still didn't resolve.&lt;/p&gt;

&lt;h1&gt;
  
  
  Time for Experimentation
&lt;/h1&gt;

&lt;p&gt;Made a testing gear for generating a large number of asynchronous requests to access Redis Cache using the same lazy initialization pattern.&lt;/p&gt;

&lt;p&gt;Viola! The much-awaited timeout finally occurred on my local system. It was occurring when multiple threads were trying to access the cache. Due to the lazy loading pattern mentioned above, the cache connection was asynchronously tried to be initiated by every request. According to the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.lazythreadsafetymode?view=netcore-3.1"&gt;documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Lazy instance is not thread safe; if the instance is accessed from multiple threads, its behavior is undefined. Use this mode only when high performance is crucial and the Lazy instance is guaranteed never to be initialized from more than one thread. If you use a Lazy constructor that specifies an initialization method (valueFactory parameter), and if that initialization method throws an exception (or fails to handle an exception) the first time you call the Value property, then the exception is cached and thrown again on subsequent calls to the Value property.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But how was this occurring on production? The answer, consumption plan.&lt;/p&gt;

&lt;p&gt;The function app is not always running on the consumption plan. The Redis connection was being initialized whenever the function was triggered by an Azure Storage Queue message. The problem was occurring if the function app received a burst of messages either when it wasn't already running or it was scaling out.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;Pass a &lt;code&gt;LazyThreadSafetyMode&lt;/code&gt; mode in the constructor. Yes, that's it. Other than &lt;code&gt;None&lt;/code&gt;, there are two options &lt;code&gt;PublicationOnly&lt;/code&gt; or &lt;code&gt;ExecutionAndPublication&lt;/code&gt;. For my use-case, I needed &lt;code&gt;PublicationOnly&lt;/code&gt; as stated in the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.lazythreadsafetymode?view=netcore-3.1"&gt;documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When multiple threads try to initialize a Lazy instance simultaneously, all threads are allowed to run the initialization method (or the parameterless constructor, if there is no initialization method). The first thread to complete initialization sets the value of the Lazy instance. That value is returned to any other threads that were simultaneously running the initialization method, unless the initialization method throws exceptions on those threads. Any instances of T that were created by the competing threads are discarded.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lazyConnection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cacheConnection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CacheConnection"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;ToString&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;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheConnection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;LazyThreadSafetyMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PublicationOnly&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&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;lazyConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&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 fix itself was simple but figuring out the exact conditions on the production environment was difficult.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: In the above code snippets, &lt;code&gt;ConfigurationManager&lt;/code&gt; is being used to access App Settings. I wrote that here to stay consistent with the documentation. Since Azure Function App v2, &lt;code&gt;Environment.GetEnvironmentVariable&lt;/code&gt; should be used.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I hope this helps.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>redis</category>
      <category>serverless</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
