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

Semaphore Bundle Laravel Package

avtonom/semaphore-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require avtonom/semaphore-bundle ^1.4
    composer require snc/redis-bundle  # Required for Redis adapter
    

    Add bundles to AppKernel.php:

    new Avtonom\SemaphoreBundle\AvtonomSemaphoreBundle(),
    new Snc\RedisBundle\SncRedisBundle(),
    
  2. Configure Redis (config/packages/snc_redis.yaml):

    snc_redis:
        clients:
            semaphore:
                type: predis
                alias: semaphore
                dsn: redis://localhost/2
    
  3. Configure Semaphore (config/packages/avtonom_semaphore.yaml):

    avtonom_semaphore:
        adapter_redis_client: snc_redis.semaphore
        key_storage_class: App\Semaphore\KeyStorage
    
  4. Define Key Storage (src/Semaphore/KeyStorage.php):

    namespace App\Semaphore;
    use Avtonom\SemaphoreBundle\Model\SemaphoreKeyStorageInterface;
    
    class KeyStorage implements SemaphoreKeyStorageInterface {
        const MY_LOCK = 'my_lock_key';
    }
    
  5. First Use Case:

    use Avtonom\SemaphoreBundle\Traits\SemaphoreTrait;
    
    class MyService {
        use SemaphoreTrait;
    
        public function criticalOperation($param) {
            $lockKey = [KeyStorage::MY_LOCK, $param];
            $this->lockAcquire($lockKey, __METHOD__, 10); // 10s TTL
    
            // Thread-safe code here
    
            $this->lockRelease($lockKey, __METHOD__);
        }
    }
    

Implementation Patterns

Core Workflows

  1. Lock Acquisition:

    $lockKey = [$storage::KEY, $uniqueIdentifier];
    $this->lockAcquire($lockKey, __METHOD__, $ttl); // TTL in seconds
    
    • Best Practice: Use method name (__METHOD__) as context for debugging.
    • TTL: Override default (60s) for long-running tasks.
  2. Lock Release:

    $this->lockRelease($lockKey, __METHOD__);
    
    • Always pair acquire/release in finally blocks or use context managers.
  3. Key Storage:

    • Centralize keys in a dedicated class (e.g., KeyStorage) for maintainability.
    • Combine keys with dynamic data (e.g., [KEY, userId, resourceId]).
  4. Dependency Injection:

    public function __construct(private SemaphoreManagerInterface $semaphore) {}
    
    • Inject avtonom_semaphore.manager service directly for granular control.

Integration Tips

  • Symfony Events: Use locks in event subscribers/listeners:
    $this->lockAcquire([KeyStorage::EVENT_KEY, $eventName], __METHOD__);
    // Process event
    $this->lockRelease([KeyStorage::EVENT_KEY, $eventName], __METHOD__);
    
  • Commands: Protect CLI commands from concurrent execution:
    use Symfony\Component\Console\Command\Command;
    use Avtonom\SemaphoreBundle\Traits\SemaphoreTrait;
    
    class MyCommand extends Command {
        use SemaphoreTrait;
    
        protected function execute(InputInterface $input, OutputInterface $output) {
            $this->lockAcquire([KeyStorage::COMMAND_KEY, $this->getName()], __METHOD__);
            // Command logic
            $this->lockRelease([KeyStorage::COMMAND_KEY, $this->getName()], __METHOD__);
        }
    }
    
  • Doctrine Entities: Use locks for atomic operations:
    $lockKey = [KeyStorage::ENTITY_KEY, $entity->getId()];
    $this->lockAcquire($lockKey, __METHOD__);
    $entityManager->persist($entity);
    $entityManager->flush();
    $this->lockRelease($lockKey, __METHOD__);
    

