Showing posts from 2023

Avoid Multiple Cache Refreshes: The Double Check Approach

 In previous articles, we've stressed the importance of caching to enhance the performance of our applications. This time, we're discussing a small yet potent tip to further amplify the benefits derived from caching. A standard caching routine often looks like this: This code is 'functional' and can be regarded as the 'default' approach to caching. Here, we're fetching a value from the cache, and if it's missing, we generate it and store it for future requests. However, a problem arises when we deal with a high-traffic application, such as a .NET Core web application or API, which must handle many concurrent requests. Suppose multiple requests reach this code simultaneously, each finding that it needs to generate the value. In such a case, you'll experience "multiple" refreshes of the same value and several calls to SetValue. To prevent this, we can employ a mutual-exclusion (mutex) lock to restrict multiple threads from accessing the sam

The Power of Simplicity: How a random iterator saved the day

 …and my database. How many times has something so simple saved the day? Sometimes, the simplest solutions can have a significant impact on a problematic situation. In this blog post, we'll explore one such scenario where a small algorithm change led to substantial performance improvements in an existing system. The problem  The issue at hand involved a component in a system using a database table as a makeshift queue for processing updated rows. Multiple processor instances read from the same table, with Redis locking in place to prevent concurrent processing of the same row. However, the processors working in the same order led to numerous collisions and timeouts, causing the system to slow down.  As illustrated in the image, the processors were interfering with one another; while they ultimately completed the work, excessive time was consumed in fruitless attempts to lock records, which consequently increased the strain on the database. The solution: A Random Iterator A random i

Building an Active/Standby HA Architecture with Queue-Based Microservices using Azure Functions

In recent years, the microservices architecture has gained traction owing to its remarkable scalability and adaptability. Azure Functions, a serverless compute service, facilitates the development of dynamic, scalable microservices. When designing microservices for enterprise systems, achieving high availability (HA) is crucial. This blog post delves into a strategy for constructing a HA architecture that incorporates queue-based microservices through the use of Azure Functions. Challenges with Queue-Based Microservices Creating HA queue-based microservices using Azure Functions comes with some design challenges. One of them is deciding between an Active/Active or Active/Standby architecture. While having an Active/Active architecture is trivial, it carries a cost from inter-region traffic and compute resources. Additionally, there may be scenarios where multiple consumers for the same queue are not necessary or desired, making Active/Standby a better option.  But there is a problem wi