-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #153 from discoverygarden/feature/pgsql-locking
DGI9-584: Feature/pgsql locking
- Loading branch information
Showing
9 changed files
with
520 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
namespace Drupal\dgi_migrate\Attribute; | ||
|
||
use Drupal\Component\Plugin\Attribute\AttributeBase; | ||
|
||
/** | ||
* The dgi_migrate "locker" plugin attribute. | ||
*/ | ||
#[\Attribute(\Attribute::TARGET_CLASS)] | ||
final class Locker extends AttributeBase { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
namespace Drupal\dgi_migrate; | ||
|
||
use Drupal\Component\Plugin\FallbackPluginManagerInterface; | ||
use Drupal\Core\Cache\CacheBackendInterface; | ||
use Drupal\Core\Extension\ModuleHandlerInterface; | ||
use Drupal\Core\Plugin\DefaultPluginManager; | ||
use Drupal\dgi_migrate\Attribute\Locker; | ||
use Drupal\dgi_migrate\Plugin\dgi_migrate\locker\LockerInterface; | ||
|
||
/** | ||
* Locker plugin manager service. | ||
*/ | ||
final class LockerPluginManager extends DefaultPluginManager implements FallbackPluginManagerInterface, LockerPluginManagerInterface { | ||
|
||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct( | ||
\Traversable $namespaces, | ||
CacheBackendInterface $cacheBackend, | ||
ModuleHandlerInterface $module_handler, | ||
) { | ||
parent::__construct( | ||
'Plugin/dgi_migrate/locker', | ||
$namespaces, | ||
$module_handler, | ||
LockerInterface::class, | ||
Locker::class, | ||
); | ||
|
||
$this->alterInfo('dgi_migrate__locker_info'); | ||
$this->setCacheBackend($cacheBackend, 'dgi_migrate__locker_plugins'); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function getFallbackPluginId($plugin_id, array $configuration = []) : string { | ||
return 'flock'; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
namespace Drupal\dgi_migrate; | ||
|
||
use Drupal\Component\Plugin\PluginManagerInterface; | ||
|
||
/** | ||
* Locker plugin manager interface definition. | ||
*/ | ||
interface LockerPluginManagerInterface extends PluginManagerInterface { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<?php | ||
|
||
namespace Drupal\dgi_migrate\Plugin\dgi_migrate\locker; | ||
|
||
use Drupal\Core\File\FileSystemInterface; | ||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | ||
use Drupal\Core\Plugin\PluginBase; | ||
use Drupal\dgi_migrate\Attribute\Locker; | ||
use Drupal\dgi_migrate\Plugin\migrate\process\LockingMigrationLookup; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
/** | ||
* Base implementation using flock. | ||
*/ | ||
#[Locker('flock')] | ||
class Flock extends PluginBase implements LockerInterface, ContainerFactoryPluginInterface { | ||
|
||
/** | ||
* An array of SplFileObjects, to facilitate locking. | ||
* | ||
* @var \SplFileObject[] | ||
*/ | ||
protected array $lockFiles = []; | ||
|
||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct( | ||
array $configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
protected FileSystemInterface $fileSystem, | ||
) { | ||
parent::__construct($configuration, $plugin_id, $plugin_definition); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||
return new static( | ||
$configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
$container->get('file_system'), | ||
); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function acquireLock(string $name, int $mode = LOCK_EX, bool &$would_block = FALSE): bool { | ||
return $this->getLockFile($name)->flock($mode, $would_block); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function releaseLock(string $name): bool { | ||
return $this->getLockFile($name)->flock(LOCK_UN); | ||
} | ||
|
||
/** | ||
* Get an \SplFileObject instance to act as the lock. | ||
* | ||
* @param string $name | ||
* The name of the lock to acquire. Should result in a file being created | ||
* under the temporary:// scheme of the same name, against which `flock` | ||
* commands will be issued. | ||
* | ||
* @return \SplFileObject | ||
* The \SplFileObject instance against which to lock. | ||
*/ | ||
protected function getLockFile(string $name) : \SplFileObject { | ||
if (!isset($this->lockFiles[$name])) { | ||
$file_uri = "temporary://{$name}"; | ||
$directory = $this->fileSystem->dirname($file_uri); | ||
$basename = $this->fileSystem->basename($file_uri); | ||
$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); | ||
$file_uri = "{$directory}/{$basename}"; | ||
|
||
// XXX: Drupal's LocalStream wrappers presently have a bug in their | ||
// ::stream_lock() method which underlies flock()/\SplFileObject::flock(), | ||
// where they fail to properly report the lock status when non-blockingly | ||
// acquiring locks, so let's side-step the issue by referencing the real | ||
// file path directly. | ||
// | ||
// @see https://www.drupal.org/project/drupal/issues/3493632 | ||
// @see https://github.com/php/doc-en/issues/4299 | ||
$file_path = $this->fileSystem->realpath($file_uri); | ||
|
||
touch($file_path); | ||
$this->lockFiles[$name] = new \SplFileObject($file_path, 'a+'); | ||
} | ||
|
||
return $this->lockFiles[$name]; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function acquireControl(): bool { | ||
return $this->acquireLock(LockingMigrationLookup::CONTROL_LOCK); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function releaseControl(): bool { | ||
return $this->releaseLock(LockingMigrationLookup::CONTROL_LOCK); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
namespace Drupal\dgi_migrate\Plugin\dgi_migrate\locker; | ||
|
||
/** | ||
* Interface for locker plugins. | ||
* | ||
* Intended to be very similar to flock(), without being strictly bound to | ||
* files. | ||
*/ | ||
interface LockerInterface { | ||
|
||
/** | ||
* Acquire lock of given name. | ||
* | ||
* @param string $name | ||
* The name/ID to lock. | ||
* @param int $mode | ||
* The mode with which to lock, as a bit-field, expecting the use of the | ||
* LOCK_EX, LOCK_SH and LOCK_NB constants. | ||
* @param bool $would_block | ||
* If LOCK_NB was in $mode, flag if we failed to acquire the lock due to it | ||
* being held by another process. | ||
* | ||
* @return bool | ||
* TRUE if we acquired the lock; otherwise, FALSE. | ||
*/ | ||
public function acquireLock(string $name, int $mode = LOCK_EX, bool &$would_block = FALSE) : bool; | ||
|
||
/** | ||
* Release lock of the given name. | ||
* | ||
* @param string $name | ||
* The name/ID of the lock to release. | ||
* | ||
* @return bool | ||
* TRUE if we released the lock; otherwise, FALSE (if we did not hold the | ||
* given lock?). | ||
*/ | ||
public function releaseLock(string $name) : bool; | ||
|
||
/** | ||
* Acquire control lock. | ||
* | ||
* @return bool | ||
* TRUE if it was successfully acquired; otherwise, FALSE. | ||
*/ | ||
public function acquireControl() : bool; | ||
|
||
/** | ||
* Release control lock. | ||
* | ||
* @return bool | ||
* TRUE if it was successfully released; otherwise, FALSE. | ||
*/ | ||
public function releaseControl() : bool; | ||
|
||
} |
Oops, something went wrong.