resource = $resource; $this->deploymentConfig = $deploymentConfig; $this->prefix = $prefix; } /** * Sets a lock for name * * @param string $name lock name * @param int $timeout How long to wait lock acquisition in seconds, negative value means infinite timeout * @return bool * @throws InputException * @throws AlreadyExistsException * @throws \Zend_Db_Statement_Exception */ public function lock(string $name, int $timeout = -1): bool { if (!$this->deploymentConfig->isDbAvailable()) { return true; }; $name = $this->addPrefix($name); /** * Before MySQL 5.7.5, only a single simultaneous lock per connection can be acquired. * This limitation can be removed once MySQL minimum requirement has been raised, * currently we support MySQL 5.6 way only. */ if ($this->currentLock) { throw new AlreadyExistsException( new Phrase( 'Current connection is already holding lock for %1, only single lock allowed', [$this->currentLock] ) ); } $result = (bool)$this->resource->getConnection()->query( "SELECT GET_LOCK(?, ?);", [(string)$name, (int)$timeout] )->fetchColumn(); if ($result === true) { $this->currentLock = $name; } return $result; } /** * Releases a lock for name * * @param string $name lock name * @return bool * @throws InputException * @throws \Zend_Db_Statement_Exception */ public function unlock(string $name): bool { if (!$this->deploymentConfig->isDbAvailable()) { return true; }; $name = $this->addPrefix($name); $result = (bool)$this->resource->getConnection()->query( "SELECT RELEASE_LOCK(?);", [(string)$name] )->fetchColumn(); if ($result === true) { $this->currentLock = false; } return $result; } /** * Tests of lock is set for name * * @param string $name lock name * @return bool * @throws InputException * @throws \Zend_Db_Statement_Exception */ public function isLocked(string $name): bool { if (!$this->deploymentConfig->isDbAvailable()) { return false; }; $name = $this->addPrefix($name); return (bool)$this->resource->getConnection()->query( "SELECT IS_USED_LOCK(?);", [(string)$name] )->fetchColumn(); } /** * Adds prefix and checks for max length of lock name * * Limited to 64 characters in MySQL. * * @param string $name * @return string * @throws InputException */ private function addPrefix(string $name): string { $name = $this->getPrefix() . '|' . $name; if (strlen($name) > 64) { throw new InputException(new Phrase('Lock name too long: %1...', [substr($name, 0, 64)])); } return $name; } /** * Get installation specific lock prefix to avoid lock conflicts * * @return string lock prefix */ private function getPrefix(): string { if ($this->prefix === null) { $this->prefix = (string)$this->deploymentConfig->get( ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . '/' . ConfigOptionsListConstants::KEY_NAME, '' ); } return $this->prefix; } }