Skip to content

Omkar-Ghongade/Locking

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Locking Strategies: Optimistic vs Pessimistic

A production-grade system design implementation demonstrating both optimistic and pessimistic locking strategies with comprehensive tests, benchmarks, and REST API.

Architecture Overview

src/
├── domain/                    # Core business logic & interfaces
│   ├── entities/              # Domain models (Product, etc.)
│   ├── locking/              # Lock manager interfaces
│   ├── repositories/         # Repository interfaces
│   └── exceptions/           # Domain exceptions
├── infrastructure/           # Implementation details
│   ├── locking/              # Lock manager implementations
│   └── repositories/         # Repository implementations
├── application/             # Use cases & orchestration
│   └── ProductService.ts     # Business logic
├── api/                     # HTTP layer
│   └── routes/              # Express routes
├── benchmark/               # Performance benchmarking
└── __tests__/              # Test suites

When to Use Which Lock

Scenario Recommendation Reason
Low contention, frequent reads Optimistic Low overhead, no blocking
High contention, frequent writes Pessimistic Guarantees consistency
Long-running operations Pessimistic Prevents stale reads
Short, quick operations Optimistic Less overhead
Financial transactions Pessimistic Zero tolerance for conflicts
UI form updates Optimistic User can retry on conflict

API Endpoints

Products

# Create product
POST /api/products
{
  "name": "iPhone 15",
  "description": "Latest iPhone",
  "price": 999,
  "quantity": 100
}

# Get product
GET /api/products/:id

# List all products
GET /api/products

# Update with optimistic lock
PUT /api/products/:id?version=1&lockType=OPTIMISTIC
{
  "name": "Updated Name"
}

# Update with pessimistic lock
PUT /api/products/:id?lockType=PESSIMISTIC

# Purchase with optimistic locking
POST /api/products/:id/purchase/optimistic
{
  "quantity": 2,
  "version": 1,
  "ownerId": "user-123"
}

# Purchase with pessimistic locking
POST /api/products/:id/purchase/pessimistic
{
  "quantity": 2,
  "ownerId": "user-123"
}

# Restock
POST /api/products/:id/restock
{
  "quantity": 50,
  "version": 1,
  "lockType": "OPTIMISTIC"
}

Running the Project

# Install dependencies
npm install

# Build
npm run build

# Run server
npm start

# Run tests
npm test

# Run with coverage
npm run test:coverage

# Run benchmarks
npm run benchmark

Key Design Decisions

1. Version-Based Optimistic Locking

Each product has a version field that increments on every update. Client must provide expected version - if mismatched, conflict detected.

2. Token-Based Pessimistic Locking

Lock tokens provide exclusive access. Other requesters wait in queue until lock released.

3. Retry Logic for Optimistic

Configurable retry with exponential backoff for optimistic lock failures.

4. Hybrid Approach Available

ProductService supports both strategies, choose per-operation based on use case.

System Design Principles Demonstrated

  • Separation of Concerns: Domain, Infrastructure, Application layers
  • Interface Segregation: Clear abstractions for lock managers
  • Dependency Injection: Easy to swap implementations
  • Error Handling: Specific exception types for different failure modes
  • Observability: Benchmarking and testing built-in
  • Extensibility: Easy to add new lock strategies or persistence

Benchmark Report

Executive Summary

This project implements a production-grade comparison of Optimistic Locking vs Pessimistic Locking strategies through practical implementation and comprehensive benchmarking.

Test Configuration

Parameter Value
Total Operations 100
Concurrency Level 10
Lock Timeout 5,000 ms
Max Retries (Optimistic) 5
Retry Delay 50 ms

Results

Scenario 1: Low Contention (Sequential Operations)

Metric Optimistic Pessimistic
Successful 100 100
Failed 0 0
Conflicts 0 0
Throughput 100,000 ops/sec N/A*

*Pessimistic has overhead from lock acquisition in sequential scenario.

Verdict: Both strategies perform excellently. Optimistic slightly better due to no lock overhead.


Scenario 2: High Contention (10 Concurrent Users, 1 Resource)

Metric Optimistic Pessimistic
Successful 10 100
Failed 90 0
Conflicts 90 0
Total Time 5,056 ms 3 ms
Throughput 1.98 ops/sec 33,333 ops/sec

Verdict: Pessimistic wins decisively - 16,800x better throughput in high contention.


Scenario 3: Multiple Products (10 Products, Distributed Contention)

Metric Optimistic Pessimistic
Successful 67 100
Failed 33 0
Conflicts 33 0
Total Time 3,025 ms 2 ms
Throughput 22 ops/sec 50,000 ops/sec

Verdict: Pessimistic wins - 2,272x better throughput even with distributed contention.


Key Findings

When Optimistic Locking Excels

  • Low contention scenarios - Near-zero overhead
  • Read-heavy workloads - No locks needed for reads
  • Web interfaces - Users can retry on conflict
  • Microservices - Reduces inter-service coordination

When Pessimistic Locking Excels

  • High contention - Guarantees first-come-first-served
  • Financial transactions - Zero tolerance for lost updates
  • Inventory management - Prevents overselling
  • Resource allocation - Ensures deterministic behavior

Recommendations

Decision Matrix

Use Case Recommended Strategy Rationale
E-commerce checkout Pessimistic Prevent overselling during flash sales
User profile update Optimistic Low conflict, user can retry
Inventory sync Pessimistic High contention, consistency critical
Analytics dashboard Optimistic Mostly reads, occasional writes
Payment processing Pessimistic Zero tolerance for failures
Form submission Optimistic User-mediated retry acceptable

Hybrid Approach (Production Recommended)

// Use both strategies based on operation type
class HybridProductService {
  async getProduct(id) {
    // Read operations: Optimistic (no lock needed)
    return this.repository.findById(id);
  }

  async purchaseCritical(productId, qty) {
    // Critical writes: Pessimistic
    const lock = await this.pessimisticLock.acquire(productId);
    try {
      return await this.repository.decrement(productId, qty);
    } finally {
      await this.pessimisticLock.release(lock);
    }
  }

  async updateMetadata(productId, data) {
    // Non-critical writes: Optimistic with retry
    return this.optimisticUpdate(productId, data);
  }
}

Architecture Strengths

Implemented Patterns

  1. Repository Pattern - Abstracts data access
  2. Strategy Pattern - Swappable lock implementations
  3. Factory Pattern - Easy lock manager creation
  4. Unit of Work - Transaction-like operations

Code Quality Metrics

  • ✅ 33 unit tests passing
  • ✅ 100% test coverage on core logic
  • ✅ Type-safe throughout
  • ✅ Clean layered architecture
  • ✅ Comprehensive error handling

Production Considerations

For Optimistic Locking

  • Monitor conflict rates - if >10%, consider pessimistic
  • Implement exponential backoff for retries
  • Add version field to all entities
  • Log conflicts for analysis

For Pessimistic Locking

  • Set appropriate timeout values
  • Implement dead lock detection
  • Use connection pooling
  • Consider fair queuing for waiters

Scaling Recommendations

  • Redis for distributed locks
  • Database row-level locks for persistence
  • Circuit breaker for lock service failures
  • Metrics on lock wait times

Conclusion

Both locking strategies have their place in modern systems:

  • Optimistic Locking: Best for scalability, low contention, and user-facing operations where retry is acceptable.

  • Pessimistic Locking: Best for correctness-critical operations, high contention scenarios, and when deterministic ordering matters.

The benchmark demonstrates that choosing the wrong strategy can result in 16,800x performance degradation in high-contention scenarios.

Final Recommendation: Implement both strategies and choose per-operation based on the specific use case's contention profile.

About

A production-grade implementation comparing Optimistic vs Pessimistic locking strategies with REST API, benchmarks, and 33 passing tests. Demonstrates how the wrong lock strategy can result in 16,800x performance difference in high-contention scenarios.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors