src/PessimisticLock.php
public function lock(string $id, bool $blocking = true, int $limit = 300): void
    {
        $this->autoCreateIndexes && $this->createIndexes();

        if ($limit > $this->limit) {
            throw new \LogicException('The limit could not be greater than a default one. The default is used to set an expiration index');
        }

        $timeout = time() + $limit; // I think it must be a bit greater then mongos index ttl so there is a way to process data.

        while (time() < $timeout) {
            try {
                $result = $this->collection->insertOne([
                    'id' => $id,
                    'timestamp' => new UTCDatetime(time() * 1000),
                    'sessionId' => $this->sessionId,
                ]);

                if (false == $result->isAcknowledged()) {
                    throw new \LogicException(sprintf('Cannot obtain the lock for id %s. The insertOne operation is not acknowledged.', $id));
                }

                return;
            } catch (BulkWriteException $e) {
            } catch (DuplicateKeyException $e) {
                // The lock is obtained by another process. Let's try again later.
            }

            if (false == $blocking) {
                throw PessimisticLockException::failedObtainLock($id, $limit);
            }

            // Mongo does database lock level on insert, so everything has to wait even reads.
            // I decided to do it rarely to decrease global lock rate.
            // We will have at least 150 attempts to get the lock, pretty enough IMO.
            // More here http://docs.mongodb.org/manual/faq/concurrency/
            usleep(200000);
        }

        throw PessimisticLockException::failedObtainLock($id, $limit);
    }
public function lock(string $id, bool $blocking = true, int $limit = 300): void
    {
        $this->autoCreateIndexes && $this->createIndexes();

        if ($limit > $this->limit) {
            throw new \LogicException('The limit could not be greater than a default one. The default is used to set an expiration index');
        }

        $timeout = time() + $limit; // I think it must be a bit greater then mongos index ttl so there is a way to process data.

        while (time() < $timeout) {
            try {
                $result = $this->collection->insertOne([
                    'id' => $id,
                    'timestamp' => new UTCDatetime(time() * 1000),
                    'sessionId' => $this->sessionId,
                ]);

                if (false == $result->isAcknowledged()) {
                    throw new \LogicException(sprintf('Cannot obtain the lock for id %s. The insertOne operation is not acknowledged.', $id));
                }

                return;
            } catch (BulkWriteException $e) {
            } catch (DuplicateKeyException $e) {
                // The lock is obtained by another process. Let's try again later.
            }

            if (false == $blocking) {
                throw PessimisticLockException::failedObtainLock($id, $limit);
            }

            // Mongo does database lock level on insert, so everything has to wait even reads.
            // I decided to do it rarely to decrease global lock rate.
            // We will have at least 150 attempts to get the lock, pretty enough IMO.
            // More here http://docs.mongodb.org/manual/faq/concurrency/
            usleep(200000);
        }

        throw PessimisticLockException::failedObtainLock($id, $limit);
    }
public function lock ( string $ id , bool $ blocking = true , int $ limit = 300 ) : void { $ this -> autoCreateIndexes && $ this -> createIndexes ( ) ; if ( $ limit > $ this -> limit ) { throw new \ LogicException ( 'The limit could not be greater than a default one. The default is used to set an expiration index' ) ; } $ timeout = time ( ) + $ limit ; // I think it must be a bit greater then mongos index ttl so there is a way to process data. while ( time ( ) < $ timeout ) { try { $ result = $ this -> collection -> insertOne ( [ 'id' => $ id , 'timestamp' => new UTCDatetime ( time ( ) * 1000 ) , 'sessionId' => $ this -> sessionId , ] ) ; if ( false == $ result -> isAcknowledged ( ) ) { throw new \ LogicException ( sprintf ( 'Cannot obtain the lock for id %s. The insertOne operation is not acknowledged.' , $ id ) ) ; } return ; } catch ( BulkWriteException $ e ) { } catch ( DuplicateKeyException $ e ) { // The lock is obtained by another process. Let's try again later. } if ( false == $ blocking ) { throw PessimisticLockException :: failedObtainLock ( $ id , $ limit ) ; } // Mongo does database lock level on insert, so everything has to wait even reads. // I decided to do it rarely to decrease global lock rate. // We will have at least 150 attempts to get the lock, pretty enough IMO. // More here http://docs.mongodb.org/manual/faq/concurrency/ usleep ( 200000 ) ; } throw PessimisticLockException :: failedObtainLock ( $ id , $ limit ) ; }
Limit is in seconds

@param string $id
@param bool $blocking
@param int $limit is ignored if blocking is false.
Limit is in seconds