Tied up examples.

This commit is contained in:
Michael Whittaker 2021-02-07 12:19:08 -08:00
parent 9f0532a7d5
commit 3cfb26e281
7 changed files with 227 additions and 243 deletions

View file

@ -1,3 +1,8 @@
"""
This script contains the code used in the case study of our paper
(https://mwhittaker.github.io/publications/quoracle.pdf).
"""
# See https://stackoverflow.com/a/19521297/3187068 # See https://stackoverflow.com/a/19521297/3187068
import matplotlib import matplotlib
matplotlib.use('pdf') matplotlib.use('pdf')
@ -8,6 +13,7 @@ from quoracle import *
import datetime import datetime
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def main() -> None: def main() -> None:
def seconds(x: int) -> datetime.timedelta: def seconds(x: int) -> datetime.timedelta:
return datetime.timedelta(seconds=x) return datetime.timedelta(seconds=x)

View file

@ -1,71 +0,0 @@
from quoracle import *
def load(qs: QuorumSystem, fr: float, f: int) -> float:
try:
return qs.load(read_fraction=fr, f=f)
except ValueError:
return float('inf')
def main():
a = Node('a')
b = Node('b')
c = Node('c')
d = Node('d')
e = Node('e')
reads_examples = [
# 1 node.
a,
# 2 nodes.
choose(1, [a, b]),
choose(2, [a, b]),
# 3 nodes.
choose(1, [a, b, c]),
choose(2, [a, b, c]),
choose(3, [a, b, c]),
# 4 nodes.
a*b + c*d,
(a+b)*(c+d),
choose(1, [a, b, c, d]),
choose(2, [a, b, c, d]),
choose(3, [a, b, c, d]),
choose(4, [a, b, c, d]),
# 5 nodes.
a*b + a*c*e + d*e + d*c*b,
a*b + c*d*e,
(a+b) * (c+d+e),
(a+b) * (c+d+e),
a + b*c + d*e,
a * (b+c) * (d+e),
choose(1, [a, b, c, d, e]),
choose(2, [a, b, c, d, e]),
choose(3, [a, b, c, d, e]),
choose(4, [a, b, c, d, e]),
choose(5, [a, b, c, d, e]),
]
fs = [0, 1, 2]
frs = [0, 0.25, 0.5, 0.75, 1]
header = (['Quorum System', 'n', 'Dup Free?', 'Read Resilience',
'Write Resilience', 'Resilience'] +
[f'f={f},fr={fr}' for f in fs for fr in frs])
print(';'.join(header))
for reads in reads_examples:
qs = QuorumSystem(reads=reads)
data = ([reads, len(qs.nodes()), qs.dup_free(), qs.read_resilience(),
qs.write_resilience(), qs.resilience()]+
['{:.4f}'.format(load(qs, fr, f=f))
for f in [0, 1, 2]
for fr in [0, 0.25, 0.5, 0.75, 1]])
print(';'.join(str(x) for x in data))
if __name__ == '__main__':
main()

View file

@ -1,3 +1,8 @@
"""
This script contains the code used in our paper
(https://mwhittaker.github.io/publications/quoracle.pdf).
"""
from quoracle import * from quoracle import *
import datetime import datetime

View file

@ -1,9 +1,11 @@
from quoracle import * from quoracle import *
import argparse
import matplotlib import matplotlib
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import os.path
def main(): def main(output_directory: str):
a = Node('a', capacity=100) a = Node('a', capacity=100)
b = Node('b', capacity=200) b = Node('b', capacity=200)
c = Node('c', capacity=100) c = Node('c', capacity=100)
@ -18,34 +20,40 @@ def main():
} }
for name, qs in quorum_systems.items(): for name, qs in quorum_systems.items():
d = {0.0: 1, 0.1: 1, 0.2: 1, 0.3: 1, 0.4: 1, 0.5: 1, dist = {0.0: 1., 0.1: 1., 0.2: 1., 0.3: 1., 0.4: 1., 0.5: 1.,
0.6: 1, 0.7: 1, 0.8: 1, 0.9: 1, 1.0: 1} 0.6: 1., 0.7: 1., 0.8: 1., 0.9: 1., 1.0: 1.}
fig, axes = plt.subplots(3, 4, figsize=(6 * 2, 4 * 2), sharey='all') fig, axes = plt.subplots(3, 4, figsize=(6 * 2, 4 * 2), sharey='all')
axes_iter = (axes[row][col] for row in range(3) for col in range(4)) axes_iter = (axes[row][col] for row in range(3) for col in range(4))
for fr in d.keys(): for fr in dist.keys():
sigma = qs.strategy(read_fraction=fr) sigma = qs.strategy(read_fraction=fr)
ax = next(axes_iter) ax = next(axes_iter)
plot_load_distribution_on(ax, sigma, nodes) plot_load_distribution_on(ax, sigma, nodes)
ax.set_title(f'Optimized For\nRead Fraction = {fr}') ax.set_title(f'Optimized For\nRead Fraction = {fr}')
ax.set_xlabel('Read Fraction') ax.set_xlabel('Read Fraction')
ax.grid() ax.grid()
# ax.legend()
sigma = qs.strategy(read_fraction=d) sigma = qs.strategy(read_fraction=dist)
ax = next(axes_iter) ax = next(axes_iter)
plot_load_distribution_on(ax, sigma, nodes) plot_load_distribution_on(ax, sigma, nodes)
ax.set_title('Optimized For\nUniform Read Fraction') ax.set_title('Optimized For\nUniform Read Fraction')
ax.set_xlabel('Read Fraction') ax.set_xlabel('Read Fraction')
ax.grid() ax.grid()
# ax.legend()
axes[0][0].set_ylabel('Load') axes[0][0].set_ylabel('Load')
axes[1][0].set_ylabel('Load') axes[1][0].set_ylabel('Load')
axes[2][0].set_ylabel('Load') axes[2][0].set_ylabel('Load')
fig.tight_layout() fig.tight_layout()
fig.savefig(f'{name}.pdf') output_filename = os.path.join(output_directory, f'{name}.pdf')
fig.savefig(output_filename)
print(f'Wrote figure to "{output_filename}".')
if __name__ == '__main__': if __name__ == '__main__':
main() parser = argparse.ArgumentParser()
parser.add_argument('--output',
type=str,
default='.',
help='Output directory')
args = parser.parse_args()
main(args.output)

View file

@ -1,10 +1,17 @@
"""
This script shows how to use plot_node_load_on, plot_node_utilization, and
plot_node_throughput to plot the load, utilization, and throughput of nodes in
a read-write quorum system.
"""
from quoracle import * from quoracle import *
import argparse
import datetime import datetime
import matplotlib import matplotlib
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def main(): def main(output_filename: str) -> None:
a = Node('a', write_capacity=1000, read_capacity=10000) a = Node('a', write_capacity=1000, read_capacity=10000)
b = Node('b', write_capacity=500, read_capacity=5000) b = Node('b', write_capacity=500, read_capacity=5000)
c = Node('c', write_capacity=1000, read_capacity=10000) c = Node('c', write_capacity=1000, read_capacity=10000)
@ -31,11 +38,18 @@ def main():
ax[0][2].set_title('Paths') ax[0][2].set_title('Paths')
ax[0][3].set_title(f'Opt {opt.reads}') ax[0][3].set_title(f'Opt {opt.reads}')
ax[0][0].set_ylabel('Load') ax[0][0].set_ylabel('Load')
ax[1][0].set_ylabel('Utilization at Peak Throughput') ax[1][0].set_ylabel('Utilization')
ax[2][0].set_ylabel('Throughput at Peak Throughput') ax[2][0].set_ylabel('Throughput')
fig.tight_layout() fig.tight_layout()
fig.savefig('node_loads.pdf') fig.savefig(output_filename)
print(f'Wrote figure to "{output_filename}".')
if __name__ == '__main__': if __name__ == '__main__':
main() parser = argparse.ArgumentParser()
parser.add_argument('--output',
type=str,
default='node_loads.pdf',
help='Output filename')
args = parser.parse_args()
main(args.output)

