Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lock Laravel Package

symfony/lock

Symfony Lock component provides a unified API to create and manage locks, ensuring exclusive access to shared resources. Supports multiple backends (e.g., filesystem, Redis, PDO) to prevent race conditions in concurrent PHP apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require symfony/lock
    

    For Laravel, use the symfony/lock facade directly or integrate with Laravel’s built-in Lock facade (v8.0+).

  2. Basic Usage:

    use Symfony\Component\Lock\LockFactory;
    use Symfony\Component\Lock\Store\FlockStore;
    
    // Create a lock factory with a FlockStore (file-based)
    $factory = new LockFactory(new FlockStore('/tmp/locks/'));
    
    // Acquire a lock with a 10-second TTL
    $lock = $factory->createLock('order_123', 10);
    $acquired = $lock->acquire(true); // Block until acquired
    
    if ($acquired) {
        // Critical section
        $lock->release(); // Release when done
    }
    
  3. Laravel Integration:

    use Illuminate\Support\Facades\Lock;
    
    // Use Laravel's built-in facade (v8.0+)
    Lock::blocking('order_123', 10, function () {
        // Critical section
    });
    

First Use Case: Queue Job Idempotency

use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore;

$factory = new LockFactory(new RedisStore('redis://localhost'));
$lock = $factory->createLock('process_payment_123', 300); // 5-minute TTL

if ($lock->acquire()) {
    try {
        // Process payment logic
    } finally {
        $lock->release();
    }
}

Implementation Patterns

Common Workflows

1. Blocking vs. Non-Blocking Locks

  • Blocking: Wait indefinitely or until a timeout.
    $lock->acquire(true); // Blocks until acquired
    $lock->acquire(true, 5); // Blocks for 5 seconds max
    
  • Non-Blocking: Return immediately if lock is unavailable.
    if ($lock->acquire(false)) {
        // Critical section
    }
    

2. Laravel Job Integration

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore;

class ProcessPayment implements ShouldQueue
{
    protected $factory;

    public function __construct()
    {
        $this->factory = new LockFactory(new RedisStore('redis://localhost'));
    }

    public function handle()
    {
        $lock = $this->factory->createLock('payment_' . $this->paymentId, 300);
        if ($lock->acquire()) {
            try {
                // Process payment
            } finally {
                $lock->release();
            }
        }
    }
}

3. Retry Logic with Locks

use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\PdoStore;

$factory = new LockFactory(new PdoStore($pdo, 'locks'));
$lock = $factory->createLock('retryable_job_456', 60);

if ($lock->acquire(false)) {
    // Proceed if lock acquired
} else {
    // Fallback or retry logic
    $this->retryAfterDelay();
}

4. Distributed Locking with Redis

use Symfony\Component\Lock\Store\RedisStore;

$store = new RedisStore('redis://redis-cluster:6379');
$factory = new LockFactory($store);

// Use with Laravel's Lock facade
Lock::extend('redis', function () use ($factory) {
    return $factory->createLock('key', 10);
});

Lock::blocking('redis', 'order_789', 10, function () {
    // Critical section
});

Integration Tips

Laravel Service Container

Register the lock factory as a singleton:

$app->singleton(LockFactory::class, function ($app) {
    return new LockFactory(new RedisStore($app['redis']->connection()));
});

Custom Stores

Extend AbstractStore for domain-specific needs:

use Symfony\Component\Lock\Store\AbstractStore;

class DynamoDbStore extends AbstractStore
{
    public function __construct(ClientInterface $client)
    {
        $this->client = $client;
    }

    protected function doAcquire($name, $ttl, $block, $timeout)
    {
        // Custom DynamoDB logic
    }

    // Implement other required methods
}

Lock Key Normalization

Use LockKeyNormalizer for consistent key generation:

use Symfony\Component\Lock\LockKeyNormalizer;

$normalizer = new LockKeyNormalizer();
$normalizedKey = $normalizer->normalize('user:123:profile');

Gotchas and Tips

Pitfalls

  1. Lock Leaks:

    • Issue: Unreleased locks due to uncaught exceptions or long-running processes.
    • Fix: Use finally blocks or Laravel’s Lock::blocking() with automatic release.
    • Example:
      Lock::blocking('key', 10, function () {
          // Automatically released even if exception occurs
      });
      
  2. Network Partitions:

    • Issue: Redis/DB locks may become stale if the lock holder crashes.
    • Fix: Set appropriate TTLs (e.g., 5–10x the expected critical section duration).
  3. Key Collisions:

    • Issue: Different keys normalizing to the same value (e.g., user:123 vs. user/123).
    • Fix: Use LockKeyNormalizer for consistent key generation.
  4. PostgreSQL Transaction Contention:

    • Issue: Locks may abort outer transactions (fixed in v8.0.9+).
    • Fix: Upgrade to the latest version or use PdoStore with LOCK TABLE syntax.
  5. Redis Cluster Limitations:

    • Issue: RedisStore may fail in clusters if the lock key is not hash-tagged.
    • Fix: Use {sha}:key format or enable Redis Cluster support in the store.

Debugging

  1. Check Lock Status:

    if (!$lock->isAcquired()) {
        log("Lock not acquired for key: {$lock->getName()}");
    }
    
  2. Inspect Store State:

    • For RedisStore, use redis-cli to check keys:
      redis-cli keys '*lock*'
      
    • For PdoStore, query the locks table directly.
  3. Log Lock Operations:

    $factory->createLock('key', 10)->acquire(true, 5, function ($waited) {
        log("Acquired lock after {$waited}ms");
    });
    

Configuration Quirks

  1. FlockStore Paths:

    • Ensure the lock directory is writable and exists:
      $store = new FlockStore('/var/lock/app');
      if (!file_exists($store->getDirectory())) {
          mkdir($store->getDirectory(), 0755, true);
      }
      
  2. Redis Connection:

    • Use connection pooling or failover configurations:
      $store = new RedisStore('redis://default,redis://backup');
      
  3. PDO Store Schema:

    • For PdoStore, ensure the locks table exists:
      CREATE TABLE locks (
          name VARCHAR(255) PRIMARY KEY,
          expires_at DATETIME NOT NULL
      );
      

Extension Points

  1. Custom Stores:

    • Implement StoreInterface for domain-specific backends (e.g., etcd, consul).
    • Example:
      class EtcdStore implements StoreInterface
      {
          public function acquire($name, $ttl, $block, $timeout, callable $callback)
          {
              // Custom etcd logic
          }
          // Implement other required methods
      }
      
  2. Lock Factory Extensions:

    • Extend LockFactory to add domain-specific lock creation:
      class DomainLockFactory extends LockFactory
      {
          public function createOrderLock($orderId, $ttl = 300)
          {
              return $this->createLock("order:{$orderId}", $ttl);
          }
      }
      
  3. Event Listeners:

    • Listen for lock events (e.g., LockAcquiredEvent):
      $factory->createLock('key')->acquire(true, 5, function ($waited) {
          event(new LockAcquiredEvent($this->getName(), $waited));
      });
      
  4. Laravel Events:

    • Integrate with Laravel’s event system:
      Lock::blocking('key', 10, function () {
          event(new OrderProcessed($order));
      });
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai