mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-03-14 10:01:52 +00:00
remove lmpop unit tests
Also remove sentinel_test.py only for this branch. Signed-off-by: adi_holden <adi@dragonflydb.io> Signed-off-by: Roman Gershman <roman@dragonflydb.io>
This commit is contained in:
committed by
Roman Gershman
parent
b16b5349e6
commit
e713b8c692
@ -1086,227 +1086,6 @@ TEST_F(ListFamilyTest, ContendExpire) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPopInvalidSyntax) {
|
||||
// Not enough arguments
|
||||
auto resp = Run({"lmpop", "1", "a"});
|
||||
EXPECT_THAT(resp, ErrArg("wrong number of arguments"));
|
||||
|
||||
// Zero keys
|
||||
resp = Run({"lmpop", "0", "LEFT", "COUNT", "1"});
|
||||
EXPECT_THAT(resp, ErrArg("syntax error"));
|
||||
|
||||
// Number of keys is not uint
|
||||
resp = Run({"lmpop", "aa", "a", "LEFT"});
|
||||
EXPECT_THAT(resp, ErrArg("value is not an integer or out of range"));
|
||||
|
||||
// Missing LEFT/RIGHT
|
||||
resp = Run({"lmpop", "1", "a", "COUNT", "1"});
|
||||
EXPECT_THAT(resp, ErrArg("syntax error"));
|
||||
|
||||
// Wrong number of keys
|
||||
resp = Run({"lmpop", "1", "a", "b", "LEFT"});
|
||||
EXPECT_THAT(resp, ErrArg("syntax error"));
|
||||
|
||||
// COUNT without number
|
||||
resp = Run({"lmpop", "1", "a", "LEFT", "COUNT"});
|
||||
EXPECT_THAT(resp, ErrArg("syntax error"));
|
||||
|
||||
// COUNT is not uint
|
||||
resp = Run({"lmpop", "1", "a", "LEFT", "COUNT", "boo"});
|
||||
EXPECT_THAT(resp, ErrArg("value is not an integer or out of range"));
|
||||
|
||||
// Too many arguments
|
||||
resp = Run({"lmpop", "1", "c", "LEFT", "COUNT", "2", "foo"});
|
||||
EXPECT_THAT(resp, ErrArg("syntax error"));
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPop) {
|
||||
// All lists are empty
|
||||
auto resp = Run({"lmpop", "1", "e", "LEFT"});
|
||||
EXPECT_THAT(resp, ArgType(RespExpr::NIL));
|
||||
|
||||
// LEFT operation
|
||||
resp = Run({"lpush", "a", "a1", "a2"});
|
||||
EXPECT_THAT(resp, IntArg(2));
|
||||
|
||||
resp = Run({"lmpop", "1", "a", "LEFT"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("a", RespArray(ElementsAre("a2")))));
|
||||
|
||||
// RIGHT operation
|
||||
resp = Run({"lpush", "b", "b1", "b2"});
|
||||
EXPECT_THAT(resp, IntArg(2));
|
||||
|
||||
resp = Run({"lmpop", "1", "b", "RIGHT"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("b", RespArray(ElementsAre("b1")))));
|
||||
|
||||
// COUNT > 1
|
||||
resp = Run({"lpush", "c", "c1", "c2"});
|
||||
EXPECT_THAT(resp, IntArg(2));
|
||||
|
||||
resp = Run({"lmpop", "1", "c", "RIGHT", "COUNT", "2"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("c", RespArray(ElementsAre("c1", "c2")))));
|
||||
|
||||
resp = Run({"llen", "c"});
|
||||
EXPECT_THAT(resp, IntArg(0));
|
||||
|
||||
// COUNT > number of elements in list
|
||||
resp = Run({"lpush", "d", "d1", "d2"});
|
||||
EXPECT_THAT(resp, IntArg(2));
|
||||
|
||||
resp = Run({"lmpop", "1", "d", "RIGHT", "COUNT", "3"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("d", RespArray(ElementsAre("d1", "d2")))));
|
||||
|
||||
resp = Run({"llen", "d"});
|
||||
EXPECT_THAT(resp, IntArg(0));
|
||||
|
||||
// First non-empty list is not the first list
|
||||
resp = Run({"lpush", "x", "x1"});
|
||||
EXPECT_THAT(resp, IntArg(1));
|
||||
|
||||
resp = Run({"lpush", "y", "y1"});
|
||||
EXPECT_THAT(resp, IntArg(1));
|
||||
|
||||
resp = Run({"lmpop", "3", "empty", "x", "y", "RIGHT"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("x", RespArray(ElementsAre("x1")))));
|
||||
|
||||
resp = Run({"llen", "x"});
|
||||
EXPECT_THAT(resp, IntArg(0));
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPopMultipleElements) {
|
||||
// Test removing multiple elements from left end
|
||||
Run({"rpush", "list1", "a", "b", "c", "d", "e"});
|
||||
auto resp = Run({"lmpop", "1", "list1", "LEFT", "COUNT", "3"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("list1", RespArray(ElementsAre("a", "b", "c")))));
|
||||
|
||||
resp = Run({"lrange", "list1", "0", "-1"});
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre("d", "e"));
|
||||
|
||||
// Test removing multiple elements from right end
|
||||
Run({"rpush", "list2", "v", "w", "x", "y", "z"});
|
||||
resp = Run({"lmpop", "1", "list2", "RIGHT", "COUNT", "2"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("list2", RespArray(ElementsAre("z", "y")))));
|
||||
|
||||
resp = Run({"lrange", "list2", "0", "-1"});
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre("v", "w", "x"));
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPopMultipleLists) {
|
||||
// Test finding first non-empty list
|
||||
Run({"rpush", "list1", "a", "b"});
|
||||
Run({"rpush", "list2", "c", "d"});
|
||||
Run({"rpush", "list3", "e", "f"});
|
||||
|
||||
// Pop from first non-empty list
|
||||
auto resp = Run({"lmpop", "3", "list1", "list2", "list3", "LEFT"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("list1", RespArray(ElementsAre("a")))));
|
||||
|
||||
// Pop from second list after first becomes empty
|
||||
Run({"lmpop", "1", "list1", "LEFT"}); // Empty list1
|
||||
resp = Run({"lmpop", "3", "list1", "list2", "list3", "RIGHT", "COUNT", "2"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("list2", RespArray(ElementsAre("d", "c")))));
|
||||
|
||||
// Verify third list remains untouched
|
||||
resp = Run({"lrange", "list3", "0", "-1"});
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre("e", "f"));
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPopEdgeCases) {
|
||||
// Test with empty list
|
||||
Run({"rpush", "empty_list", "a"});
|
||||
Run({"lpop", "empty_list"});
|
||||
auto resp = Run({"lmpop", "1", "empty_list", "LEFT"});
|
||||
EXPECT_THAT(resp, ArgType(RespExpr::NIL));
|
||||
|
||||
// Test with non-existent list
|
||||
resp = Run({"lmpop", "1", "nonexistent", "LEFT"});
|
||||
EXPECT_THAT(resp, ArgType(RespExpr::NIL));
|
||||
|
||||
// Test with wrong type key
|
||||
Run({"set", "string_key", "value"});
|
||||
resp = Run({"lmpop", "1", "string_key", "LEFT"});
|
||||
EXPECT_THAT(resp, ErrArg("WRONGTYPE Operation against a key holding the wrong kind of value"));
|
||||
|
||||
// Test without COUNT parameter - should return 1 element by default
|
||||
Run({"rpush", "list", "a", "b"});
|
||||
resp = Run({"lmpop", "1", "list", "LEFT"});
|
||||
EXPECT_THAT(resp,
|
||||
RespArray(ElementsAre(
|
||||
"list", RespArray(ElementsAre("a"))))); // Should return 1 element by default
|
||||
|
||||
// Test with COUNT = 0 - should return error
|
||||
resp = Run({"lmpop", "1", "list", "LEFT", "COUNT", "0"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("list", RespArray(ElementsAre()))));
|
||||
|
||||
// Test with negative COUNT - should return error
|
||||
resp = Run({"lmpop", "1", "list", "LEFT", "COUNT", "-1"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("list", RespArray(ElementsAre("b")))));
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPopDocExample) {
|
||||
// Try to pop from non-existing lists
|
||||
auto resp = Run({"LMPOP", "2", "non1", "non2", "LEFT", "COUNT", "10"});
|
||||
EXPECT_THAT(resp, ArgType(RespExpr::NIL));
|
||||
|
||||
// Create first list and test basic pop
|
||||
resp = Run({"LPUSH", "mylist", "one", "two", "three", "four", "five"});
|
||||
EXPECT_THAT(resp, IntArg(5));
|
||||
|
||||
resp = Run({"LMPOP", "1", "mylist", "LEFT"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("mylist", RespArray(ElementsAre("five")))));
|
||||
|
||||
resp = Run({"LRANGE", "mylist", "0", "-1"});
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre("four", "three", "two", "one"));
|
||||
|
||||
// Test RIGHT pop with COUNT
|
||||
resp = Run({"LMPOP", "1", "mylist", "RIGHT", "COUNT", "10"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("mylist",
|
||||
RespArray(ElementsAre("one", "two", "three", "four")))));
|
||||
|
||||
// Create two lists and test multi-key pop
|
||||
resp = Run({"LPUSH", "mylist", "one", "two", "three", "four", "five"});
|
||||
EXPECT_THAT(resp, IntArg(5));
|
||||
|
||||
resp = Run({"LPUSH", "mylist2", "a", "b", "c", "d", "e"});
|
||||
EXPECT_THAT(resp, IntArg(5));
|
||||
|
||||
resp = Run({"LMPOP", "2", "mylist", "mylist2", "RIGHT", "COUNT", "3"});
|
||||
EXPECT_THAT(resp,
|
||||
RespArray(ElementsAre("mylist", RespArray(ElementsAre("one", "two", "three")))));
|
||||
|
||||
resp = Run({"LRANGE", "mylist", "0", "-1"});
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre("five", "four"));
|
||||
|
||||
resp = Run({"LMPOP", "2", "mylist", "mylist2", "RIGHT", "COUNT", "5"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("mylist", RespArray(ElementsAre("four", "five")))));
|
||||
|
||||
resp = Run({"LMPOP", "2", "mylist", "mylist2", "RIGHT", "COUNT", "10"});
|
||||
EXPECT_THAT(resp,
|
||||
RespArray(ElementsAre("mylist2", RespArray(ElementsAre("a", "b", "c", "d", "e")))));
|
||||
|
||||
// Verify both lists are now empty
|
||||
resp = Run({"EXISTS", "mylist", "mylist2"});
|
||||
EXPECT_THAT(resp, IntArg(0));
|
||||
}
|
||||
|
||||
TEST_F(ListFamilyTest, LMPopWrongType) {
|
||||
// Setup: create a list and a hash
|
||||
Run({"lpush", "l1", "e1"});
|
||||
Run({"hset", "foo", "k1", "v1"});
|
||||
|
||||
// Test: first key is wrong type
|
||||
auto resp = Run({"lmpop", "2", "foo", "l1", "left"});
|
||||
EXPECT_THAT(resp, ErrArg("WRONGTYPE Operation against a key holding the wrong kind of value"));
|
||||
|
||||
// Test: second key is wrong type but first doesn't exist
|
||||
resp = Run({"lmpop", "2", "nonexistent", "foo", "left"});
|
||||
EXPECT_THAT(resp, ErrArg("WRONGTYPE Operation against a key holding the wrong kind of value"));
|
||||
|
||||
// Test: second key is wrong type but first is a valid list
|
||||
resp = Run({"lmpop", "2", "l1", "foo", "left"});
|
||||
EXPECT_THAT(resp, RespArray(ElementsAre("l1", RespArray(ElementsAre("e1")))));
|
||||
}
|
||||
|
||||
// Reproduce a flow that trigerred a wrong DCHECK in the transaction flow.
|
||||
TEST_F(ListFamilyTest, AwakeMulti) {
|
||||
auto f1 = pp_->at(1)->LaunchFiber(Launch::dispatch, [&] {
|
||||
|
@ -1,308 +0,0 @@
|
||||
import pathlib
|
||||
import subprocess
|
||||
from typing import Awaitable
|
||||
from redis import asyncio as aioredis
|
||||
import pytest
|
||||
import time
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from sys import stderr
|
||||
import logging
|
||||
|
||||
from .utility import assert_eventually, wait_available_async
|
||||
|
||||
from .instance import DflyInstanceFactory
|
||||
from . import dfly_args
|
||||
|
||||
|
||||
# Helper function to parse some sentinel cli commands output as key value dictionaries.
|
||||
# Output is expected be of even number of lines where each pair of consecutive lines results in a single key value pair.
|
||||
# If new_dict_key is not empty, encountering it in the output will start a new dictionary, this let us return multiple
|
||||
# dictionaries, for example in the 'slaves' command, one dictionary for each slave.
|
||||
def stdout_as_list_of_dicts(cp: subprocess.CompletedProcess, new_dict_key=""):
|
||||
lines = cp.stdout.splitlines()
|
||||
res = []
|
||||
d = None
|
||||
if new_dict_key == "":
|
||||
d = dict()
|
||||
res.append(d)
|
||||
for i in range(0, len(lines), 2):
|
||||
if (lines[i]) == new_dict_key: # assumes output never has '' as a key
|
||||
d = dict()
|
||||
res.append(d)
|
||||
d[lines[i]] = lines[i + 1]
|
||||
return res
|
||||
|
||||
|
||||
def wait_for(func, pred, timeout_sec, timeout_msg=""):
|
||||
while not pred(func()):
|
||||
assert timeout_sec > 0, timeout_msg
|
||||
timeout_sec = timeout_sec - 1
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
async def await_for(func, pred, timeout_sec, timeout_msg=""):
|
||||
done = False
|
||||
while not done:
|
||||
val = func()
|
||||
if isinstance(val, Awaitable):
|
||||
val = await val
|
||||
done = pred(val)
|
||||
assert timeout_sec > 0, timeout_msg
|
||||
timeout_sec = timeout_sec - 1
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
@assert_eventually
|
||||
async def assert_master_became_replica(client):
|
||||
repl_info = await client.info("replication")
|
||||
assert repl_info["role"] == "slave"
|
||||
|
||||
|
||||
class Sentinel:
|
||||
def __init__(self, port, master_port, config_dir) -> None:
|
||||
self.config_file = pathlib.Path(config_dir).joinpath("sentinel.conf")
|
||||
self.port = port
|
||||
self.image = "bitnami/redis-sentinel:latest"
|
||||
self.container_name = "sentinel_test_py_sentinel"
|
||||
self.default_deployment = "my_deployment"
|
||||
self.initial_master_port = master_port
|
||||
self.proc = None
|
||||
|
||||
def start(self):
|
||||
config = [
|
||||
f"port {self.port}",
|
||||
f"sentinel monitor {self.default_deployment} 127.0.0.1 {self.initial_master_port} 1",
|
||||
f"sentinel down-after-milliseconds {self.default_deployment} 3000",
|
||||
f"slave-priority 100",
|
||||
]
|
||||
self.config_file.write_text("\n".join(config))
|
||||
|
||||
logging.info(self.config_file.read_text())
|
||||
|
||||
self.proc = subprocess.Popen(
|
||||
["redis-server", f"{self.config_file.absolute()}", "--sentinel"]
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
self.proc.terminate()
|
||||
self.proc.wait(timeout=10)
|
||||
|
||||
def run_cmd(
|
||||
self, args, sentinel_cmd=True, capture_output=False, assert_ok=True
|
||||
) -> subprocess.CompletedProcess:
|
||||
run_args = ["redis-cli", "-p", f"{self.port}"]
|
||||
if sentinel_cmd:
|
||||
run_args = run_args + ["sentinel"]
|
||||
run_args = run_args + args
|
||||
cp = subprocess.run(run_args, capture_output=capture_output, text=True)
|
||||
if assert_ok:
|
||||
assert cp.returncode == 0, f"Command failed: {run_args}"
|
||||
return cp
|
||||
|
||||
def wait_ready(self):
|
||||
wait_for(
|
||||
lambda: self.run_cmd(["ping"], sentinel_cmd=False, assert_ok=False),
|
||||
lambda cp: cp.returncode == 0,
|
||||
timeout_sec=10,
|
||||
timeout_msg="Timeout waiting for sentinel to become ready.",
|
||||
)
|
||||
|
||||
def master(self, deployment="") -> dict:
|
||||
if deployment == "":
|
||||
deployment = self.default_deployment
|
||||
cp = self.run_cmd(["master", deployment], capture_output=True)
|
||||
return stdout_as_list_of_dicts(cp)[0]
|
||||
|
||||
def slaves(self, deployment="") -> dict:
|
||||
if deployment == "":
|
||||
deployment = self.default_deployment
|
||||
cp = self.run_cmd(["slaves", deployment], capture_output=True)
|
||||
return stdout_as_list_of_dicts(cp)
|
||||
|
||||
def live_master_port(self, deployment=""):
|
||||
if deployment == "":
|
||||
deployment = self.default_deployment
|
||||
cp = self.run_cmd(["get-master-addr-by-name", deployment], capture_output=True)
|
||||
return int(cp.stdout.splitlines()[1])
|
||||
|
||||
def failover(self, deployment=""):
|
||||
if deployment == "":
|
||||
deployment = self.default_deployment
|
||||
self.run_cmd(
|
||||
[
|
||||
"failover",
|
||||
deployment,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="function"
|
||||
) # Sentinel has state which we don't want carried over form test to test.
|
||||
def sentinel(tmp_dir, port_picker) -> Sentinel:
|
||||
s = Sentinel(port_picker.get_available_port(), port_picker.get_available_port(), tmp_dir)
|
||||
s.start()
|
||||
s.wait_ready()
|
||||
yield s
|
||||
s.stop()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.slow
|
||||
async def test_failover(df_factory: DflyInstanceFactory, sentinel, port_picker):
|
||||
master = df_factory.create(port=sentinel.initial_master_port)
|
||||
replica = df_factory.create(port=port_picker.get_available_port())
|
||||
|
||||
master.start()
|
||||
replica.start()
|
||||
|
||||
master_client = aioredis.Redis(port=master.port)
|
||||
replica_client = aioredis.Redis(port=replica.port)
|
||||
logging.info("master: " + str(master.port) + " replica: " + str(replica.port))
|
||||
|
||||
await replica_client.execute_command("REPLICAOF localhost " + str(master.port))
|
||||
|
||||
assert sentinel.live_master_port() == master.port
|
||||
|
||||
# Verify sentinel picked up replica.
|
||||
await await_for(
|
||||
lambda: sentinel.master(),
|
||||
lambda m: m["num-slaves"] == "1",
|
||||
timeout_sec=15,
|
||||
timeout_msg="Timeout waiting for sentinel to pick up replica.",
|
||||
)
|
||||
sentinel.failover()
|
||||
|
||||
# Verify sentinel switched.
|
||||
await await_for(
|
||||
lambda: sentinel.live_master_port(),
|
||||
lambda p: p == replica.port,
|
||||
timeout_sec=10,
|
||||
timeout_msg="Timeout waiting for sentinel to report replica as master.",
|
||||
)
|
||||
assert sentinel.slaves()[0]["port"] == str(master.port)
|
||||
|
||||
# Verify we can now write to replica and read replicated value from master.
|
||||
assert await replica_client.set("key", "value"), "Failed to set key on promoted replica."
|
||||
|
||||
logging.info("key was set on promoted replica, awaiting get on promoted replica. ")
|
||||
|
||||
await assert_master_became_replica(master_client)
|
||||
await wait_available_async(master_client)
|
||||
|
||||
try:
|
||||
await await_for(
|
||||
lambda: master_client.get("key"),
|
||||
lambda val: val == b"value",
|
||||
10,
|
||||
"Timeout waiting for key to exist in replica.",
|
||||
)
|
||||
except AssertionError:
|
||||
syncid, r_offset = await master_client.execute_command("DEBUG REPLICA OFFSET")
|
||||
replicaoffset_cmd = "DFLY REPLICAOFFSET " + syncid.decode()
|
||||
m_offset = await replica_client.execute_command(replicaoffset_cmd)
|
||||
logging.info(f"{syncid.decode()} {r_offset} {m_offset}")
|
||||
logging.info("replica client role:")
|
||||
logging.info(await replica_client.execute_command("role"))
|
||||
logging.info("master client role:")
|
||||
logging.info(await master_client.execute_command("role"))
|
||||
logging.info("replica client info:")
|
||||
logging.info(await replica_client.info())
|
||||
logging.info("master client info:")
|
||||
logging.info(await master_client.info())
|
||||
replica_val = await replica_client.get("key")
|
||||
master_val = await master_client.get("key")
|
||||
logging.info(f"replica val: {replica_val}")
|
||||
logging.info(f"master val: {master_val}")
|
||||
raise
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.slow
|
||||
async def test_master_failure(df_factory, sentinel, port_picker):
|
||||
master = df_factory.create(port=sentinel.initial_master_port)
|
||||
replica = df_factory.create(port=port_picker.get_available_port())
|
||||
|
||||
master.start()
|
||||
replica.start()
|
||||
|
||||
replica_client = aioredis.Redis(port=replica.port)
|
||||
|
||||
await replica_client.execute_command("REPLICAOF localhost " + str(master.port))
|
||||
|
||||
assert sentinel.live_master_port() == master.port
|
||||
|
||||
# Verify sentinel picked up replica.
|
||||
await await_for(
|
||||
lambda: sentinel.master(),
|
||||
lambda m: m["num-slaves"] == "1",
|
||||
timeout_sec=15,
|
||||
timeout_msg="Timeout waiting for sentinel to pick up replica.",
|
||||
)
|
||||
|
||||
# Simulate master failure.
|
||||
master.stop()
|
||||
|
||||
# Verify replica promoted.
|
||||
await await_for(
|
||||
lambda: sentinel.live_master_port(),
|
||||
lambda p: p == replica.port,
|
||||
timeout_sec=300,
|
||||
timeout_msg="Timeout waiting for sentinel to report replica as master.",
|
||||
)
|
||||
|
||||
# Verify we can now write to replica.
|
||||
await replica_client.set("key", "value")
|
||||
assert await replica_client.get("key") == b"value"
|
||||
|
||||
|
||||
@dfly_args({"info_replication_valkey_compatible": True})
|
||||
@pytest.mark.asyncio
|
||||
async def test_priority_on_failover(df_factory, sentinel, port_picker):
|
||||
master = df_factory.create(port=sentinel.initial_master_port)
|
||||
# lower priority is the best candidate for sentinel
|
||||
low_priority_repl = df_factory.create(
|
||||
port=port_picker.get_available_port(), replica_priority=20
|
||||
)
|
||||
mid_priority_repl = df_factory.create(
|
||||
port=port_picker.get_available_port(), replica_priority=60
|
||||
)
|
||||
high_priority_repl = df_factory.create(
|
||||
port=port_picker.get_available_port(), replica_priority=80
|
||||
)
|
||||
|
||||
master.start()
|
||||
low_priority_repl.start()
|
||||
mid_priority_repl.start()
|
||||
high_priority_repl.start()
|
||||
|
||||
high_client = aioredis.Redis(port=high_priority_repl.port)
|
||||
await high_client.execute_command("REPLICAOF localhost " + str(master.port))
|
||||
|
||||
mid_client = aioredis.Redis(port=mid_priority_repl.port)
|
||||
await mid_client.execute_command("REPLICAOF localhost " + str(master.port))
|
||||
|
||||
low_client = aioredis.Redis(port=low_priority_repl.port)
|
||||
await low_client.execute_command("REPLICAOF localhost " + str(master.port))
|
||||
|
||||
assert sentinel.live_master_port() == master.port
|
||||
|
||||
# Verify sentinel picked up replica.
|
||||
await await_for(
|
||||
lambda: sentinel.master(),
|
||||
lambda m: m["num-slaves"] == "3",
|
||||
timeout_sec=15,
|
||||
timeout_msg="Timeout waiting for sentinel to pick up replica.",
|
||||
)
|
||||
|
||||
# Simulate master failure.
|
||||
master.stop()
|
||||
|
||||
# Verify replica promoted.
|
||||
await await_for(
|
||||
lambda: sentinel.live_master_port(),
|
||||
lambda p: p == low_priority_repl.port,
|
||||
timeout_sec=30,
|
||||
timeout_msg="Timeout waiting for sentinel to report replica as master.",
|
||||
)
|
Reference in New Issue
Block a user