ReplicaKillSwitch.php

Same filename in other branches
  1. 9 core/lib/Drupal/Core/Database/ReplicaKillSwitch.php
  2. 8.9.x core/lib/Drupal/Core/Database/ReplicaKillSwitch.php
  3. 10 core/lib/Drupal/Core/Database/ReplicaKillSwitch.php

Namespace

Drupal\Core\Database

File

core/lib/Drupal/Core/Database/ReplicaKillSwitch.php

View 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

Title Deprecated Summary
ReplicaKillSwitch Provides replica server kill switch to ignore it.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.