Azure Cosmos DB design patterns – Part 4: Global distributed lock
Over the years, customers have asked us for help in designing applications around specific scenarios they were trying to achieve. In some cases, these centered around implementing certain patterns using a JSON-based NoSQL database. Some of these patterns are very common in the NoSQL world, but not well understood by those new to NoSQL databases. Other patterns were very specific to the Cosmos DB service itself in demonstrating how to leverage specific capabilities to solve difficult architectural challenges.
We’ve been capturing these patterns and sharing them with customers individually. We felt now was a good time to publish some of these more broadly to make more discoverable by users. The result is Azure Cosmos DB Design Patterns. A repository on GitHub that includes a wide variety of samples that show how to implement specific patterns that will allow you to solve design-related challenges when using Azure Cosmos DB for your solutions.
To help share these, we’ve created a blog post series on each of them. Each post will focus on a specific design pattern with a corresponding sample application that’s featured in this repository. We hope you enjoy and find this series useful.
Here’s a list of the previous posts in this series:
Azure Cosmos DB design pattern: Global Distributed Lock
This blog post will focus on the Global Distributed Lock Pattern example we’ve provided in this GitHub repository. The NoSQL Global Distributed Lock Design Pattern is designed for synchronizing access to shared resources in distributed systems. It allows a process to acquire a global lock, ensuring exclusive access to a resource and preventing concurrent modifications. This pattern is superior to regular locks as it handles challenges like network delays, system failures, and data partitioning in a distributed environment. Key features include fence tokens for secure operation ordering, high availability, fault tolerance, scalability, and effective management of network partitions and failures. It’s ideal for large-scale applications requiring consistent access to shared resources across multiple nodes.
A distributed global lock in the NoSQL design pattern is commonly used for enforcing mutual exclusion or coordination across multiple nodes or processes in a distributed system. Here are some examples:
- Critical Sections: In distributed systems, some operations or code segments, known as critical sections, require atomic execution by only one node at a time. A distributed global lock ensures that only one node or process enters this critical section at any given moment, thereby preventing conflicts and maintaining data consistency.
- Resource Synchronization: For scenarios where multiple nodes or processes simultaneously access and modify a shared resource, a distributed lock coordinates this access. For instance, in a document-oriented NoSQL database, if several nodes are updating the same document, a distributed lock guarantees that only one node modifies the document at a time, thus avoiding conflicts and preserving data integrity.
- Concurrency Control: In situations where multiple nodes or processes execute parallel operations on shared data, distributed locks are crucial for concurrency control. By securing a lock on a specific resource or data entity, a node ensures exclusive access, preventing concurrent modifications that could result in inconsistency or errors.
- Distributed Transactions: Distributed locks are pivotal in distributed transactional systems, where multiple operations across different nodes must be executed atomically. They facilitate the coordination of various transaction phases, ensuring no conflicting operations occur during the transaction.
Utilizing a distributed global lock allows for the coordination and synchronization of actions across multiple nodes or processes, thus ensuring consistency and preventing conflicts in a distributed setting. However, it’s vital to recognize the complexity in correctly and efficiently implementing distributed locks. The specific mechanisms and techniques employed may vary depending on the NoSQL database in use.
In this example, the application generates a Lock using a user-provided Name and Time to Live (TTL). This Lock is established within Azure Cosmos DB, allowing it to be monitored by multiple worker threads distributed across various locations. The example illustrates this by creating three threads that persistently attempt to acquire the lock. Once a worker thread successfully obtains the lock, it retains it for a randomly determined duration in milliseconds before relinquishing it. Should the lock not be released within the specified TTL, it is automatically freed.
The implementation leverages the TTL feature to automatically dispose of a lease object, eliminating the need for clients to manually verify a ‘leasedUntil‘ date. However, there’s still a requirement to ensure that two clients aren’t attempting to lease the same object simultaneously. This concurrency check is efficiently handled in Azure Cosmos DB using the ‘etag’ property associated with the object. The screenshot provided demonstrates the Distributed Lock Application in operation.
Why It Matters:
The use of the Global Distributed Lock Design Pattern is crucial for software developers, particularly when dealing with distributed systems, for several key reasons:
- Ensures Data Consistency: In distributed systems, where multiple processes or nodes might attempt to access or modify a shared resource simultaneously, this pattern helps in maintaining data consistency. It prevents conflicting updates and ensures that each operation on the resource is performed atomically.
- Avoids Race Conditions: Race conditions occur when the behavior of a system depends on the sequence or timing of uncontrollable events. The global distributed lock mitigates this by controlling access to shared resources, ensuring that only one process can operate on a resource at a time.
- Facilitates Synchronization Across Multiple Nodes: In distributed systems that span across multiple geographical locations or networks, this pattern synchronizes operations across all nodes. It’s essential for operations that need to be coordinated globally.
- Improves System Reliability: By managing access to shared resources effectively, the pattern reduces the likelihood of system crashes or failures due to concurrent access or data corruption, thus enhancing the overall reliability of the system.
- Supports Scalability: As systems grow and more nodes or processes need to access shared resources, a global distributed lock scales to accommodate this growth, ensuring that the system continues to function efficiently and consistently.
- Handles Network Partitions and Failures: In distributed systems, network issues and node failures are common. The global distributed lock pattern is designed to handle these situations gracefully, ensuring that the system remains robust and operational.
- Facilitates Complex Operations in Distributed Transactions: For complex operations that span multiple databases or services, this pattern can be critical in ensuring that the entire operation either completes successfully or rolls back in case of a failure, maintaining data integrity.
- Simplifies Development: While the underlying mechanisms of global distributed locks can be complex, using a well-designed pattern simplifies the development process by providing a clear and tested method for handling concurrency in distributed systems.
- Promotes Best Practices: Utilizing such patterns encourages developers to think in terms of global, distributed architectures, promoting best practices in designing and building scalable, high-performance applications.
In summary, the Global Distributed Lock Design Pattern is a vital tool for software developers working with distributed systems, as it addresses key challenges such as data consistency, race conditions, synchronization, scalability, and system reliability.
Getting Started with Azure Cosmos DB Design Patterns
You can review the sample code by visiting the Global Distributed Lock Pattern on GitHub. You can also try this out for yourself by visiting the Azure Cosmos DB Design Patterns GitHub repo and cloning or forking it. Then run locally or from Code Spaces in GitHub. If you are new to Azure Cosmos DB, we have got you covered with a free Azure Cosmos DB account for 30 days, no credit card required. If you want more time, you can extend the free period. You can even upgrade too.
Sign up for your free Azure Cosmos DB account at aka.ms/trycosmosdb.
Explore this and the other design patterns and see how Azure Cosmos DB can enhance your application development and data modeling efforts. Whether you’re an experienced developer or just getting started, the free trial allows you to discover the benefits firsthand.
To get started with Azure Cosmos DB Design Patterns, follow these steps:
- Visit the GitHub repository and explore the various design patterns and best practices provided.
- Clone or download the repository to access the sample code and documentation.
- Review the README files and documentation for each design pattern to understand when and how to apply them to your Azure Cosmos DB projects.
- Experiment with the sample code and adapt it to your specific use cases.
About Azure Cosmos DB
Azure Cosmos DB is a fully managed and serverless distributed database for modern app development, with SLA-backed speed and availability, automatic and instant scalability, and support for open-source PostgreSQL, MongoDB, and Apache Cassandra. Try Azure Cosmos DB for free here. To stay in the loop on Azure Cosmos DB updates, follow us on Twitter, YouTube, and LinkedIn.