Concurrency Control

Back

Loading concept...

NoSQL Transactions: Concurrency Control

The Busy Kitchen Story

Imagine a super busy restaurant kitchen. Many cooks are working at the same time. They all need to grab ingredients, use the stove, and plate dishes. But what happens when two cooks reach for the last tomato at the same time? Chaos!

That’s exactly what happens in databases when many users try to change the same data at once. We call this concurrency — multiple things happening together.

Today, we’ll learn how NoSQL databases keep the kitchen running smoothly, even when it’s packed with cooks!


1. Optimistic Concurrency

The “Hope for the Best” Approach

Think of it like this: You’re coloring a picture in a coloring book. Instead of asking your sister “Can I use this page?”, you just start coloring. You hope she won’t try to color the same page.

How it works:

  1. You read the data (pick up the coloring page)
  2. You make your changes (start coloring)
  3. Before saving, you check: “Did anyone else change this while I was working?”
  4. If yes → Start over. If no → Save your work!
graph TD A["Read Data + Version"] --> B["Make Changes"] B --> C{Version Still Same?} C -->|Yes| D["Save Changes!"] C -->|No| E["Someone Changed It!"] E --> F["Try Again"] F --> A

Real Example

// You want to update a user's score
let user = db.get("player123")
// user = {name: "Alex", score: 100, version: 5}

// You add 10 points
user.score = 110

// Try to save, but only if version is still 5
db.update("player123", user, {
  ifVersion: 5
})
// If someone else changed it, version would be 6
// Your save would FAIL — and that's good!

Why use it? It’s fast! You don’t lock anything. Most of the time, nobody else is changing the same data anyway.


2. Conflict Resolution Strategies

When Two Cooks Grab the Same Tomato

Sometimes, despite our best hopes, two people DO change the same thing. Now what? We need rules to decide who wins!

Strategy 1: Last Write Wins (LWW)

The simplest rule: whoever saves last, wins.

Like two kids drawing on the same whiteboard. The last drawing is what stays.

// 10:00 AM - Alice sets price to $10
product.price = 10

// 10:01 AM - Bob sets price to $15
product.price = 15

// Result: Price is $15 (Bob wrote last)

Downside: Alice’s change is lost forever!

Strategy 2: Merge Changes

Instead of picking a winner, combine both changes!

// Alice adds "milk" to shopping list
// Bob adds "eggs" to shopping list

// MERGE result:
shoppingList = ["milk", "eggs"]
// Both items are kept!

Strategy 3: Ask the User

When the database can’t decide, ask the human!

"Oops! Someone else changed this too."
"Your version: $10"
"Their version: $15"
"Which one should we keep?"

Strategy 4: Custom Rules

You make your own rules based on your app’s needs.

// Rule: Always keep the HIGHER score
if (aliceScore > bobScore) {
  keep(aliceScore)
} else {
  keep(bobScore)
}

3. Data Versioning

The Magic Sticker System

Remember how we checked “is the version still the same?” Let’s understand versions better!

Think of it like putting a sticker number on every change. Each time something changes, you add a new sticker with a higher number.

Version 1: {name: "Sam", age: 8}     // Created
Version 2: {name: "Sam", age: 9}     // Birthday!
Version 3: {name: "Sammy", age: 9}   // Nickname update

Types of Version Markers

Simple Number (Counter)

{
  data: {name: "Luna", color: "purple"},
  version: 42  // Just a number that goes up
}

Timestamp

{
  data: {name: "Luna", color: "purple"},
  updatedAt: "2024-01-15T10:30:00Z"  // When it changed
}

Vector Clock (for distributed systems)

{
  data: {name: "Luna"},
  vectorClock: {
    serverA: 3,  // Server A made 3 changes
    serverB: 2   // Server B made 2 changes
  }
}

Why Versioning Matters

graph TD A["You Read: Version 5"] --> B["You Edit"] B --> C["Try to Save"] C --> D{Current Version?} D -->|Still 5| E["Save as Version 6"] D -->|Now 6| F["CONFLICT!"] F --> G["Re-read and Try Again"]

Without versions, you’d overwrite other people’s work without knowing!


