Drupal: Use database replica as entity query target

14 Dec, 2019
Drupal: Use database replica as entity query target

In our latest project, we’ve already configured primary/replica database configuration. You can visit https://www.drupal.org/docs/8/api/database-api/database-configuration if you haven’t set it up yet.

Now we can target those replica(s) when makes a query in our custom code.

$database = \Drupal::database();
$result = $database->query("SELECT id, example FROM {mytable}", [], [
  'target' => 'replica',
  'fetch' => PDO::FETCH_ASSOC,
]);

But what about query in core or contributed modules that we can’t manually set the target?


Our project uses Drupal as a backend in decoupled architecture. Most of the requests are API requests with JSON:API specification. Drupal 8 JSON:API module works based on entity, so when fetching data, it will use \Drupal\Core\Entity\Query\QueryInterface. Drupal’s core defined this entity query as a service in core.services.yml.

core.services.yml
entity.query.sql:
  class: Drupal\Core\Entity\Query\Sql\QueryFactory
  arguments: [ '@database' ]
  tags:
    - { name: backend_overridable }

As we can see, the first argument for those services is @database which defined as

database:
  class: Drupal\Core\Database\Connection
  factory: Drupal\Core\Database\Database::getConnection
  arguments: [ default ]
database.replica:
  class: Drupal\Core\Database\Connection
  factory: Drupal\Core\Database\Database::getConnection
  arguments: [ replica ]

We can also see, there is another service database.replica that has the same definition as database except for the argument. If we look at \Drupal\Core\Database\Database::getConnection

\Drupal\Core\Database\Database
/**
 * Gets the connection object for the specified database key and target.
 *
 * @param string $target
 *   The database target name.
 * @param string $key
 *   The database connection key. Defaults to NULL which means the active key.
 *
 * @return \Drupal\Core\Database\Connection
 *   The corresponding connection object.
 */
final public static function getConnection($target = 'default', $key = NULL) {
  ...
}

The $target argument is the database target, in database.replica’s case, the target is the replica. So we can use database.replica as argument in entity.query.sql service.


There are a couple of things to replace those service argument. It works around Symfony’s dependency injection. A straightforward way to achieve this is by overriding the backend of entity.query.sql service. Assuming the database’s driver is mysql, here is how to override the service’s backend in the services.yml:

sites/default/services.yml
services:
  mysql.entity.query.sql:
    class: Drupal\Core\Entity\Query\Sql\QueryFactory
    arguments: ['@database.replica']

References:


Photo by panumas nikhomkhai from Pexels

#Drupal#Drupal 8#database

Related Posts

post preview

Use Redis to speed up your Drupal site

post preview

Teknik Coding: Perulangan Berkesinambungan