master

laravel/framework

Last updated at: 29/12/2023 09:20

DatabaseLock.php

TLDR

This file, DatabaseLock.php, is part of the Illuminate\Cache namespace and defines a class called DatabaseLock that extends the Lock class. It is responsible for acquiring, releasing, and managing locks using a database connection.

Methods

__construct(Connection $connection, $table, $name, $seconds, $owner = null, $lottery = [2, 100], $defaultTimeoutInSeconds = 86400)

This method is the constructor for the DatabaseLock class. It initializes the instance variables of the class such as the database connection, table name, lock name, timeout duration in seconds, lock owner, and lottery odds. The Connection class is a dependency and should be provided for proper instantiation.

acquire(): bool

This method attempts to acquire the lock. It first tries to insert a new lock record into the database table. If a QueryException is caught, it means that a lock record with the same name already exists in the table. In this case, the method updates the existing lock record with the current owner and expiration time. If the update process is successful, the lock is considered acquired. The method also deletes expired lock records from the table based on the provided lottery odds. It returns a boolean value indicating whether the lock was acquired or not.

expiresAt(): int

This protected method returns a UNIX timestamp indicating when the lock should expire. It calculates the expiration time based on the lock duration ($this->seconds) set during object instantiation. If no duration was set ($this->seconds is 0), it uses the default timeout duration ($this->defaultTimeoutInSeconds) provided during object instantiation.

release(): bool

This method releases the lock by deleting the lock record from the database table. It checks if the lock is owned by the current process before performing the delete operation. It returns a boolean value indicating whether the release was successful or not.

forceRelease(): void

This method forcefully releases the lock by deleting the lock record from the database table without checking ownership.

getCurrentOwner(): string

This protected method retrieves the current lock owner value written into the database for this lock. It queries the lock record from the database table based on the lock name and returns the owner value.

getConnectionName(): string

This method returns the name of the database connection being used to manage the lock.

Classes

The DatabaseLock class does not have any additional classes defined within it.

<?php

namespace Illuminate\Cache;

use Illuminate\Database\Connection;
use Illuminate\Database\QueryException;

class DatabaseLock extends Lock
{
    /**
     * The database connection instance.
     *
     * @var \Illuminate\Database\Connection
     */
    protected $connection;

    /**
     * The database table name.
     *
     * @var string
     */
    protected $table;

    /**
     * The prune probability odds.
     *
     * @var array
     */
    protected $lottery;

    /**
     * The default number of seconds that a lock should be held.
     *
     * @var int
     */
    protected $defaultTimeoutInSeconds;

    /**
     * Create a new lock instance.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  string  $table
     * @param  string  $name
     * @param  int  $seconds
     * @param  string|null  $owner
     * @param  array  $lottery
     * @return void
     */
    public function __construct(Connection $connection, $table, $name, $seconds, $owner = null, $lottery = [2, 100], $defaultTimeoutInSeconds = 86400)
    {
        parent::__construct($name, $seconds, $owner);

        $this->connection = $connection;
        $this->table = $table;
        $this->lottery = $lottery;
        $this->defaultTimeoutInSeconds = $defaultTimeoutInSeconds;
    }

    /**
     * Attempt to acquire the lock.
     *
     * @return bool
     */
    public function acquire()
    {
        try {
            $this->connection->table($this->table)->insert([
                'key' => $this->name,
                'owner' => $this->owner,
                'expiration' => $this->expiresAt(),
            ]);

            $acquired = true;
        } catch (QueryException) {
            $updated = $this->connection->table($this->table)
                ->where('key', $this->name)
                ->where(function ($query) {
                    return $query->where('owner', $this->owner)->orWhere('expiration', '<=', time());
                })->update([
                    'owner' => $this->owner,
                    'expiration' => $this->expiresAt(),
                ]);

            $acquired = $updated >= 1;
        }

        if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
            $this->connection->table($this->table)->where('expiration', '<=', time())->delete();
        }

        return $acquired;
    }

    /**
     * Get the UNIX timestamp indicating when the lock should expire.
     *
     * @return int
     */
    protected function expiresAt()
    {
        $lockTimeout = $this->seconds > 0 ? $this->seconds : $this->defaultTimeoutInSeconds;

        return time() + $lockTimeout;
    }

    /**
     * Release the lock.
     *
     * @return bool
     */
    public function release()
    {
        if ($this->isOwnedByCurrentProcess()) {
            $this->connection->table($this->table)
                        ->where('key', $this->name)
                        ->where('owner', $this->owner)
                        ->delete();

            return true;
        }

        return false;
    }

    /**
     * Releases this lock in disregard of ownership.
     *
     * @return void
     */
    public function forceRelease()
    {
        $this->connection->table($this->table)
                    ->where('key', $this->name)
                    ->delete();
    }

    /**
     * Returns the owner value written into the driver for this lock.
     *
     * @return string
     */
    protected function getCurrentOwner()
    {
        return optional($this->connection->table($this->table)->where('key', $this->name)->first())->owner;
    }

    /**
     * Get the name of the database connection being used to manage the lock.
     *
     * @return string
     */
    public function getConnectionName()
    {
        return $this->connection->getName();
    }
}