Weave 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

Resource Lock Bundle Laravel Package

aboutcoders/resource-lock-bundle

Symfony bundle providing a resource locking system backed by ORM. Configure a default lock manager and optional custom managers with prefixes, then fetch lock manager services from the container to set, get, and check locks across your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require aboutcoders/resource-lock-bundle:dev-master
    

    Add the bundle to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):

    Abc\Bundle\ResourceLockBundle\AbcResourceLockBundle::class => ['all' => true],
    
  2. Basic Configuration: Add to config/packages/aboutcoders_resource_lock.yaml (Symfony 4+):

    abc_resource_lock:
        db_driver: orm  # or 'pdo' for raw PDO
    
  3. First Use Case: Lock a resource (e.g., inventory item) in a service:

    use Abc\Bundle\ResourceLockBundle\Lock\LockManagerInterface;
    
    class InventoryService {
        public function __construct(private LockManagerInterface $lockManager) {}
    
        public function updateInventory(string $itemId, int $quantity) {
            $lock = $this->lockManager->getLock('inventory_' . $itemId);
            if (!$lock->isLocked()) {
                $lock->lock(); // Acquire lock
            }
    
            // Critical section: Update inventory
            $this->update($itemId, $quantity);
    
            $lock->release(); // Release lock
        }
    }
    

Implementation Patterns

Core Workflows

  1. Locking Strategies:

    • Exclusive Locks: Use for write operations (e.g., inventory updates).
      $lock = $lockManager->getLock('resource_key');
      $lock->lock(); // Blocks until lock is acquired
      
    • Shared Locks: Use for read-heavy operations (if supported by the driver).
      $lock = $lockManager->getLock('resource_key', Lock::SHARED);
      
  2. Lock Scopes:

    • Global Locks: Use a generic prefix (e.g., inventory_).
    • Custom Managers: Define scoped locks via config:
      abc_resource_lock:
          managers:
              inventory:
                  prefix: 'inventory_'
      
      Access via:
      $inventoryLockManager = $container->get('abc.resource_lock.lock_manager_inventory');
      
  3. Lock Expiration:

    • Set TTL (Time-To-Live) for automatic release:
      $lock = $lockManager->getLock('resource_key', Lock::TTL_30); // 30-second lock
      
  4. Retry Logic:

    • Implement exponential backoff for failed lock attempts:
      $attempts = 0;
      $maxAttempts = 3;
      while (!$lock->isLocked() && $attempts < $maxAttempts) {
          $lock->lock();
          $attempts++;
          if (!$lock->isLocked()) {
              sleep(2 ** $attempts); // Exponential delay
          }
      }
      
  5. Integration with Doctrine:

    • Use db_driver: orm to leverage Doctrine for lock storage.
    • Example: Lock a Product entity:
      $lock = $lockManager->getLock('product_' . $product->getId());
      

Common Use Cases

  • Concurrent Order Processing: Lock order IDs to prevent duplicate processing.
  • Rate Limiting: Lock user IDs to enforce API rate limits.
  • Batch Jobs: Lock queues to avoid parallel execution of the same job.

Gotchas and Tips

Pitfalls

  1. Deadlocks:

    • Always acquire locks in a consistent order (e.g., alphabetical by resource ID).
    • Avoid nested locks (e.g., locking A then B in one method, and B then A in another).
  2. Lock Leaks:

    • Ensure locks are always released (use try-finally or dependency injection for cleanup):
      try {
          $lock->lock();
          // Critical section
      } finally {
          $lock->release();
      }
      
    • For Symfony services, use onKernelTerminate to release locks if the request fails.
  3. Driver Limitations:

    • PDO Driver: Less portable than ORM; ensure transactions are handled manually.
    • ORM Driver: Requires Doctrine; may cause performance issues with high lock contention.
  4. Configuration Quirks:

    • If using db_driver: orm, ensure your Doctrine entity manager is configured.
    • Custom managers must be defined in config; dynamic creation isn’t supported.
  5. Lock Timeouts:

    • Default TTL is infinite. Set explicit TTLs to avoid stale locks:
      abc_resource_lock:
          default_ttl: 300  # 5 minutes
      

Debugging Tips

  1. Check Lock Status:

    if ($lock->isLocked()) {
        echo "Locked by: " . $lock->getOwner(); // If owner tracking is enabled
    }
    
  2. Log Lock Operations: Add a decorator to the LockManagerInterface to log lock attempts:

    $lockManager->getLock('key')->lock(); // Logs to monolog
    
  3. Test Lock Contention: Use PHPUnit to simulate concurrent requests:

    $this->parallel()->with([1, 2, 3])->run(function ($i) {
        $lock = $lockManager->getLock('test_lock');
        $lock->lock();
        // Assert lock is held
    });
    

Extension Points

  1. Custom Lock Storage: Extend the LockStorageInterface to support Redis, Memcached, etc.:

    class RedisLockStorage implements LockStorageInterface {
        // Implement lock logic using Redis
    }
    
  2. Event Listeners: Trigger events on lock acquisition/release:

    # config/services.yaml
    services:
        App\EventListener\LockListener:
            tags:
                - { name: kernel.event_listener, event: abc.resource_lock.lock_acquired, method: onLockAcquired }
    
  3. Lock Metadata: Store additional metadata (e.g., user ID, timestamp) by extending the Lock class:

    class ExtendedLock extends Lock {
        protected $metadata;
    
        public function setMetadata(array $metadata) {
            $this->metadata = $metadata;
        }
    }
    
  4. Fallback Mechanisms: Implement a fallback to a simpler lock (e.g., file-based) if the DB driver fails:

    try {
        $lockManager->getLock('key')->lock();
    } catch (LockException $e) {
        file_put_contents('/tmp/fallback_lock', 'locked');
    }
    
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.
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support