annotate core/lib/Drupal/Core/Database/ReplicaKillSwitch.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents
children
rev   line source
Chris@5 1 <?php
Chris@5 2
Chris@5 3 namespace Drupal\Core\Database;
Chris@5 4
Chris@5 5 use Drupal\Component\Datetime\TimeInterface;
Chris@5 6 use Drupal\Core\Site\Settings;
Chris@5 7 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Chris@5 8 use Symfony\Component\HttpFoundation\Session\SessionInterface;
Chris@5 9 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
Chris@5 10 use Symfony\Component\HttpKernel\KernelEvents;
Chris@5 11
Chris@5 12 /**
Chris@5 13 * Provides replica server kill switch to ignore it.
Chris@5 14 */
Chris@5 15 class ReplicaKillSwitch implements EventSubscriberInterface {
Chris@5 16
Chris@5 17 /**
Chris@5 18 * The settings object.
Chris@5 19 *
Chris@5 20 * @var \Drupal\Core\Site\Settings
Chris@5 21 */
Chris@5 22 protected $settings;
Chris@5 23
Chris@5 24 /**
Chris@5 25 * The time service.
Chris@5 26 *
Chris@5 27 * @var \Drupal\Component\Datetime\TimeInterface
Chris@5 28 */
Chris@5 29 protected $time;
Chris@5 30
Chris@5 31 /**
Chris@5 32 * The session.
Chris@5 33 *
Chris@5 34 * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
Chris@5 35 */
Chris@5 36 protected $session;
Chris@5 37
Chris@5 38 /**
Chris@5 39 * Constructs a ReplicaKillSwitch object.
Chris@5 40 *
Chris@5 41 * @param \Drupal\Core\Site\Settings $settings
Chris@5 42 * The settings object.
Chris@5 43 * @param \Drupal\Component\Datetime\TimeInterface $time
Chris@5 44 * The time service.
Chris@5 45 * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
Chris@5 46 * The session.
Chris@5 47 */
Chris@5 48 public function __construct(Settings $settings, TimeInterface $time, SessionInterface $session) {
Chris@5 49 $this->settings = $settings;
Chris@5 50 $this->time = $time;
Chris@5 51 $this->session = $session;
Chris@5 52 }
Chris@5 53
Chris@5 54 /**
Chris@5 55 * Denies access to replica database on the current request.
Chris@5 56 *
Chris@5 57 * @see https://www.drupal.org/node/2286193
Chris@5 58 */
Chris@5 59 public function trigger() {
Chris@5 60 $connection_info = Database::getConnectionInfo();
Chris@5 61 // Only set ignore_replica_server if there are replica servers being used,
Chris@5 62 // which is assumed if there are more than one.
Chris@5 63 if (count($connection_info) > 1) {
Chris@5 64 // Five minutes is long enough to allow the replica to break and resume
Chris@5 65 // interrupted replication without causing problems on the Drupal site
Chris@5 66 // from the old data.
Chris@5 67 $duration = $this->settings->get('maximum_replication_lag', 300);
Chris@5 68 // Set session variable with amount of time to delay before using replica.
Chris@5 69 $this->session->set('ignore_replica_server', $this->time->getRequestTime() + $duration);
Chris@5 70 }
Chris@5 71 }
Chris@5 72
Chris@5 73 /**
Chris@5 74 * Checks and disables the replica database server if appropriate.
Chris@5 75 *
Chris@5 76 * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
Chris@5 77 * The Event to process.
Chris@5 78 */
Chris@5 79 public function checkReplicaServer(GetResponseEvent $event) {
Chris@5 80 // Ignore replica database servers for this request.
Chris@5 81 //
Chris@5 82 // In Drupal's distributed database structure, new data is written to the
Chris@5 83 // master and then propagated to the replica servers. This means there is a
Chris@5 84 // lag between when data is written to the master and when it is available
Chris@5 85 // on the replica. At these times, we will want to avoid using a replica
Chris@5 86 // server temporarily. For example, if a user posts a new node then we want
Chris@5 87 // to disable the replica server for that user temporarily to allow the
Chris@5 88 // replica server to catch up.
Chris@5 89 // That way, that user will see their changes immediately while for other
Chris@5 90 // users we still get the benefits of having a replica server, just with
Chris@5 91 // slightly stale data. Code that wants to disable the replica server should
Chris@5 92 // use the 'database.replica_kill_switch' service's trigger() method to set
Chris@5 93 // 'ignore_replica_server' session flag to the timestamp after which the
Chris@5 94 // replica can be re-enabled.
Chris@5 95 if ($this->session->has('ignore_replica_server')) {
Chris@5 96 if ($this->session->get('ignore_replica_server') >= $this->time->getRequestTime()) {
Chris@5 97 Database::ignoreTarget('default', 'replica');
Chris@5 98 }
Chris@5 99 else {
Chris@5 100 $this->session->remove('ignore_replica_server');
Chris@5 101 }
Chris@5 102 }
Chris@5 103 }
Chris@5 104
Chris@5 105 /**
Chris@5 106 * {@inheritdoc}
Chris@5 107 */
Chris@5 108 public static function getSubscribedEvents() {
Chris@5 109 $events[KernelEvents::REQUEST][] = ['checkReplicaServer'];
Chris@5 110 return $events;
Chris@5 111 }
Chris@5 112
Chris@5 113 }