View file

@ -1,3 +1,11 @@
"""
In this script, we generate a strategy sigma that is optimal for a distribution
of read fractions. We plot this strategy's capacity as a function of read
fraction and compare it to other strategies optimized for specific points in
this distribution. This plot was used in our paper
(https://mwhittaker.github.io/publications/quoracle.pdf).
"""
# See https://stackoverflow.com/a/19521297/3187068 # See https://stackoverflow.com/a/19521297/3187068
import matplotlib import matplotlib
matplotlib.use('pdf') matplotlib.use('pdf')
@ -5,12 +13,13 @@ font = {'size': 8}
matplotlib.rc('font', **font) matplotlib.rc('font', **font)
from quoracle import * from quoracle import *
import argparse
import itertools import itertools
import matplotlib import matplotlib
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def main(): def main(output_filename: str) -> None:
a = Node('a', write_capacity=100, read_capacity=200) a = Node('a', write_capacity=100, read_capacity=200)
b = Node('b', write_capacity=100, read_capacity=200) b = Node('b', write_capacity=100, read_capacity=200)
c = Node('c', write_capacity=50, read_capacity=100) c = Node('c', write_capacity=50, read_capacity=100)
@ -44,8 +53,15 @@ def main():
ax.set_xticks([0, 0.25, 0.5, 0.75, 1]) ax.set_xticks([0, 0.25, 0.5, 0.75, 1])
ax.grid() ax.grid()
fig.tight_layout() fig.tight_layout()
fig.savefig(f'workload_distribution.pdf') fig.savefig(output_filename)
print(f'Wrote figure to "{output_filename}".')
if __name__ == '__main__': if __name__ == '__main__':
main() parser = argparse.ArgumentParser()
parser.add_argument('--output',
type=str,
default='workload_distribution.pdf',
help='Output filename')
args = parser.parse_args()
main(args.output)

View file

