in scripts/capacity/calculator.py [0:0]
def calculate(args):
"""calculate job configuration according to requirements.
For segcache, returns a dict with:
cpu, ram, disk,
hash_power, seg_mem,
instance, host_limit, rack_limit,
memory_bound
For twemcache, returns a dict with:
cpu, ram, disk,
hash_power, slab_mem,
instance, host_limit, rack_limit,
memory_bound
For slimcache, return a dict with:
cpu, ram, disk,
item_size, nitem,
instance, host_limit, rack_limit,
memory_bound
"""
if args.failure_domain < FAILURE_DOMAIN_LOWER or args.failure_domain > FAILURE_DOMAIN_UPPER:
print('ERROR: failure domain should be between {:.1f}% and {:.1f}'.format(
FAILURE_DOMAIN_LOWER, FAILURE_DOMAIN_UPPER))
# first calculate njob disrecarding memory, note both njob & bottleneck are not yet final
njob_qps = int(ceil(1.0 * args.qps / KQPS))
njob_fd = int(ceil(100.0 / args.failure_domain))
if njob_qps >= njob_fd:
bottleneck = 'qps'
njob = njob_qps
else:
bottleneck = 'failure domain'
njob = njob_fd
# then calculate njob (vector) assuming memory-bound
# all ram-related values in this function are in MB
# amount of ram needed to store dataset, factoring in overhead
item_size = int(KEYVAL_ALIGNMENT * ceil(1.0 * (ITEM_OVERHEAD[args.runnable] + args.size) /
KEYVAL_ALIGNMENT))
ram_data = 1.0 * item_size * args.nkey * M / MB
# per-job memory overhead, in MB
ram_conn = int(ceil(1.0 * CONN_OVERHEAD * args.nconn / MB))
ram_fixed = BASE_OVERHEAD + SAFETY_BUF
njob_mem = []
sorted_ram = sorted(args.ram)
for ram in sorted_ram:
ram = ram * GB / MB # change unit to MB
n_low = int(ceil(ram_data / ram)) # number of shards, lower bound
nkey_per_shard = 1.0 * args.nkey * M / n_low # number of keys per shard, upper bound
hash_power, ram_hash = hash_parameters(nkey_per_shard, args.runnable) # upper bound for both
n = int(ceil(ram_data / (ram - ram_fixed - ram_conn - ram_hash)))
njob_mem.append(n)
# get final njob count; prefer larger ram if it reduces njob, which means:
# if cluster needs higher job ram AND more instances due to memory, update njob
# if cluster is memory-bound with smaller job ram but qps-bound with larger ones, use higher ram
# otherwise, use smaller job ram and keep njob value unchanged
index = 0 # if qps bound, use smallest ram setting
for i, n in reversed(list(enumerate(njob_mem))[1:]):
if n > njob or njob_mem[i - 1] > njob:
bottleneck = 'memory'
index = i
njob = max(njob, n)
break
if njob > WARNING_THRESHOLD:
print('WARNING: more than {} instances needed, please verify input.'.format(WARNING_THRESHOLD))
# recalculate hash parameters with the final job count
nkey_per_shard = 1.0 * (sorted_ram[index] * GB - ram_fixed * MB - ram_conn * MB) / item_size
# used by twemcache and segcache
hash_power, ram_hash = hash_parameters(nkey_per_shard, args.runnable)
slab_mem = sorted_ram[index] * GB / MB - ram_fixed - ram_conn - ram_hash
# only used by slimcache
nitem = int(NITEM_ALIGNMENT * floor(nkey_per_shard / NITEM_ALIGNMENT))
rack_limit = int(floor(njob * args.failure_domain / 100)) # >= 1 given how we calculate njob
host_limit = int(floor(min(MAX_HOST_LIMIT, max(1, rack_limit / RACK_TO_HOST_RATIO))))
ret = {
'cpu': CPU_PER_JOB,
'ram': sorted_ram[index],
'disk': DISK_PER_JOB,
'instance': njob,
'rack_limit': rack_limit,
'host_limit': host_limit,
'bottleneck': bottleneck}
if args.runnable == 'twemcache':
ret['hash_power'] = hash_power
ret['slab_mem'] = slab_mem
elif args.runnable == 'segcache':
ret['hash_power'] = hash_power
ret['seg_mem'] = slab_mem
elif args.runnable == 'slimcache':
ret['item_size'] = item_size
ret['nitem'] = nitem
return ret