Added quorum system tests.

This commit is contained in:
Michael Whittaker 2021-01-31 21:55:05 -08:00
parent a981d44d7f
commit 03b307d5f0
2 changed files with 190 additions and 2 deletions

View file

@ -192,6 +192,10 @@ class QuorumSystem(Generic[T]):
raise ValueError('sigma_r has negative weights') raise ValueError('sigma_r has negative weights')
if not all(0 <= weight for weight in sigma_w.values()): if not all(0 <= weight for weight in sigma_w.values()):
raise ValueError('sigma_w has negative weights') raise ValueError('sigma_w has negative weights')
if not all(self.is_read_quorum(set(rq)) for rq in sigma_r):
raise ValueError('sigma_r has non-read quorums')
if not all(self.is_write_quorum(set(wq)) for wq in sigma_w):
raise ValueError('sigma_w has non-write quorums')
normalized_sigma_r = {rq: weight / sum(sigma_r.values()) normalized_sigma_r = {rq: weight / sum(sigma_r.values())
for (rq, weight) in sigma_r.items()} for (rq, weight) in sigma_r.items()}
normalized_sigma_w = {wq: weight / sum(sigma_w.values()) normalized_sigma_w = {wq: weight / sum(sigma_w.values())

View file

@ -128,6 +128,190 @@ class TestQuorumSystem(unittest.TestCase):
frozenset({'b', 'd'}): 1 / 4, frozenset({'b', 'd'}): 1 / 4,
}) })
def test_make_strategy(self):
a = Node('a')
b = Node('b')
c = Node('c')
d = Node('d')
qs = QuorumSystem(reads = a*b + c*d)
sigma = qs.make_strategy(
sigma_r = {
frozenset({'a', 'b'}): 25,
frozenset({'c', 'd'}): 75,
},
sigma_w = {
frozenset({'a', 'c'}): 1,
frozenset({'a', 'd'}): 1,
frozenset({'b', 'c'}): 1,
frozenset({'b', 'd'}): 1,
},
)
self.assertEqual(sigma.sigma_r,
{
frozenset({'a', 'b'}): 0.25,
frozenset({'c', 'd'}): 0.75,
},
)
self.assertEqual(sigma.sigma_w,
{
frozenset({'a', 'c'}): 0.25,
frozenset({'a', 'd'}): 0.25,
frozenset({'b', 'c'}): 0.25,
frozenset({'b', 'd'}): 0.25,
},
)
with self.assertRaises(ValueError):
sigma = qs.make_strategy(
sigma_r = {
frozenset({'a', 'b'}): -1,
frozenset({'c', 'd'}): 1,
},
sigma_w = {
frozenset({'a', 'c'}): 1,
frozenset({'a', 'd'}): 1,
frozenset({'b', 'c'}): 1,
frozenset({'b', 'd'}): 1,
},
)
with self.assertRaises(ValueError):
sigma = qs.make_strategy(
sigma_r = {
frozenset({'a'}): 1,
frozenset({'c', 'd'}): 1,
},
sigma_w = {
frozenset({'a', 'c'}): 1,
frozenset({'a', 'd'}): 1,
frozenset({'b', 'c'}): 1,
frozenset({'b', 'd'}): 1,
},
)
def test_optimal_strategy(self): def test_optimal_strategy(self):
# TODO(mwhittaker): Implement. def s(n: int) -> datetime.timedelta:
pass return datetime.timedelta(seconds=n)
a = Node('a', write_capacity=1, read_capacity=2, latency=s(1))
b = Node('b', write_capacity=1, read_capacity=2, latency=s(2))
c = Node('c', write_capacity=1, read_capacity=2, latency=s(3))
d = Node('d', write_capacity=1, read_capacity=2, latency=s(4))
qs = QuorumSystem(reads=a*b + c*d)
# Load Optimized.
self.assertEqual(qs.load(read_fraction=1), 0.25)
self.assertEqual(qs.capacity(read_fraction=1), 4)
self.assertEqual(qs.load(read_fraction=0), 0.5)
self.assertEqual(qs.capacity(read_fraction=0), 2)
self.assertEqual(qs.load(read_fraction=1, network_limit=2), 0.25)
self.assertEqual(qs.capacity(read_fraction=1, network_limit=2), 4)
self.assertEqual(qs.load(read_fraction=0, network_limit=2), 0.5)
self.assertEqual(qs.capacity(read_fraction=0, network_limit=2), 2)
self.assertEqual(qs.load(read_fraction=1, latency_limit=s(4)), 0.25)
self.assertEqual(qs.capacity(read_fraction=1, latency_limit=s(4)), 4)
self.assertEqual(qs.load(read_fraction=0, latency_limit=s(4)), 0.5)
self.assertEqual(qs.capacity(read_fraction=0, latency_limit=s(4)), 2)
# Network Optimized.
self.assertEqual(qs.network_load(
read_fraction=1,
optimize='network',
), 2)
self.assertEqual(qs.network_load(
read_fraction=0,
optimize='network',
), 2)
self.assertEqual(qs.network_load(
read_fraction=1,
optimize='network',
load_limit = 0.25,
), 2)
self.assertEqual(qs.network_load(
read_fraction=0,
optimize='network',
load_limit = 0.5,
), 2)
self.assertEqual(qs.network_load(
read_fraction=1,
optimize='network',
latency_limit = s(2),
), 2)
self.assertEqual(qs.network_load(
read_fraction=0,
optimize='network',
latency_limit = s(3),
), 2)
# Latency Optimized.
self.assertEqual(qs.latency(read_fraction=1, optimize='latency'), s(2))
self.assertEqual(qs.latency(read_fraction=0, optimize='latency'), s(3))
self.assertEqual(qs.latency(
read_fraction=1,
optimize='latency',
load_limit = 1.0,
), s(2))
self.assertEqual(qs.latency(
read_fraction=0,
optimize='latency',
load_limit = 1.0,
), s(3))
self.assertEqual(qs.latency(
read_fraction=1,
optimize='latency',
network_limit = 2,
), s(2))
self.assertEqual(qs.latency(
read_fraction=0,
optimize='latency',
network_limit = 2,
), s(3))
# 1-Resilient Load Optimized.
self.assertEqual(qs.load(read_fraction=1, f=1), 0.5)
self.assertEqual(qs.capacity(read_fraction=1, f=1), 2)
self.assertEqual(qs.load(read_fraction=0, f=1), 1)
self.assertEqual(qs.capacity(read_fraction=0, f=1), 1)
# 1-Resilient Network Optimized.
self.assertEqual(
qs.network_load(read_fraction=1, optimize='network', f=1), 4)
self.assertEqual(
qs.network_load(read_fraction=0, optimize='network', f=1), 4)
# 1-Resilient Latency Optimized.
self.assertEqual(
qs.latency(read_fraction=1, optimize='latency', f=1), s(2))
self.assertEqual(
qs.latency(read_fraction=0, optimize='latency', f=1), s(3))
# Illegal Specification
with self.assertRaises(ValueError):
qs.strategy(read_fraction=0.1, optimize='load', load_limit=1)
with self.assertRaises(ValueError):
qs.strategy(read_fraction=0.1, optimize='network', network_limit=2)
with self.assertRaises(ValueError):
qs.strategy(read_fraction=0.1, optimize='latency',
latency_limit=s(5))
# Unsatisfiable Constraints
with self.assertRaises(NoStrategyFoundError):
qs.strategy(read_fraction=0,
optimize='load',
network_limit=1.5)
with self.assertRaises(NoStrategyFoundError):
qs.strategy(read_fraction=0,
optimize='load',
latency_limit=s(1))
with self.assertRaises(NoStrategyFoundError):
qs.strategy(read_fraction=1,
optimize='network',
load_limit=0.25,
latency_limit=s(2))