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. Install the package:

    composer require symfony/lock
    

    For Laravel, use the symfony/lock facade or bind it in config/app.php:

    'aliases' => [
        'Lock' => Symfony\Component\Lock\LockFactory::class,
    ],
    
  2. Configure a store (e.g., Redis):

    use Symfony\Component\Lock\Store\RedisStore;
    use Symfony\Component\Lock\LockFactory;
    
    $store = new RedisStore(new \Redis());
    $factory = new LockFactory($store);
    
  3. First use case: Protect a critical section in a job or command:

    use Symfony\Component\Lock\LockFactory;
    
    public function handle()
    {
        $lock = $this->lockFactory->createLock('inventory_update_' . $this->orderId, 30);
        $acquired = $lock->acquire(true); // Block until acquired
    
        if ($acquired) {
            // Critical section: update inventory
            Inventory::decrement($this->productId);
            $lock->release(); // Release when done
        }
    }
    
  4. Laravel integration: Bind the factory in AppServiceProvider:

    public function register()
    {
        $this->app->singleton(LockFactory::class, function ($app) {
            $store = new RedisStore($app['redis']);
            return new LockFactory($store);
        });
    }
    

    Use the Lock facade in controllers/jobs:

    use Illuminate\Support\Facades\Lock;
    
    Lock::acquire('critical_section', 10); // Non-blocking
    

Implementation Patterns

Core Workflows

1. Job/Queue Protection

  • Pattern: Wrap job execution in a lock to prevent duplicates.
public function handle()
{
    $lock = Lock::createLock('job_' . $this->data['id'], 60);
    if ($lock->acquire()) {
        try {
            $this->processData();
        } finally {
            $lock->release();
        }
    }
}
  • Laravel Queue Tip: Use dispatchSync() with locks for fire-and-forget jobs requiring idempotency.

2. Database Transaction Coordination

  • Pattern: Combine locks with transactions for atomicity.
DB::transaction(function () use ($lock) {
    $lock->acquire(); // Acquire before DB ops
    // Transaction logic
    $lock->release();
});
  • PostgreSQL Note: Use PdoStore with LOCK TABLE for heavy DB workloads (see Gotchas).

3. Distributed Cache Synchronization

  • Pattern: Use Redis for low-latency locks across services.
$store = new RedisStore($redisClient, 'locks_');
$factory = new LockFactory($store);
  • TTL Strategy: Set TTLs based on expected job duration (e.g., 30 seconds for short jobs).

4. Command-Line Safety

  • Pattern: Protect Artisan commands from concurrent runs.
public function handle()
{
    $lock = Lock::createLock('command_' . $this->command, 3600);
    if (!$lock->acquire()) {
        throw new \RuntimeException('Command already running.');
    }
}

Integration Tips

Laravel-Specific

  • Service Container: Bind stores dynamically:

    $this->app->bind(RedisStore::class, function ($app) {
        return new RedisStore($app['redis'], 'laravel_locks_');
    });
    
  • Lock Factory: Extend for custom logic:

    class AppLockFactory extends LockFactory
    {
        public function createNamedLock(string $name, int $ttl = 30): LockInterface
        {
            return $this->createLock("app_{$name}", $ttl);
        }
    }
    

Multi-Store Strategies

  • Fallback Chain: Combine stores for resilience:

    $primary = new RedisStore($redis);
    $fallback = new FlockStore('/tmp/locks');
    $factory = new LockFactory(new ChainedStore([$primary, $fallback]));
    
  • Store-Specific Keys: Prefix keys to avoid collisions:

    $store = new RedisStore($redis, 'myapp_');
    

Event Listeners

  • Lock in Events: Use locks for idempotent listeners:
    public function handle(OrderPlaced $event)
    {
        $lock = Lock::createLock('order_' . $event->order->id, 120);
        if ($lock->acquire()) {
            // Process once
            $lock->release();
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Lock Leaks

    • Issue: Unreleased locks block resources indefinitely (e.g., crashes, timeouts).
    • Fix: Use try-finally or context managers:
      $lock = Lock::createLock('resource', 60);
      try {
          if ($lock->acquire()) {
              // Work
          }
      } finally {
          $lock->release(); // Always release
      }
      
    • Laravel Tip: Use Lock::release() in finally blocks or try-catch.
  2. PostgreSQL Transaction Contention

    • Issue: PdoStore with LOCK TABLE can abort outer transactions (fixed in v8.0.9+).
    • Workaround: Use SELECT FOR UPDATE instead:
      $store = new PdoStore($pdo, 'locks', 'SELECT * FROM locks WHERE name = :name FOR UPDATE');
      
  3. Redis Cluster Quirks

    • Issue: Redis Cluster may fail with evalSha (fixed in v7.2.6+).
    • Fix: Ensure Redis 6.2+ or use RedisStore with useEval: false:
      $store = new RedisStore($redis, null, null, false);
      
  4. Key Normalization

    • Issue: Custom keys may not serialize correctly (e.g., objects, resources).
    • Fix: Use LockKeyNormalizer:
      $normalizer = new LockKeyNormalizer();
      $key = $normalizer->normalize($customKey);
      
  5. Windows File Locks

    • Issue: FlockStore may fail on network drives.
    • Fix: Use RedisStore or PdoStore for cross-platform reliability.

Debugging

  1. Lock Timeouts

    • Debug: Check TTL vs. actual execution time:
      $lock = Lock::createLock('resource', 10); // 10-second TTL
      if (!$lock->acquire(true, 5)) { // Wait 5s
          throw new \RuntimeException('Lock acquisition timed out.');
      }
      
  2. Redis Errors

    • Debug: Enable Redis logging:
      $redis = new \Redis();
      $redis->connect('127.0.0.1', 6379, 0, null, 5.0, 2000, \Redis::OPT_SERIALIZER, \Redis::SERIALIZER_IGBINARY);
      $store = new RedisStore($redis);
      
  3. PostgreSQL Locks

    • Debug: Monitor pg_locks:
      SELECT * FROM pg_locks WHERE relation::regclass = 'locks';
      

Extension Points

  1. Custom Stores

    • Implement StoreInterface for new backends (e.g., DynamoDB):
      class DynamoStore implements StoreInterface
      {
          public function acquire($name, $ttl, $block = false, $timeout = null): bool
          {
              // DynamoDB logic
          }
          // ... other methods
      }
      
  2. Lock Strategies

    • Extend LockFactory for custom acquisition logic:
      class RetryLockFactory extends LockFactory
      {
          public function createLock(string $name, int $ttl = 30): LockInterface
          {
              return new RetryLock($this->store, $name, $ttl, 3); // Retry 3 times
          }
      }
      
  3. Event Listeners

    • Hook into lock events (e.g., LockAcquiredEvent):
      $factory->addListener(new class implements LockEvents {
          public function onLockAcquired(LockAcquiredEvent $event) {
              Log::info("Lock acquired: {$event->getLock()->getName()}");
          }
      });
      

Performance Tips

  1. TTL Tuning
    • Set TTLs based on workload:
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata