In [5]:
from __future__ import print_function

from typing import List, Tuple, Sequence

from collections import namedtuple
import random

import simpy

from serversim import *


def simulate_deployment_scenario(num_users, weight1, weight2, server_range1,
                                 server_range2):
    # type: (int, float, float, Sequence[int], Sequence[int]) -> Result

    Result = namedtuple("Result", ["num_users", "weight1", "weight2", "server_range1",
                         "server_range2", "servers", "grp"])

    def cug(mid, delta):
        """Computation units generator"""
        def f():
            return random.uniform(mid - delta, mid + delta)
        return f

    def ld_bal(svc_name):
        """Application server load-balancer."""
        if svc_name == "svc_1":
            svr = random.choice(servers1)
        elif svc_name == "svc_2":
            svr = random.choice(servers2)
        else:
            assert False, "Invalid service type."
        return svr

    simtime = 200
    hw_threads = 10
    sw_threads = 20
    speed = 20
    svc_1_comp_units = 2.0
    svc_2_comp_units = 1.0
    quantiles = (0.5, 0.95, 0.99)

    env = simpy.Environment()

    n_servers = max(server_range1[-1] + 1, server_range2[-1] + 1)
    servers = [Server(env, hw_threads, sw_threads, speed, "AppServer_%s" % i)
               for i in range(n_servers)]
    servers1 = [servers[i] for i in server_range1]
    servers2 = [servers[i] for i in server_range2]

    svc_1 = CoreSvcRequester(env, "svc_1", cug(svc_1_comp_units,
                                               svc_1_comp_units*.9), ld_bal)
    svc_2 = CoreSvcRequester(env, "svc_2", cug(svc_2_comp_units,
                                               svc_2_comp_units*.9), ld_bal)

    weighted_txns = [(svc_1, weight1),
                     (svc_2, weight2)
                     ]

    min_think_time = 2.0  # .5 # 4
    max_think_time = 10.0  # 1.5 # 20
    svc_req_log = []  # type: List[Tuple[str, SvcRequest]]

    grp = UserGroup(env, num_users, "UserTypeX", weighted_txns, min_think_time,
                    max_think_time, quantiles, svc_req_log)
    grp.activate_users()

    env.run(until=simtime)

    return Result(num_users=num_users, weight1=weight1, weight2=weight2,
            server_range1=server_range1, server_range2=server_range2,
            servers=servers, grp=grp)

In [6]:
random.seed(123456)

In [7]:
sc1 = simulate_deployment_scenario(num_users=720, weight1=2, weight2=1, 
                                   server_range1=range(0, 10), server_range2=range(0, 10))

In [8]:
import seaborn as sns

from typing import TYPE_CHECKING, Sequence, Tuple
from collections import OrderedDict

import matplotlib.pyplot as plt
import pandas as pd

if TYPE_CHECKING:
    from serversim import UserGroup


def bands(time_resolution, grp):
    # type: (float, UserGroup) -> Tuple[Sequence[float], Sequence[float], Sequence[float], Sequence[float], Sequence[float], Sequence[float]]

    times = ((svc_req.time_dict["submitted"] // time_resolution) *
             time_resolution
             for (_, svc_req) in grp.svc_req_log
             if svc_req.is_completed)

    vals = (svc_req.time_dict["completed"] - svc_req.time_dict["submitted"]
            for (_, svc_req) in grp.svc_req_log
            if svc_req.is_completed)

    series = pd.Series(vals, index=times)
    grouped = series.groupby(level=0)

    counts_ser = grouped.count()
    ts = counts_ser.index.values
    counts = counts_ser.values
    means = grouped.mean().values
    q_50 = grouped.quantile(.50).values
    q_95 = grouped.quantile(.95).values
    q_05 = grouped.quantile(.05).values

    return ts, counts, means, q_50, q_95, q_05

def errorfill(x, y, ylow, yhigh, color='blue', alpha_fill=0.3, ax=None):
    ax = ax if ax is not None else plt.gca()
    if color is None:
        color = ax._get_lines.color_cycle.next()
    ax.plot(x, y, color=color)
    ax.fill_between(x, ylow, yhigh, color=color, alpha=alpha_fill)
    plt.show()

ts, _, means, _, q_95, q_05 = bands(5, sc1.grp)

errorfill(ts, means, q_05, q_95)