Distributed Lock - Using fencing token for preventing concurrent writes to a network file
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Distributed systems, by their nature, involve multiple processes, often running on different machines, that need to coordinate their actions. One common challenge in these systems is ensuring that only one process can perform a particular action at a time. This is particularly crucial when the action involves writing to a shared resource, such as a network file. Without proper management, concurrent writes can lead to data corruption, inconsistencies, or lost updates. To address this issue, one effective mechanism employed is the use of distributed locks with fencing tokens.
What are Distributed Locks?
Distributed locks are mechanisms designed to prevent multiple processes from performing the same piece of work at the same time. They extend the concept of locks in single-computer systems to distributed environments, where nodes do not share memory and might even be spread across geographically different locations.
Why Fencing Tokens?
A fencing token is a unique, monotonically increasing number provided to a process when it acquires a lock. This token helps ensure that operations using older tokens, possibly from previous owners of the lock, are not allowed to proceed, thus preventing out-of-order execution that could compromise data integrity.
How Fencing Tokens Work
When a process requests a lock from the distributed lock manager, it receives not just the permission to proceed but also a fencing token. This token is used in subsequent operations to validate that the action is being taken by the current lock holder. When there's a new lock holder, a new, higher-value token is issued, invalidating the older one.
Technical Scenario
Imagine multiple nodes editing a shared configuration file on a network. Node A obtains the lock with a token value of 101. While Node A is editing, Node B attempts to get the lock but must wait. Suppose Node A releases the lock and Node B acquires it, receiving a token value of 102. If Node A tries to write back its changes after Node B starts its session, the system will reject the write request from Node A even though it originally had the lock, due to the out-of-date token.
Implementation Example
Consider a distributed lock manager using Apache Zookeeper. Zookeeper allows nodes in distributed systems to coordinate with each other through a shared hierarchical namespace which is organized similarly to a filesystem.
In this example, lock.node might be used as a fencing token. Every time a lock is acquired, a different node under /locks/myapp/ is created (e.g., /locks/myapp/lock00000001), each with a unique, incrementing identifier. This identifier acts as a fencing token.
Security and Reliability Concerns
While distributed locks greatly enhance the coordination in distributed applications, they also introduce potential for deadlock and increased latency, especially in cases where network partitions occur. The choice of lock service and the implementation must be resilient to these network partitions and other failures.
| Feature | Description |
| Data Integrity | Ensures that no two processes can make concurrent modifications. |
| Consistency | Helps in maintaining a consistent state across distributed system nodes. |
| Reliability | If implemented well, can handle node failures and network partitions. |
| Performance | Can introduce latency and reduce throughput if not managed efficiently. |
| Complexity | Adds complexity to system operation, requiring careful management. |
Conclusion
Fencing tokens provide a robust way to handle distributed locks by ensuring that older tokens quickly become invalid as new lock holders take over. While implementing distributed locks, it is crucial to choose an appropriate locking mechanism and to be aware of the potential challenges and pitfalls, especially concerning the system's performance and reliability. This approach, when implemented correctly, safeguards your distributed applications from concurrent write pitfalls, maintaining data integrity and system consistency across your networked environment.

