Open In App

Strict consistency or Linearizability in System Design

Strong consistency (strict consistency) provides the strongest level of consistency. It ensures that all operations appear to execute atomically and in a globally agreed-upon order. Reads always return the most recent write. Strict consistency, also known as linearizability, is a consistency model in distributed systems that provides the illusion of executing operations atomically and sequentially. In other words, it ensures that the system’s behavior appears as if all operations happened in a linear order, even though they may have been executed concurrently.

Strict Consistency

Under linearizability: Each operation has an associated point in time called its linearization point. The linearization point of an operation is a moment in time when the operation appears to take effect atomically, without any interference from other concurrent operations. This model guarantees that the system behaves as if all operations occurred in a sequential and consistent manner.



Different Approaches to Achieving Strict Consistency aka Linearizability:

Achieving strict consistency or linearizability in distributed systems can be challenging. Here are a few common approaches:

(a) Distributed Locking:

One approach is to use distributed locks, such as ZooKeeper or Consul, to enforce mutual exclusion on shared resources. By acquiring locks before performing operations, you can ensure that operations appear to execute atomically and sequentially, maintaining strict consistency.



(b) Two-Phase Commit (2PC):

The two-phase commit protocol is a coordination mechanism used to ensure atomicity and consistency across multiple distributed nodes. It involves a coordinator and multiple participants. The coordinator proposes a transaction to all participants, and if all participants agree, the coordinator commits the transaction. If any participant disagrees or fails, the coordinator aborts the transaction. 2PC can be used to enforce strict consistency in distributed systems.

(c) Distributed Data Store with Consensus:

Using a distributed data store that provides consensus algorithms, such as Apache Cassandra or Google Spanner, can ensure strict consistency. These systems replicate data across multiple nodes and use consensus algorithms like Paxos or Raft to ensure that all operations are linearizable.

How locks can be used to ensure linearizability within a single process

Example: Implementing strict consistency requires a complex distributed system design. Below is a simplified code example in Node.js to demonstrate how locks can be used to ensure linearizability within a single process:




// This line imports the Lock class
// from the 'distributed-lock'
// library.
const { Lock } = require('distributed-lock');
 
// This is a class declaration named BankAccount. It has a
// constructor that takes an initial balance as a parameter
// and initializes the balance property with the provided
// value. It also creates a new instance of the Lock class
// and assigns it to the lock property.
class BankAccount {
    constructor(initialBalance)
    {
        this.balance = initialBalance;
        this.lock = new Lock();
    }
 
    async deposit(amount)
    {
        await this.lock.acquire();
        try {
            const currentBalance = this.balance;
            // Simulating some processing time
            await sleep(200);
            this.balance = currentBalance + amount;
        }
        finally {
            this.lock.release();
        }
    }
 
    async withdraw(amount)
    {
        await this.lock.acquire();
        try {
            const currentBalance = this.balance;
            // Simulating some processing time
            await sleep(200);
            if (currentBalance >= amount) {
                this.balance = currentBalance - amount;
                return true;
            }
            return false;
        }
        finally {
            this.lock.release();
        }
    }
}
 
// This is an asynchronous function testBankAccount.
// It creates a new instance of BankAccount with an
// initial balance of 100. It then uses Promise.all
// to concurrently execute multiple operations: a
// deposit of 50, a withdrawal of 70, and another
// deposit of 30. Finally, it logs the final balance
// of the account object.
async function
testBankAccount()
{
    const account = new BankAccount(100);
    await Promise.all([
        account.deposit(50),
        account.withdraw(70),
        account.deposit(30),
    ]);
 
    console.log('Final balance:',
                account.balance); // Expected output:

Explanation:


Article Tags :