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; } }