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(),
Configure Redis (config/packages/snc_redis.yaml):
snc_redis:
clients:
semaphore:
type: predis
alias: semaphore
dsn: redis://localhost/2
Configure Semaphore (config/packages/avtonom_semaphore.yaml):
avtonom_semaphore:
adapter_redis_client: snc_redis.semaphore
key_storage_class: App\Semaphore\KeyStorage
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';
}
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__);
}
}
Lock Acquisition:
$lockKey = [$storage::KEY, $uniqueIdentifier];
$this->lockAcquire($lockKey, __METHOD__, $ttl); // TTL in seconds
__METHOD__) as context for debugging.Lock Release:
$this->lockRelease($lockKey, __METHOD__);
acquire/release in finally blocks or use context managers.Key Storage:
KeyStorage) for maintainability.[KEY, userId, resourceId]).Dependency Injection:
public function __construct(private SemaphoreManagerInterface $semaphore) {}
avtonom_semaphore.manager service directly for granular control.$this->lockAcquire([KeyStorage::EVENT_KEY, $eventName], __METHOD__);
// Process event
$this->lockRelease([KeyStorage::EVENT_KEY, $eventName], __METHOD__);
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__);
}
}
$lockKey = [KeyStorage::ENTITY_KEY, $entity->getId()];
$this->lockAcquire($lockKey, __METHOD__);
$entityManager->persist($entity);
$entityManager->flush();
$this->lockRelease($lockKey, __METHOD__);
Nested Locks:
[PARENT_KEY, CHILD_KEY]) for nested operations.Lock Timeouts:
$this->lockAcquire($lockKey, __METHOD__, $dynamicTtl);
Lock Validation:
if ($this->lockValidate($lockKey, __METHOD__)) {
$this->lockRelease($lockKey, __METHOD__);
}
Deadlocks:
Zombie Locks:
release calls.try_count and sleep_time to limit wait time (default: 240 attempts × 0.5s = 120s).mode: demo) for testing without side effects.Key Collisions:
[KEY, $id] where $id is reused).[KEY, $id, $timestamp]).Adapter Failures:
Logging Overhead:
semaphore channel to a dedicated handler (e.g., file or syslog).Check Logs:
%kernel.logs_dir%/%kernel.environment%.semaphore.log.acquire, release, and expire events.Demo Mode:
mode: demo in config to test without modifying locks:
avtonom_semaphore:
mode: demo
Lock Inspection:
redis-cli KEYS "lock_*" # Check active locks
redis-cli GET "lock_my_key"
Timeout Handling:
SemaphoreException for failed acquisitions:
try {
$this->lockAcquire($lockKey, __METHOD__);
} catch (\Avtonom\SemaphoreBundle\Exception\SemaphoreException $e) {
// Handle timeout or deadlock
}
Prefix:
lock_. Customize in parameters.yaml:
avtonom_semaphore.prefix: 'myapp_'
TTL:
max_lock_time (default: 60s) is the hard limit. Override per lock if needed.Adapter-Specific:
snc_redis bundle is properly configured.Custom Adapters:
Avtonom\SemaphoreBundle\Adapter\AbstractAdapter for new backends (e.g., SQL).Key Storage:
SemaphoreKeyStorageInterface for project-specific keys.Manager Overrides:
SemaphoreManager with a custom class by configuring manager_class.Logging:
use Monolog\Logger;
$logger->pushHandler(new CustomSemaphoreHandler());
Minimize Lock Scope:
Avoid Nested Locks:
TTL Tuning:
Bulk Operations:
$this->lockAcquire([KeyStorage::PARENT_KEY], __METHOD__);
// Acquire child locks...
$this->lockRelease([KeyStorage::PARENT_KEY], __METHOD__);
How can I help you explore Laravel packages today?