Develop a custom Drush command to remove the Redis cache by pattern.

What is Drush?

Drush is a command line interface for drupal, It consists of utility and generator commands for drupal like commands for flushing cache and commands for importing and exporting configs. It also allows us to develop our custom command.

Here is the list of commands shipped with Drush https://www.drush.org/latest/commands/all/

Unlike .drush.inc file and hook_drush_command, we use drush.services.yml and annotation to register a Drush command in Drush 9.

Annotation.

@command:- Defines our Drush command

* @command redis:flush-pattern

@aliases:- Defines an alias for the command.

* @aliases rfp,redis-flush

Defining Drush command.

You can use drush generate drush-command-file command to generate your command related files, You can find more about this command here.

So basically we need to create drush.services.yml in our custom module to declare a tagged service that points to our command file, the syntax is similar like we use in custom_module.services.yml.

drush.services.yml

services:
  custom_module.redis_flush:
    class: \Drupal\custom_module\Commands\RedisFlushCommands
    tags:
      - { name: drush.command }
    arguments: [ '@redis.factory' ]

Here we are injecting redis.factory service which is provided by a contributed module Redis and so we must add it as a dependency of our module in custom_module.info.yml.

dependencies:
  - redis:redis

Drush command class (RedisFlushCommands).

<?php

namespace Drupal\custom_module\Commands;

use Drupal\redis\ClientFactory;
use Drupal\redis\RedisPrefixTrait;
use Drush\Commands\DrushCommands;

/**
 * A drush command file for flushing redis cache.
 */
class RedisFlushCommands extends DrushCommands {

  use RedisPrefixTrait;

  /**
   * Predefined patterns.
   *
   * @var array|string[]
   */
  protected array $cacheKeysPatterns = [
    'render:view:api_user_data:*',
    'render:view:api_article_data:*',
    'render:view:api_tag_data:*',
    'render:view:event_pages:display:*',
  ];

  /**
   * Redis active client object.
   *
   * @var \Redis|null
   */
  protected $redis;

  /**
   * Constructs a new RedisFlushCommands object.
   *
   * @param \Drupal\redis\ClientFactory $clientFactory
   *   Redis client factory service.
   */
  public function __construct(ClientFactory $clientFactory) {
    parent::__construct();

    if ($clientFactory->hasClient()) {
      $this->redis = $clientFactory->getClient();
    }
  }

  /**
   * Flush redis cache for a specific pattern.
   *
   * @param string $pattern
   *   Pattern for cache keys.
   *
   * @command redis:flush-pattern
   * @aliases rfp,redis-flush-pattern
   *
   * @usage redis:flush-pattern
   *   Will flush predefined patterns.
   * @usage redis:flush-pattern "*:render:view:api_tag_data:*"
   *   Will flush provided patterns.
   */
  public function redisFlushPattern(string $pattern = "all") {

    // Prepare pattern.
    $pattern = ($pattern == "all") ? $this->cacheKeysPatterns : [$pattern];

    // Check if we have a redis client.
    if ($this->redis instanceof \Redis) {

      // Loop through pattern.
      foreach ($pattern as $key_pattern) {

        // Add prefix to pattern.
        $key_pattern = $this->getDefaultPrefix() . ':' . $key_pattern;

        // Fetch keys.
        $_keys = $this->redis->keys($key_pattern);

        if (!empty($_keys)) {

          // Flush keys.
          $this->redis->del($_keys);
        }
      }
      // Flushed successfully.
      $this->output()
        ->writeln('Successfully flushed.');
    }
    else {

      // No client available.
      $this->output()
        ->writeln('Redis client not available.');
    }

  }

}

We can also switch between drush.services.yml according to the Drush version and for this, we need to add it to the module’s composer.json file with the Drush version. You can check more details about it here https://docs.drush.org/en/9.x/commands/

The above Drush command is developed for a special situation where we need to flush a specific Redis cache by pattern. However, one can handle it easily by developing a shell script.