4. Idempotent Operations

The “Safe to Repeat” Trick

Big word alert! Idempotent means: doing something twice gives the same result as doing it once.

Everyday Examples

Idempotent (Safe to repeat):

  • Turning ON a light switch (already on? Still on!)
  • Setting your age to 10 (do it 100 times, still 10)
  • Deleting a file (delete twice? Still gone)

NOT Idempotent (Changes each time):

  • Adding $5 to your piggy bank (do it twice = $10!)
  • Liking a post (like twice = unlike!)

In Database World

// NOT idempotent - dangerous!
user.balance = user.balance + 10
// Run twice = added $20 instead of $10!

// IDEMPOTENT - safe!
user.balance = 110  // Set to exact value
// Run twice = still $110

Making Operations Safe

Bad (not idempotent):

db.update({
  $increment: {views: 1}  // Add 1 to views
})
// Network glitch? Might run twice!

Good (idempotent):

db.update({
  $addToSet: {
    viewedBy: "user123"  // Add user to list
  }
})
// Run 100 times, user only appears once!

The Request ID Trick

// Give each request a unique ID
const requestId = "abc-123-xyz"

// Before processing, check:
if (alreadyProcessed(requestId)) {
  return previousResult  // Don't do it again!
}

// Process and remember this ID
processRequest()
markAsProcessed(requestId)

5. Saga Pattern

The Adventure Story Approach

Imagine you’re planning a birthday party. You need to:

  1. Book the venue
  2. Order the cake
  3. Hire a magician
  4. Send invitations

What if the magician cancels? Do you cancel EVERYTHING? That’s expensive! Instead, you handle each problem step by step.

Saga is exactly this: a series of steps where each step can be undone if something goes wrong.

graph TD A["Book Venue"] -->|Success| B["Order Cake"] B -->|Success| C["Hire Magician"] C -->|FAILS!| D["Cancel Cake"] D --> E["Cancel Venue"] E --> F["Party Cancelled"] C -->|Success| G["Send Invites"] G --> H["Party Ready!"]

Real Example: Online Shopping

// The Saga for buying something online:

// Step 1: Reserve the item
reserveItem("toy-123")

// Step 2: Charge the credit card
try {
  chargeCard("$25")
} catch (error) {
  // UNDO Step 1!
  unreserveItem("toy-123")
  return "Payment failed"
}

// Step 3: Ship the package
try {
  shipPackage()
} catch (error) {
  // UNDO Step 2!
  refundCard("$25")
  // UNDO Step 1!
  unreserveItem("toy-123")
  return "Shipping failed"
}

return "Order complete!"

Two Types of Sagas

Choreography (Everyone knows their part)

Each service watches for events and reacts:
- Inventory sees "order placed" → reserves item
- Payment sees "item reserved" → charges card
- Shipping sees "payment done" → ships package

Orchestration (One boss gives orders)

A central controller tells everyone what to do:
- Controller: "Inventory, reserve item!"
- Controller: "Payment, charge card!"
- Controller: "Shipping, send package!"

Compensating Actions

Every forward step needs a “rewind” step:

Forward Action Compensating Action
Reserve item Release item
Charge card Refund card
Ship package Request return
Send email Send “oops” email

Summary: Keeping the Kitchen Running

Concept Simple Explanation
Optimistic Concurrency Hope for the best, check before saving
Conflict Resolution Rules for when two changes collide
Data Versioning Sticker numbers to track changes
Idempotent Operations Safe to accidentally repeat
Saga Pattern Step-by-step with undo buttons

The Big Picture

graph TD A["Many Users"] --> B["Same Data"] B --> C{Conflict?} C -->|Prevent| D["Optimistic Concurrency"] C -->|Detect| E["Data Versioning"] C -->|Resolve| F["Conflict Resolution"] D --> G["Safe Updates"] E --> G F --> G G --> H["Idempotent Ops"] H --> I["Saga Pattern"] I --> J["Happy Database!"]

Now you’re ready to handle a busy database kitchen like a pro chef! Remember: it’s all about checking before you save, having rules for conflicts, tracking versions, making operations safe to repeat, and having an undo plan.

You’ve got this!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.