Web Hosting

Tanpa judul

Race condition is a common concurrency issue that occurs when multiple processes or requests access and modify shared data at the same time. In web applications, this problem often appears in features such as balance deduction, stock updates, or limited-quantity purchases.

This tutorial explains how to test race conditions and shows PHP code examples before and after handling race conditions using proper techniques.

What Is a Race Condition

A race condition happens when the outcome of an operation depends on the timing of multiple concurrent executions. Without proper synchronization, data can become inconsistent or corrupted.

Common Real-World Examples

  • Double spending on wallet balance
  • Overselling limited stock
  • Duplicate order creation
  • Multiple password reset tokens generated

Race Condition Flow Overview


flowchart TD
    A[Multiple Requests] --> B[Read Same Data]
    B --> C[Process Logic]
    C --> D[Write Data]
    D --> E[Inconsistent Result]

Example Scenario: Wallet Balance Deduction

Assume a user has a balance of 100. Two requests attempt to deduct 80 simultaneously.

Code Example Before Handling Race Condition

The following code looks correct at first glance, but it is vulnerable to race conditions because there is no locking or transaction protection.


$user = getUserById($userId);

if ($user['balance'] >= 80) {
    $newBalance = $user['balance'] - 80;
    updateBalance($userId, $newBalance);
}

If two requests read the balance at the same time, both will pass the condition and deduct the balance, resulting in a negative or incorrect value.

How to Test Race Conditions

Using Multiple Parallel Requests

You can simulate race conditions by sending concurrent HTTP requests to the same endpoint.


for i in {1..5}; do
  curl -X POST http://localhost/deduct-balance &
done
wait

This command sends multiple requests at nearly the same time, increasing the chance of triggering a race condition.

Indicators That a Race Condition Exists

  • Negative balances
  • Duplicate transactions
  • Stock values below zero
  • Inconsistent database records

Handling Race Condition with Database Transactions

Using Transaction and Row Locking

One of the most reliable ways to prevent race conditions is to use database transactions combined with row-level locking.


$pdo->beginTransaction();

$stmt = $pdo->prepare(
    "SELECT balance FROM users WHERE id = ? FOR UPDATE"
);
$stmt->execute([$userId]);

$balance = $stmt->fetchColumn();

if ($balance >= 80) {
    $stmt = $pdo->prepare(
        "UPDATE users SET balance = balance - 80 WHERE id = ?"
    );
    $stmt->execute([$userId]);
}

$pdo->commit();

The FOR UPDATE clause ensures that other transactions must wait until the current one finishes before accessing the same row.

Alternative Approach: Atomic Update

Another effective method is to let the database handle validation and update in a single atomic query.


$stmt = $pdo->prepare(
    "UPDATE users
     SET balance = balance - 80
     WHERE id = ? AND balance >= 80"
);

$stmt->execute([$userId]);

if ($stmt->rowCount() === 0) {
    throw new Exception("Insufficient balance");
}

This approach avoids race conditions by ensuring the condition and update are executed as one operation.

Race Condition Flow After Handling


flowchart TD
    A[Concurrent Requests] --> B[Row Lock or Atomic Query]
    B --> C[Single Valid Update]
    C --> D[Consistent Data]

Best Practices to Prevent Race Conditions

  • Always use database transactions for critical updates
  • Prefer atomic SQL queries over read-modify-write logic
  • Never rely solely on application-level checks
  • Test concurrency scenarios during development

Conclusion

Race conditions are subtle but dangerous bugs that can cause serious data integrity issues. By testing with concurrent requests and applying proper handling techniques such as transactions, row locking, or atomic updates, PHP applications can safely handle high-concurrency scenarios.

Understanding and addressing race conditions early will significantly improve the reliability and security of your application.

Rendi Julianto

Experienced programming developer with a passion for creating efficient, scalable solutions. Proficient in Python, JavaScript, and PHP, with expertise in web development, API integration, and software optimization. Adept at problem-solving and committed to delivering high-quality, user-centric applications.

Posting Komentar (0)
Lebih baru Lebih lama