ReplicaKillSwitch.php in Drupal 10
Same filename and directory in other branches
Namespace
Drupal\Core\DatabaseFile
core/lib/Drupal/Core/Database/ReplicaKillSwitch.phpView source
<?php
namespace Drupal\Core\Database;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Site\Settings;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Provides replica server kill switch to ignore it.
*/
class ReplicaKillSwitch implements EventSubscriberInterface {
/**
* The settings object.
*
* @var \Drupal\Core\Site\Settings
*/
protected $settings;
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* The session.
*
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
protected $session;
/**
* Constructs a ReplicaKillSwitch object.
*
* @param \Drupal\Core\Site\Settings $settings
* The settings object.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* The session.
*/
public function __construct(Settings $settings, TimeInterface $time, SessionInterface $session) {
$this->settings = $settings;
$this->time = $time;
$this->session = $session;
}
/**
* Denies access to replica database on the current request.
*
* @see https://www.drupal.org/node/2286193
*/
public function trigger() {
$connection_info = Database::getConnectionInfo();
// Only set ignore_replica_server if there are replica servers being used,
// which is assumed if there are more than one.
if (count($connection_info) > 1) {
// Five minutes is long enough to allow the replica to break and resume
// interrupted replication without causing problems on the Drupal site
// from the old data.
$duration = $this->settings
->get('maximum_replication_lag', 300);
// Set session variable with amount of time to delay before using replica.
$this->session
->set('ignore_replica_server', $this->time
->getRequestTime() + $duration);
}
}
/**
* Checks and disables the replica database server if appropriate.
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* The Event to process.
*/
public function checkReplicaServer(RequestEvent $event) {
// Ignore replica database servers for this request.
//
// In Drupal's distributed database structure, new data is written to the
// master and then propagated to the replica servers. This means there is a
// lag between when data is written to the master and when it is available
// on the replica. At these times, we will want to avoid using a replica
// server temporarily. For example, if a user posts a new node then we want
// to disable the replica server for that user temporarily to allow the
// replica server to catch up.
// That way, that user will see their changes immediately while for other
// users we still get the benefits of having a replica server, just with
// slightly stale data. Code that wants to disable the replica server should
// use the 'database.replica_kill_switch' service's trigger() method to set
// 'ignore_replica_server' session flag to the timestamp after which the
// replica can be re-enabled.
if ($this->session
->has('ignore_replica_server')) {
if ($this->session
->get('ignore_replica_server') >= $this->time
->getRequestTime()) {
Database::ignoreTarget('default', 'replica');
}
else {
$this->session
->remove('ignore_replica_server');
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() : array {
$events[KernelEvents::REQUEST][] = [
'checkReplicaServer',
];
return $events;
}
}
Classes
Name | Description |
---|---|
ReplicaKillSwitch | Provides replica server kill switch to ignore it. |