annotate core/lib/Drupal/Core/Database/ReplicaKillSwitch.php @ 19:fa3358dc1485 tip

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