]> git.mxchange.org Git - friendica.git/blobdiff - src/Core/Lock/Type/DatabaseLock.php
Merge pull request #11520 from annando/display-polls
[friendica.git] / src / Core / Lock / Type / DatabaseLock.php
index de44e2e2b9286d2368d2e6cbeeaa41a3ae1ee632..094fa79d0bea41e2044af8a47728c34cb528cb0b 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @copyright Copyright (C) 2010-2021, the Friendica project
+ * @copyright Copyright (C) 2010-2022, the Friendica project
  *
  * @license GNU AGPL version 3 or any later version
  *
@@ -23,13 +23,14 @@ namespace Friendica\Core\Lock\Type;
 
 use Friendica\Core\Cache\Enum\Duration;
 use Friendica\Core\Lock\Enum\Type;
+use Friendica\Core\Lock\Exception\LockPersistenceException;
 use Friendica\Database\Database;
 use Friendica\Util\DateTimeFormat;
 
 /**
  * Locking driver that stores the locks in the database
  */
-class DatabaseLock extends BaseLock
+class DatabaseLock extends AbstractLock
 {
        /**
         * The current ID of the process
@@ -44,49 +45,63 @@ class DatabaseLock extends BaseLock
        private $dba;
 
        /**
-        * @param null|int $pid The Id of the current process (null means determine automatically)
+        * @param int|null $pid The id of the current process (null means determine automatically)
         */
-       public function __construct(Database $dba, $pid = null)
+       public function __construct(Database $dba, ?int $pid = null)
        {
                $this->dba = $dba;
-               $this->pid = isset($pid) ? $pid : getmypid();
+               $this->pid = $pid ?? getmypid();
        }
 
        /**
         * (@inheritdoc)
         */
-       public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES)
+       public function acquire(string $key, int $timeout = 120, int $ttl = Duration::FIVE_MINUTES): bool
        {
                $got_lock = false;
                $start    = time();
 
-               do {
-                       $this->dba->lock('locks');
-                       $lock = $this->dba->selectFirst('locks', ['locked', 'pid'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
-
-                       if ($this->dba->isResult($lock)) {
-                               if ($lock['locked']) {
-                                       // We want to lock something that was already locked by us? So we got the lock.
-                                       if ($lock['pid'] == $this->pid) {
+               try {
+                       do {
+                               $this->dba->lock('locks');
+                               $lock = $this->dba->selectFirst('locks', ['locked', 'pid'], [
+                                       '`name` = ? AND `expires` >= ?', $key,DateTimeFormat::utcNow()
+                               ]);
+
+                               if ($this->dba->isResult($lock)) {
+                                       if ($lock['locked']) {
+                                               // We want to lock something that was already locked by us? So we got the lock.
+                                               if ($lock['pid'] == $this->pid) {
+                                                       $got_lock = true;
+                                               }
+                                       }
+                                       if (!$lock['locked']) {
+                                               $this->dba->update('locks', [
+                                                       'locked'  => true,
+                                                       'pid'     => $this->pid,
+                                                       'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')
+                                               ], ['name' => $key]);
                                                $got_lock = true;
                                        }
-                               }
-                               if (!$lock['locked']) {
-                                       $this->dba->update('locks', ['locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]);
+                               } else {
+                                       $this->dba->insert('locks', [
+                                               'name'    => $key,
+                                               'locked'  => true,
+                                               'pid'     => $this->pid,
+                                               'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]);
                                        $got_lock = true;
+                                       $this->markAcquire($key);
                                }
-                       } else {
-                               $this->dba->insert('locks', ['name' => $key, 'locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]);
-                               $got_lock = true;
-                               $this->markAcquire($key);
-                       }
 
-                       $this->dba->unlock();
+                               $this->dba->unlock();
 
-                       if (!$got_lock && ($timeout > 0)) {
-                               usleep(rand(100000, 2000000));
-                       }
-               } while (!$got_lock && ((time() - $start) < $timeout));
+                               if (!$got_lock && ($timeout > 0)) {
+                                       usleep(rand(100000, 2000000));
+                               }
+                       } while (!$got_lock && ((time() - $start) < $timeout));
+               } catch (\Exception $exception) {
+                       throw new LockPersistenceException(sprintf('Cannot acquire lock for key %s', $key), $exception);
+               }
 
                return $got_lock;
        }
@@ -94,7 +109,7 @@ class DatabaseLock extends BaseLock
        /**
         * (@inheritdoc)
         */
-       public function release($key, $override = false)
+       public function release(string $key, bool $override = false): bool
        {
                if ($override) {
                        $where = ['name' => $key];
@@ -102,10 +117,14 @@ class DatabaseLock extends BaseLock
                        $where = ['name' => $key, 'pid' => $this->pid];
                }
 
-               if ($this->dba->exists('locks', $where)) {
-                       $return = $this->dba->delete('locks', $where);
-               } else {
-                       $return = false;
+               try {
+                       if ($this->dba->exists('locks', $where)) {
+                               $return = $this->dba->delete('locks', $where);
+                       } else {
+                               $return = false;
+                       }
+               } catch (\Exception $exception) {
+                       throw new LockPersistenceException(sprintf('Cannot release lock for key %s (override %b)', $key, $override), $exception);
                }
 
                $this->markRelease($key);
@@ -116,7 +135,7 @@ class DatabaseLock extends BaseLock
        /**
         * (@inheritdoc)
         */
-       public function releaseAll($override = false)
+       public function releaseAll(bool $override = false): bool
        {
                $success = parent::releaseAll($override);
 
@@ -125,7 +144,12 @@ class DatabaseLock extends BaseLock
                } else {
                        $where = ['pid' => $this->pid];
                }
-               $return = $this->dba->delete('locks', $where);
+
+               try {
+                       $return = $this->dba->delete('locks', $where);
+               } catch (\Exception $exception) {
+                       throw new LockPersistenceException(sprintf('Cannot release all lock (override %b)', $override), $exception);
+               }
 
                $this->acquiredLocks = [];
 
@@ -135,9 +159,14 @@ class DatabaseLock extends BaseLock
        /**
         * (@inheritdoc)
         */
-       public function isLocked($key)
+       public function isLocked(string $key): bool
        {
-               $lock = $this->dba->selectFirst('locks', ['locked'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
+               try {
+                       $lock = $this->dba->selectFirst('locks', ['locked'], [
+                               '`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
+               } catch (\Exception $exception) {
+                       throw new LockPersistenceException(sprintf('Cannot check lock state for key %s', $key), $exception);
+               }
 
                if ($this->dba->isResult($lock)) {
                        return $lock['locked'] !== false;
@@ -149,7 +178,7 @@ class DatabaseLock extends BaseLock
        /**
         * {@inheritDoc}
         */
-       public function getName()
+       public function getName(): string
        {
                return Type::DATABASE;
        }
@@ -157,21 +186,26 @@ class DatabaseLock extends BaseLock
        /**
         * {@inheritDoc}
         */
-       public function getLocks(string $prefix = '')
+       public function getLocks(string $prefix = ''): array
        {
-               if (empty($prefix)) {
-                       $where = ['`expires` >= ?', DateTimeFormat::utcNow()];
-               } else {
-                       $where = ['`expires` >= ? AND `name` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
-               }
+               try {
+                       if (empty($prefix)) {
+                               $where = ['`expires` >= ?', DateTimeFormat::utcNow()];
+                       } else {
+                               $where = ['`expires` >= ? AND `name` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
+                       }
 
-               $stmt = $this->dba->select('locks', ['name'], $where);
+                       $stmt = $this->dba->select('locks', ['name'], $where);
 
-               $keys = [];
-               while ($key = $this->dba->fetch($stmt)) {
-                       array_push($keys, $key['name']);
+                       $keys = [];
+                       while ($key = $this->dba->fetch($stmt)) {
+                               array_push($keys, $key['name']);
+                       }
+               } catch (\Exception $exception) {
+                       throw new LockPersistenceException(sprintf('Cannot get lock with prefix %s', $prefix), $exception);
+               } finally {
+                       $this->dba->close($stmt);
                }
-               $this->dba->close($stmt);
 
                return $keys;
        }