@ -1,188 +1,190 @@
## Quorum Systems
from quoracle import * from quoracle import *
a = Node('a')
b = Node('b')
c = Node('c')
d = Node('d')
e = Node('e')
f = Node('f')
grid = QuorumSystem(reads=a*b*c + d*e*f) def main() -> None:
## Quorum Systems
a = Node('a')
b = Node('b')
c = Node('c')
d = Node('d')
e = Node('e')
f = Node('f')
for r in grid.read_quorums(): grid = QuorumSystem(reads=a*b*c + d*e*f)
for r in grid.read_quorums():
print(r) print(r)
for w in grid.write_quorums(): for w in grid.write_quorums():
print(w) print(w)
QuorumSystem(writes=(a + b + c) * (d + e + f)) QuorumSystem(writes=(a + b + c) * (d + e + f))
QuorumSystem(reads=a*b*c + d*e*f, writes=(a + b + c) * (d + e + f)) QuorumSystem(reads=a*b*c + d*e*f, writes=(a + b + c) * (d + e + f))
# QuorumSystem(reads=a+b+c, writes=d+e+f) # QuorumSystem(reads=a+b+c, writes=d+e+f)
# ValueError: Not all read quorums intersect all write quorums # ValueError: Not all read quorums intersect all write quorums
print(grid.is_read_quorum({'a', 'b', 'c'})) # True print(grid.is_read_quorum({'a', 'b', 'c'})) # True
print(grid.is_read_quorum({'a', 'b', 'c', 'd'})) # True print(grid.is_read_quorum({'a', 'b', 'c', 'd'})) # True
print(grid.is_read_quorum({'a', 'b', 'd'})) # False print(grid.is_read_quorum({'a', 'b', 'd'})) # False
print(grid.is_write_quorum({'a', 'd'})) # True print(grid.is_write_quorum({'a', 'd'})) # True
print(grid.is_write_quorum({'a', 'd', 'd'})) # True print(grid.is_write_quorum({'a', 'd', 'd'})) # True
print(grid.is_write_quorum({'a', 'b'})) # False print(grid.is_write_quorum({'a', 'b'})) # False
## Resilience ## Resilience
print(grid.read_resilience()) # 1 print(grid.read_resilience()) # 1
print(grid.write_resilience()) # 2 print(grid.write_resilience()) # 2
print(grid.resilience()) # 1 print(grid.resilience()) # 1
## Strategies ## Strategies
# The read quorum strategy. # The read quorum strategy.
sigma_r = { sigma_r = {
frozenset({'a', 'b', 'c'}): 2., frozenset({'a', 'b', 'c'}): 2.,
frozenset({'d', 'e', 'f'}): 1., frozenset({'d', 'e', 'f'}): 1.,
} }
# The write quorum strategy. # The write quorum strategy.
sigma_w = { sigma_w = {
frozenset({'a', 'd'}): 1., frozenset({'a', 'd'}): 1.,
frozenset({'b', 'e'}): 1., frozenset({'b', 'e'}): 1.,
frozenset({'c', 'f'}): 1., frozenset({'c', 'f'}): 1.,
} }
strategy = grid.make_strategy(sigma_r, sigma_w) strategy = grid.make_strategy(sigma_r, sigma_w)
print(strategy.get_read_quorum()) print(strategy.get_read_quorum())
print(strategy.get_read_quorum()) print(strategy.get_read_quorum())
print(strategy.get_read_quorum()) print(strategy.get_read_quorum())
print(strategy.get_read_quorum()) print(strategy.get_read_quorum())
print(strategy.get_write_quorum()) print(strategy.get_write_quorum())
print(strategy.get_write_quorum()) print(strategy.get_write_quorum())
print(strategy.get_write_quorum()) print(strategy.get_write_quorum())
print(strategy.get_write_quorum()) print(strategy.get_write_quorum())
## Load and Capacity ## Load and Capacity
print(strategy.load(read_fraction=1)) # 2/3 print(strategy.load(read_fraction=1)) # 2/3
print(strategy.load(write_fraction=1)) # 1/3 print(strategy.load(write_fraction=1)) # 1/3
print(strategy.load(read_fraction=0.25)) # 5/12 print(strategy.load(read_fraction=0.25)) # 5/12
print(strategy.node_load(a, read_fraction=0.25)) # 5/12 print(strategy.node_load(a, read_fraction=0.25)) # 5/12
print(strategy.node_load(b, read_fraction=0.25)) # 5/12 print(strategy.node_load(b, read_fraction=0.25)) # 5/12
print(strategy.node_load(c, read_fraction=0.25)) # 5/12 print(strategy.node_load(c, read_fraction=0.25)) # 5/12
print(strategy.node_load(d, read_fraction=0.25)) # 1/3 print(strategy.node_load(d, read_fraction=0.25)) # 1/3
print(strategy.node_load(e, read_fraction=0.25)) # 1/3 print(strategy.node_load(e, read_fraction=0.25)) # 1/3
print(strategy.node_load(f, read_fraction=0.25)) # 1/3 print(strategy.node_load(f, read_fraction=0.25)) # 1/3
strategy = grid.strategy(read_fraction=0.25) strategy = grid.strategy(read_fraction=0.25)
print(strategy) print(strategy)
print(strategy.load(read_fraction=0.25)) # 3/8 print(strategy.load(read_fraction=0.25)) # 3/8
print(strategy.load(read_fraction=0)) # 1/3 print(strategy.load(read_fraction=0)) # 1/3
print(strategy.load(read_fraction=0.5)) # 5/12 print(strategy.load(read_fraction=0.5)) # 5/12
print(strategy.load(read_fraction=1)) # 1/2 print(strategy.load(read_fraction=1)) # 1/2
print(grid.load(read_fraction=0.25)) # 3/8 print(grid.load(read_fraction=0.25)) # 3/8
print(grid.capacity(read_fraction=0.25)) # 8/3 print(grid.capacity(read_fraction=0.25)) # 8/3
## Workload Distributions ## Workload Distributions
distribution = {0.1: 0.5, 0.75: 0.5} distribution = {0.1: 0.5, 0.75: 0.5}
strategy = grid.strategy(read_fraction=distribution) strategy = grid.strategy(read_fraction=distribution)
print(strategy.load(read_fraction=distribution)) # 0.404 print(strategy.load(read_fraction=distribution)) # 0.404
## Heterogeneous Node ## Heterogeneous Node
a = Node('a', capacity=1000) a = Node('a', capacity=1000)
b = Node('b', capacity=500) b = Node('b', capacity=500)
c = Node('c', capacity=1000) c = Node('c', capacity=1000)
d = Node('d', capacity=500) d = Node('d', capacity=500)
e = Node('e', capacity=1000) e = Node('e', capacity=1000)
f = Node('f', capacity=500) f = Node('f', capacity=500)
grid = QuorumSystem(reads=a*b*c + d*e*f) grid = QuorumSystem(reads=a*b*c + d*e*f)
strategy = grid.strategy(read_fraction=0.75) strategy = grid.strategy(read_fraction=0.75)
print(strategy.load(read_fraction=0.75)) # 0.00075 print(strategy.load(read_fraction=0.75)) # 0.00075
print(strategy.capacity(read_fraction=0.75)) # 1333 print(strategy.capacity(read_fraction=0.75)) # 1333
a = Node('a', write_capacity=1000, read_capacity=10000) a = Node('a', write_capacity=1000, read_capacity=10000)
b = Node('b', write_capacity=500, read_capacity=5000) b = Node('b', write_capacity=500, read_capacity=5000)
c = Node('c', write_capacity=1000, read_capacity=10000) c = Node('c', write_capacity=1000, read_capacity=10000)
d = Node('d', write_capacity=500, read_capacity=5000) d = Node('d', write_capacity=500, read_capacity=5000)
e = Node('e', write_capacity=1000, read_capacity=10000) e = Node('e', write_capacity=1000, read_capacity=10000)
f = Node('f', write_capacity=500, read_capacity=5000) f = Node('f', write_capacity=500, read_capacity=5000)
grid = QuorumSystem(reads=a*b*c + d*e*f) grid = QuorumSystem(reads=a*b*c + d*e*f)
print(grid.capacity(read_fraction=1)) # 10,000 print(grid.capacity(read_fraction=1)) # 10,000
print(grid.capacity(read_fraction=0.5)) # 3913 print(grid.capacity(read_fraction=0.5)) # 3913
print(grid.capacity(read_fraction=0)) # 2000 print(grid.capacity(read_fraction=0)) # 2000
## f-resilient Strategies ## f-resilient Strategies
strategy = grid.strategy(read_fraction=0.5, f=1) strategy = grid.strategy(read_fraction=0.5, f=1)
print(strategy.get_read_quorum()) print(strategy.get_read_quorum())
print(strategy.get_write_quorum()) print(strategy.get_write_quorum())
print(grid.capacity(write_fraction=1, f=0)) print(grid.capacity(write_fraction=1, f=0))
print(grid.capacity(write_fraction=1, f=1)) print(grid.capacity(write_fraction=1, f=1))
write2 = QuorumSystem(writes=choose(2, [a, b, c, d, e])) write2 = QuorumSystem(writes=choose(2, [a, b, c, d, e]))
print(write2.capacity(write_fraction=1, f=0)) print(write2.capacity(write_fraction=1, f=0))
print(write2.capacity(write_fraction=1, f=1)) print(write2.capacity(write_fraction=1, f=1))
## Latency ## Latency
import datetime import datetime
def seconds(x: int) -> datetime.timedelta: def seconds(x: int) -> datetime.timedelta:
return datetime.timedelta(seconds=x) return datetime.timedelta(seconds=x)
a = Node('a', write_capacity=1000, read_capacity=10000, latency=seconds(1)) a = Node('a', write_capacity=1000, read_capacity=10000, latency=seconds(1))
b = Node('b', write_capacity=500, read_capacity=5000, latency=seconds(2)) b = Node('b', write_capacity=500, read_capacity=5000, latency=seconds(2))
c = Node('c', write_capacity=1000, read_capacity=10000, latency=seconds(3)) c = Node('c', write_capacity=1000, read_capacity=10000, latency=seconds(3))
d = Node('d', write_capacity=500, read_capacity=5000, latency=seconds(4)) d = Node('d', write_capacity=500, read_capacity=5000, latency=seconds(4))
e = Node('e', write_capacity=1000, read_capacity=10000, latency=seconds(5)) e = Node('e', write_capacity=1000, read_capacity=10000, latency=seconds(5))
f = Node('f', write_capacity=500, read_capacity=5000, latency=seconds(6)) f = Node('f', write_capacity=500, read_capacity=5000, latency=seconds(6))
grid = QuorumSystem(reads=a*b*c + d*e*f) grid = QuorumSystem(reads=a*b*c + d*e*f)
sigma = grid.strategy(read_fraction=0.5, optimize='latency') sigma = grid.strategy(read_fraction=0.5, optimize='latency')
print(sigma) print(sigma)
print(sigma.latency(read_fraction=1)) print(sigma.latency(read_fraction=1))
print(sigma.latency(read_fraction=0)) print(sigma.latency(read_fraction=0))
print(sigma.latency(read_fraction=0.5)) print(sigma.latency(read_fraction=0.5))
print(grid.latency(read_fraction=0.5, optimize='latency')) print(grid.latency(read_fraction=0.5, optimize='latency'))
sigma = grid.strategy(read_fraction=0.5, sigma = grid.strategy(read_fraction=0.5,
optimize='latency', optimize='latency',
load_limit=1/1500) load_limit=1/1500)
print(sigma) print(sigma)
print(sigma.capacity(read_fraction=0.5)) print(sigma.capacity(read_fraction=0.5))
print(sigma.latency(read_fraction=0.5)) print(sigma.latency(read_fraction=0.5))
sigma = grid.strategy(read_fraction=0.5, sigma = grid.strategy(read_fraction=0.5,
optimize='load', optimize='load',
latency_limit=seconds(4)) latency_limit=seconds(4))
print(sigma) print(sigma)
print(sigma.capacity(read_fraction=0.5)) print(sigma.capacity(read_fraction=0.5))
print(sigma.latency(read_fraction=0.5)) print(sigma.latency(read_fraction=0.5))
# grid.strategy(read_fraction=0.5, # grid.strategy(read_fraction=0.5,
# optimize='load', # optimize='load',
# latency_limit=seconds(1)) # latency_limit=seconds(1))
# quoracle.quorum_system.NoStrategyFoundError: no strategy satisfies the given constraints # quoracle.quorum_system.NoStrategyFoundError: no strategy satisfies the given constraints
## Network Load ## Network Load
sigma = grid.strategy(read_fraction=0.5, optimize='network') sigma = grid.strategy(read_fraction=0.5, optimize='network')
print(sigma) print(sigma)
print(sigma.network_load(read_fraction=0.5)) print(sigma.network_load(read_fraction=0.5))
print(grid.network_load(read_fraction=0.5, optimize='network')) print(grid.network_load(read_fraction=0.5, optimize='network'))
sigma = grid.strategy(read_fraction=0.5, sigma = grid.strategy(read_fraction=0.5,
optimize='network', optimize='network',
load_limit=1/2000, load_limit=1/2000,
latency_limit=seconds(4)) latency_limit=seconds(4))
## Search ## Search
qs, sigma = search(nodes=[a, b, c, d, e, f], qs, sigma = search(nodes=[a, b, c, d, e, f],
resilience=1, resilience=1,
f=1, f=1,
read_fraction=0.75, read_fraction=0.75,
@ -190,8 +192,12 @@ qs, sigma = search(nodes=[a, b, c, d, e, f],
latency_limit=seconds(4), latency_limit=seconds(4),
network_limit=4, network_limit=4,
timeout=seconds(60)) timeout=seconds(60))
print(qs) print(qs)
print(sigma) print(sigma)
print(sigma.capacity(read_fraction=0.75)) print(sigma.capacity(read_fraction=0.75))
print(sigma.latency(read_fraction=0.75)) print(sigma.latency(read_fraction=0.75))
print(sigma.network_load(read_fraction=0.75)) print(sigma.network_load(read_fraction=0.75))
if __name__ == '__main__':
main()