Repository: symfony/symfony
File: src/Symfony/Component/Semaphore/Store/LockStore.php (PHP)

Code before:
     */
    private function getExistingLocks(Key $key): array
    {
        if ($key->hasState(__CLASS__)) {
            return $key->getState(__CLASS__);
        }

        return [];
    }

    /**
     * @return array<LockInterface>
     */
    private function createLocks(Key $key, float $ttlInSecond): array
    {
        $locks = [];
        $lockName = base64_encode($key->__toString());
        $limit = $key->getLimit();

        // use a random start point to have a higher chance to catch a free slot directly
        $startPoint = rand(0, $limit - 1);

        for ($i = 0; $i < $limit; ++$i) {
            $index = ($startPoint + $i) % $limit;

            $lock = $this->lockFactory->createLock($lockName.'_'.$index, $ttlInSecond, false);
            if ($lock->acquire(false)) { // use lock if we can acquire it else try to catch next lock
                $locks[] = $lock;

                if (\count($locks) === $key->getWeight()) {
                    break;
                }

                continue;
            }

            $acquired = \count($locks);
            $required = $key->getWeight();
            $remaining = $key->getLimit() - $i;
            if (($acquired + $remaining) < $required) { // no chance to get enough locks
                break;
            }
        }

        if (\count($locks) !== $key->getWeight()) {
            // release already acquired locks because not got the amount of locks which were required
            $this->releaseLocks($locks, $key);

            throw new SemaphoreAcquiringException($key, 'There were no free locks found');
        }


Review comment (suggestion):
if you disable the `auto-release` feature, you should wrap this function in a try/catch to release locks in case of an exception (ie. network error)

Code after:

    /**
     * @return array<LockInterface>
     */
    private function getExistingLocks(Key $key): array
    {
        return $key->hasState(__CLASS__) ? $key->getState(__CLASS__) : [];
    }

    /**
     * @return array<LockInterface>
     */
    private function createLocks(Key $key, float $ttlInSecond): array
    {
        $locks = [];
        $lockName = (string) $key;
        $limit = $key->getLimit();

        // use a random start point to have a higher chance to catch a free slot directly
        $startPoint = random_int(0, $limit - 1);

        $previousException = null;
        try {
            for ($i = 0; $i < $limit; ++$i) {
                $index = ($startPoint + $i) % $limit;

                $lock = $this->lockFactory->createLock($lockName.'_'.$index, $ttlInSecond, false);
                if ($lock->acquire(false)) { // use lock if we can acquire it else try to catch next lock
                    $locks[] = $lock;

                    if (\count($locks) >= $key->getWeight()) {
                        break;
                    }

                    continue;
                }

                if (\count($locks) + $key->getLimit() - $i < $key->getWeight()) { // no chance to get enough locks
                    break;
                }
            }
        } catch (\Exception $e) {
            $previousException = $e;
        }

        if (\count($locks) === $key->getWeight()) {
            return $locks;
        }

        try {
            // release already acquired locks because not got the amount of locks which were required