lib/redix_clustered/clone.ex

defmodule RedixClustered.Clone do
  @moduledoc """
  Conditionally clone write requests to a separate RedixClustered
  """

  alias RedixClustered.Options

  @clone_commands [
    "APPEND",
    "DECR",
    "DECRBY",
    "DEL",
    "EXPIRE",
    "EXPIREAT",
    "GETSET",
    "HDEL",
    "HINCRBY",
    "HINCRBYFLOAT",
    "HMSET",
    "HSET",
    "HSETNX",
    "INCR",
    "INCRBY",
    "INCRBYFLOAT",
    "MSET",
    "MSETNX",
    "PEXPIRE",
    "PEXPIREAT",
    "PSETEX",
    "PTTL",
    "SET",
    "SETEX",
    "SETNX",
    "SETRANGE",
    "UNLINK"
  ]

  def needs_cloning?([command | rest]) when is_list(command) do
    needs_cloning?(command) || needs_cloning?(rest)
  end

  def needs_cloning?(["" <> cmd | _args]) do
    String.upcase(cmd) in @clone_commands
  end

  def needs_cloning?(_), do: false

  def clone_alive?(cluster_name) do
    Options.clone_cluster_name(cluster_name) |> RedixClustered.alive?()
  end

  def clone_command(cluster_name, cmd, opts \\ []) do
    case {clone_alive?(cluster_name), needs_cloning?(cmd)} do
      {false, _} ->
        {:disabled}

      {_, false} ->
        {:readonly}

      _ ->
        clone_cluster_name = Options.clone_cluster_name(cluster_name)
        RedixClustered.command(clone_cluster_name, cmd, opts)
    end
  end

  def clone_pipeline(cluster_name, cmds, opts \\ []) do
    case {clone_alive?(cluster_name), needs_cloning?(cmds)} do
      {false, _} ->
        {:disabled}

      {_, false} ->
        {:readonly}

      _ ->
        clone_cluster_name = Options.clone_cluster_name(cluster_name)
        RedixClustered.pipeline(clone_cluster_name, cmds, opts)
    end
  end
end