Advanced Patterns

  1. Nested Locks:

    • Use hierarchical keys (e.g., [PARENT_KEY, CHILD_KEY]) for nested operations.
    • Warning: Avoid deep nesting to prevent deadlocks.
  2. Lock Timeouts:

    • Extend TTL dynamically:
      $this->lockAcquire($lockKey, __METHOD__, $dynamicTtl);
      
  3. Lock Validation:

    • Check lock status before release:
      if ($this->lockValidate($lockKey, __METHOD__)) {
          $this->lockRelease($lockKey, __METHOD__);
      }
      

Gotchas and Tips

Pitfalls

  1. Deadlocks:

    • Cause: Circular dependencies in lock acquisition (e.g., Lock A → Lock B → Lock A).
    • Fix: Acquire locks in a consistent order (e.g., alphabetical by key).
  2. Zombie Locks:

    • Cause: Unreleased locks due to crashes or forgotten release calls.
    • Mitigation:
      • Use try_count and sleep_time to limit wait time (default: 240 attempts × 0.5s = 120s).
      • Enable demo mode (mode: demo) for testing without side effects.
  3. Key Collisions:

    • Cause: Overlapping keys (e.g., [KEY, $id] where $id is reused).
    • Fix: Include unique context (e.g., [KEY, $id, $timestamp]).
  4. Adapter Failures:

    • Redis/Memcached: Network issues or server restarts may cause locks to disappear.
    • Fix: Implement a fallback adapter or retry logic.
  5. Logging Overhead:

    • Cause: Excessive logging in production.
    • Fix: Configure Monolog to route semaphore channel to a dedicated handler (e.g., file or syslog).

Debugging Tips

  1. Check Logs:

    • Logs are written to %kernel.logs_dir%/%kernel.environment%.semaphore.log.
    • Look for acquire, release, and expire events.
  2. Demo Mode:

    • Enable mode: demo in config to test without modifying locks:
      avtonom_semaphore:
          mode: demo
      
  3. Lock Inspection:

    • Use Redis CLI to inspect keys:
      redis-cli KEYS "lock_*"  # Check active locks
      redis-cli GET "lock_my_key"
      
  4. Timeout Handling:

    • Catch SemaphoreException for failed acquisitions:
      try {
          $this->lockAcquire($lockKey, __METHOD__);
      } catch (\Avtonom\SemaphoreBundle\Exception\SemaphoreException $e) {
          // Handle timeout or deadlock
      }
      

Configuration Quirks

  1. Prefix:

    • Default prefix is lock_. Customize in parameters.yaml:
      avtonom_semaphore.prefix: 'myapp_'
      
  2. TTL:

    • max_lock_time (default: 60s) is the hard limit. Override per lock if needed.
  3. Adapter-Specific:

    • Redis: Ensure snc_redis bundle is properly configured.
    • Flock: Filesystem locks may fail on network-mounted drives.

Extension Points

  1. Custom Adapters:

    • Extend Avtonom\SemaphoreBundle\Adapter\AbstractAdapter for new backends (e.g., SQL).
  2. Key Storage:

    • Implement SemaphoreKeyStorageInterface for project-specific keys.
  3. Manager Overrides:

    • Replace SemaphoreManager with a custom class by configuring manager_class.
  4. Logging:

    • Extend Monolog handler for custom lock events:
      use Monolog\Logger;
      $logger->pushHandler(new CustomSemaphoreHandler());
      

Performance Tips

  1. Minimize Lock Scope:

    • Acquire/release locks as close as possible to the critical section.
  2. Avoid Nested Locks:

    • Prefer flat lock hierarchies to reduce deadlock risk.
  3. TTL Tuning:

    • Set TTL based on expected operation duration (e.g., 5s for fast operations, 300s for long tasks).
  4. Bulk Operations:

    • For multiple related locks, use a parent lock to serialize access:
      $this->lockAcquire([KeyStorage::PARENT_KEY], __METHOD__);
      // Acquire child locks...
      $this->lockRelease([KeyStorage::PARENT_KEY], __METHOD__);
      
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor