Current section

Files

Jump to
exq test exq heartbeat monitor_test.exs
Raw

test/exq/heartbeat/monitor_test.exs

defmodule Exq.Heartbeat.MonitorTest do
use ExUnit.Case
import ExqTestUtil
alias Exq.Support.Config
alias Exq.Redis.Heartbeat
@opts [
redis: :testredis,
heartbeat_enable: true,
heartbeat_interval: 200,
missed_heartbeats_allowed: 3,
queues: ["default"],
namespace: "test",
name: ExqHeartbeat,
stats: ExqHeartbeat.Stats
]
setup do
TestRedis.setup()
on_exit(fn ->
wait()
TestRedis.teardown()
end)
end
test "re-enqueues orphaned jobs from dead node's backup queue" do
{:ok, _} = Exq.Stats.Server.start_link(@opts)
redis = :testredis
servers =
for i <- 1..5 do
{:ok, heartbeat} =
Exq.Heartbeat.Server.start_link(Keyword.put(@opts, :node_id, to_string(i)))
{:ok, monitor} =
Exq.Heartbeat.Monitor.start_link(Keyword.put(@opts, :node_id, to_string(i)))
%{heartbeat: heartbeat, monitor: monitor}
end
assert {:ok, 1} = working(redis, "3")
Process.sleep(1000)
assert alive_nodes(redis) == ["1", "2", "3", "4", "5"]
assert queue_length(redis, "3") == {:ok, 1}
server = Enum.at(servers, 2)
:ok = GenServer.stop(server.heartbeat)
Process.sleep(2000)
assert alive_nodes(redis) == ["1", "2", "4", "5"]
assert queue_length(redis, "3") == {:ok, 0}
end
test "re-enqueues more than 10 orphaned jobs from dead node's backup queue" do
{:ok, _} = Exq.Stats.Server.start_link(@opts)
redis = :testredis
servers =
for i <- 1..5 do
{:ok, heartbeat} =
Exq.Heartbeat.Server.start_link(Keyword.put(@opts, :node_id, to_string(i)))
{:ok, monitor} =
Exq.Heartbeat.Monitor.start_link(Keyword.put(@opts, :node_id, to_string(i)))
%{heartbeat: heartbeat, monitor: monitor}
end
for i <- 1..15 do
assert {:ok, ^i} = working(redis, "3")
end
Process.sleep(1000)
assert alive_nodes(redis) == ["1", "2", "3", "4", "5"]
assert queue_length(redis, "3") == {:ok, 15}
server = Enum.at(servers, 2)
:ok = GenServer.stop(server.heartbeat)
Process.sleep(2000)
assert alive_nodes(redis) == ["1", "2", "4", "5"]
assert queue_length(redis, "3") == {:ok, 0}
end
test "can handle connection failure" do
with_application_env(:exq, :redis_timeout, 500, fn ->
{:ok, _} = Exq.Stats.Server.start_link(@opts)
redis = :testredis
assert alive_nodes(redis) == []
{:ok, _} = Exq.Heartbeat.Server.start_link(Keyword.put(@opts, :node_id, "1"))
{:ok, _} = Exq.Heartbeat.Monitor.start_link(Keyword.put(@opts, :node_id, "1"))
spawn(fn ->
Redix.command(:testredis, ["DEBUG", "SLEEP", "2"])
end)
Process.sleep(2000)
Redix.command(:testredis, ["FLUSHALL"])
Process.sleep(1000)
assert alive_nodes(redis) == ["1"]
end)
end
test "shouldn't dequeue from live node" do
redis = :testredis
namespace = Config.get(:namespace)
interval = 100
missed_heartbeats_allowed = 3
Heartbeat.register(redis, namespace, "1")
assert {:ok, 1} = working(redis, "1")
Process.sleep(1000)
{:ok, %{"1" => score}} =
Heartbeat.dead_nodes(
redis,
namespace,
interval,
missed_heartbeats_allowed
)
assert queue_length(redis, "1") == {:ok, 1}
Heartbeat.re_enqueue_backup(redis, namespace, "1", "default", score)
assert queue_length(redis, "1") == {:ok, 0}
Heartbeat.register(redis, namespace, "1")
assert {:ok, 1} = working(redis, "1")
Process.sleep(1000)
{:ok, %{"1" => score}} =
Heartbeat.dead_nodes(
redis,
namespace,
interval,
missed_heartbeats_allowed
)
# The node came back after we got the dead node list, but before we could re-enqueue
Heartbeat.register(redis, namespace, "1")
Heartbeat.re_enqueue_backup(redis, namespace, "1", "default", score)
assert queue_length(redis, "1") == {:ok, 1}
Process.sleep(1000)
{:ok, %{"1" => score}} =
Heartbeat.dead_nodes(
redis,
namespace,
interval,
missed_heartbeats_allowed
)
# The node got removed by another heartbeat monitor
Heartbeat.unregister(redis, namespace, "1")
Heartbeat.re_enqueue_backup(redis, namespace, "1", "default", score)
assert queue_length(redis, "1") == {:ok, 1}
end
defp alive_nodes(redis) do
{:ok, nodes} =
Redix.command(redis, ["ZRANGEBYSCORE", "#{Config.get(:namespace)}:heartbeats", "0", "+inf"])
Enum.sort(nodes)
end
defp working(redis, node_id) do
Redix.command(redis, [
"LPUSH",
Exq.Redis.JobQueue.backup_queue_key(Config.get(:namespace), node_id, "default"),
"{}"
])
end
defp queue_length(redis, node_id) do
Redix.command(redis, [
"LLEN",
Exq.Redis.JobQueue.backup_queue_key(Config.get(:namespace), node_id, "default")
])
end
end