diff core/lib/Drupal/Core/Database/ReplicaKillSwitch.php @ 18:af1871eacc83

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:33:08 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/lib/Drupal/Core/Database/ReplicaKillSwitch.php	Thu May 09 15:33:08 2019 +0100
@@ -0,0 +1,113 @@
+<?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\GetResponseEvent;
+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\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function checkReplicaServer(GetResponseEvent $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() {
+    $events[KernelEvents::REQUEST][] = ['checkReplicaServer'];
+    return $events;
+  }
+
+}