Chris@18: settings = $settings; Chris@18: $this->time = $time; Chris@18: $this->session = $session; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Denies access to replica database on the current request. Chris@18: * Chris@18: * @see https://www.drupal.org/node/2286193 Chris@18: */ Chris@18: public function trigger() { Chris@18: $connection_info = Database::getConnectionInfo(); Chris@18: // Only set ignore_replica_server if there are replica servers being used, Chris@18: // which is assumed if there are more than one. Chris@18: if (count($connection_info) > 1) { Chris@18: // Five minutes is long enough to allow the replica to break and resume Chris@18: // interrupted replication without causing problems on the Drupal site Chris@18: // from the old data. Chris@18: $duration = $this->settings->get('maximum_replication_lag', 300); Chris@18: // Set session variable with amount of time to delay before using replica. Chris@18: $this->session->set('ignore_replica_server', $this->time->getRequestTime() + $duration); Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Checks and disables the replica database server if appropriate. Chris@18: * Chris@18: * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event Chris@18: * The Event to process. Chris@18: */ Chris@18: public function checkReplicaServer(GetResponseEvent $event) { Chris@18: // Ignore replica database servers for this request. Chris@18: // Chris@18: // In Drupal's distributed database structure, new data is written to the Chris@18: // master and then propagated to the replica servers. This means there is a Chris@18: // lag between when data is written to the master and when it is available Chris@18: // on the replica. At these times, we will want to avoid using a replica Chris@18: // server temporarily. For example, if a user posts a new node then we want Chris@18: // to disable the replica server for that user temporarily to allow the Chris@18: // replica server to catch up. Chris@18: // That way, that user will see their changes immediately while for other Chris@18: // users we still get the benefits of having a replica server, just with Chris@18: // slightly stale data. Code that wants to disable the replica server should Chris@18: // use the 'database.replica_kill_switch' service's trigger() method to set Chris@18: // 'ignore_replica_server' session flag to the timestamp after which the Chris@18: // replica can be re-enabled. Chris@18: if ($this->session->has('ignore_replica_server')) { Chris@18: if ($this->session->get('ignore_replica_server') >= $this->time->getRequestTime()) { Chris@18: Database::ignoreTarget('default', 'replica'); Chris@18: } Chris@18: else { Chris@18: $this->session->remove('ignore_replica_server'); Chris@18: } Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * {@inheritdoc} Chris@18: */ Chris@18: public static function getSubscribedEvents() { Chris@18: $events[KernelEvents::REQUEST][] = ['checkReplicaServer']; Chris@18: return $events; Chris@18: } Chris@18: Chris@18: }