Prevent Race Condition in Symfony – Best Practices

Race conditions can quietly erode data integrity and security in Symfony apps. Here’s how to prevent them effectively using Symfony components and Doctrine's locking mechanisms.


๐Ÿ” What’s a Race Condition?

A race condition occurs when multiple processes read and write shared resources simultaneously without synchronization — leading to unpredictable and inconsistent outcomes. In web apps, this often happens when users trigger duplicate requests before previous ones finish.

Prevent Race Condition in Symfony – Best Practices

1. Use Symfony Lock Component

Symfony’s Lock component ensures only one process enters a critical section at a time.

Installation

composer require symfony/lock

Basic Usage

use Symfony\Component\Lock\LockFactory;
$lock = $lockFactory->createLock('cart_add_'.$userId);
if (!$lock->acquire()) {
    // another process is in progress
    return;
}

// critical section: add to cart
$cartService->addItem($userId, $productId);
$lock->release();

This prevents simultaneous cart modifications — ideal for actions like “Add to Cart” races.

Configuring Storage

In config/packages/lock.yaml, configure persistent stores:

framework:
  lock:
    - 'redis://localhost'
    - 'flock:///var/lock/app'

Supports flock, semaphore, Redis, MySQL, ZooKeeper, etc.


2. Doctrine Locking Strategies

Use Doctrine’s locking for entity consistency during concurrent updates.

Optimistic Locking

Add a version field to your entity:

#[ORM\Version, ORM\Column(type:'integer')]
private int $version;

On flush(), Doctrine checks the version and throws OptimisticLockException on mismatch.

Example

try {
    $em->flush();
} catch (\Doctrine\ORM\OptimisticLockException $e) {
    // reload entity, reapply changes, retry or notify user
}

Pessimistic Locking

Lock the entity directly in the database:

$em->getConnection()->beginTransaction();
$user = $em->find(User::class, $id, LockMode::PESSIMISTIC_WRITE);
// now safe to update
$em->flush();
$em->getConnection()->commit();

This uses SQL SELECT … FOR UPDATE, blocking other transactions.

When to Use Which?

Scenario     Optimistic Pessimistic
Rare conflicts     ✅    ❌ (overhead)
Frequent conflicts or high consistency required     ❌    ✅

3. Session-Level Locking (When Storing Sessions in DB)

If sessions are stored in DB, use lock_mode: LOCK_TRANSACTIONAL to avoid session race conditions under concurrent requests:

framework:
  session:
    handler_id: 'session.handler.pdo'
    cookie_secure: auto
    cookie_samesite: lax
    lock_mode: LOCK_TRANSACTIONAL

๐Ÿงช Code-Driven Example: Prevent Duplicate Order Processing

// src/Controller/OrderController.php

use Symfony\Component\Lock\LockFactory;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\DBAL\LockMode;

public function placeOrder(Request $request, LockFactory $lockFactory, EntityManagerInterface $em) {
    $userId = $this->getUser()->getId();
    $lock = $lockFactory->createLock('order_place_'.$userId);

    if (!$lock->acquire()) {
        return new JsonResponse(['error' => 'Order in progress'], 429);
    }

    $em->getConnection()->beginTransaction();
    try {
        $account = $em->find(Account::class, $userId, LockMode::PESSIMISTIC_WRITE);
        // check balance and adjust
        $account->withdraw($amount);
        $em->flush();
        $em->getConnection()->commit();
    } catch (\Throwable $e) {
        $em->getConnection()->rollBack();
        throw $e;
    } finally {
        $lock->release();
    }

    return new JsonResponse(['success' => true]);
}

This guards both the app and DB layer from duplicates.


๐Ÿงฉ Your Free Security Tool

Use our Website Vulnerability Scanner to validate your defenses.
Here’s a screenshot of the tool’s interface:

Screenshot of the free tools webpage where you can access security assessment tools.
Screenshot of the free tools webpage where you can access security assessment tools.

And a sample vulnerability assessment report generated by our tool to check Website Vulnerability:

An Example of a vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.
An Example of a vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.

Explore our blog for more best practices: Pentest Testing Corp.


๐Ÿ’ผ Additional Web Security Services


Follow & Subscribe

Stay updated on security insights: Subscribe on LinkedIn


✅ Final Tips

  1. Lock at the right level (application vs. DB).

  2. Track versions for long-lived entities.

  3. Choose between optimistic and pessimistic based on contention.

  4. Don’t forget persistent lock stores (Redis, DB, semaphore).

  5. Test with concurrency, not just unit tests.

Race conditions are sneaky but manageable. Using Symfony Lock + Doctrine safeguards, you can maintain consistency, security, and performance.

Comments

Popular posts from this blog

Fix Sensitive Data Exposure in Symfony Apps

Open Redirect Vulnerability in Symfony

Prevent Remote Code Execution (RCE) Vulnerabilities in Symfony