Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding script to generate (stacked) server instances on a single host #206

Merged
merged 32 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9ba9ead
adding script to generate (stacked) server instances on a single host
Nov 30, 2018
a946a4d
fix missing prefix for bring-up.sh
Nov 30, 2018
5bfd160
fix some bugs wrt file/dir creation
Dec 1, 2018
84bf421
adding client script
Dec 1, 2018
ef6d849
add config option for key/val size (which adjusts hash_power value)
Dec 12, 2018
c02b8d4
adding bash script to generate server config groups
Dec 12, 2018
6e5daa0
Merge branch 'master' into scripts
Dec 14, 2018
e4e9c53
making config file path relative/local to bring-up.sh
Dec 15, 2018
409f010
using memcache style timestamp to work with rpcperf properly (which a…
Dec 15, 2018
9f7621c
update server-side config generator with prefill options and minor fixes
Dec 18, 2018
e046389
Merge branch 'master' of github.com:twitter/pelikan into scripts
Dec 18, 2018
9f79189
update client_config.py to match (server) prefilling pattern
Dec 18, 2018
91abd18
update client config parameters per Brian's suggestion
Dec 19, 2018
2c4ca06
improve client config; add comment; add waterfall chart output; add l…
Dec 19, 2018
95f6822
flattern log dir config for stacked server jobs
Dec 20, 2018
ac89b1a
add script to launch a single test
Dec 20, 2018
102623c
make changes needed to launch runtest.sh from the client host
Dec 20, 2018
7e121a5
uncomment client_run
Dec 20, 2018
96c4e96
Merge branch 'master' into scripts
Jan 14, 2019
1579a8e
make binary & server_ip required parameters
Jan 15, 2019
77adec2
fix typo
Jan 15, 2019
53da822
make rpc-perf binary location configurable too
Jan 15, 2019
aa35a96
adapt client config for new rpc-perf which has different config syntax
Jan 15, 2019
ab47258
further parameterization
Jan 15, 2019
2a0d4e7
misnomer corrected
Jan 15, 2019
a51bfc6
good day for typo
Jan 15, 2019
3e09291
more one word change
Jan 15, 2019
f99cce8
restoring waterfall now that rpc-perf (prerelease) has the feature ad…
Jan 17, 2019
d3549b2
Merge branch 'master' into scripts
Jan 17, 2019
efd291c
Merge branch 'master' into scripts
Jan 29, 2019
8bd14db
add stats log related config to server
Jan 29, 2019
e0cd439
increase timing wheel size to allow 10 second timeout
Jan 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions scripts/load_testing/client_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import argparse
from math import ceil
import os


INSTANCES = 3
PREFIX = 'loadgen'
RPCPERF_THREADS = 1
RPCPERF_CONNS = 100
RPCPERF_RATE = 10000
RPCPERF_GET_WEIGHT = 9
RPCPERF_SET_WEIGHT = 1
PELIKAN_SLAB_MEM = 4294967296
PELIKAN_ITEM_OVERHEAD = 48
KSIZE = 32
VSIZE = 32
PELIKAN_SERVER_PORT = 12300


def generate_config(rate, connections, vsize, slab_mem, threads):
# create rpcperf.toml
nkey = int(ceil(1.0 * slab_mem / (vsize + KSIZE + PELIKAN_ITEM_OVERHEAD)))
conn_per_thread = connections / threads

config_str = '''
[general]
clients = {threads}
tcp-nodelay = true
poolsize = {connections} # this specifies number of connection per thread
# runtime ~= windows x duration
windows = 2
interval = 60
request_ratelimit = {rate}

thinkingfish marked this conversation as resolved.
Show resolved Hide resolved
[[keyspace]]
length = {ksize}
count = {nkey}
weight = 1
commands = [
{{action = "get", weight = {get_weight}}},
{{action = "set", weight = {set_weight}}},
]
values = [
{{length = {vsize}, weight = 1}},
]'''.format(threads=threads, connections=conn_per_thread, nkey=nkey, rate=rate,
ksize=KSIZE, vsize=vsize, get_weight=RPCPERF_GET_WEIGHT, set_weight=RPCPERF_SET_WEIGHT)

with open('rpcperf.toml', 'w') as the_file:
the_file.write(config_str)


def generate_runscript(binary, server_ip, instances):
# create test.sh
fname = 'test.sh'
with open(fname, 'w') as the_file:
for i in range(instances):
server_port = PELIKAN_SERVER_PORT + i
the_file.write('{binary_file} --config {config_file}'.format(binary_file=binary, config_file='rpcperf.toml'))
the_file.write(' --endpoint {server_ip}:{server_port}'.format(server_ip=server_ip, server_port=server_port))
the_file.write(' --waterfall latency-waterfall-{server_port}.png'.format(server_port=server_port))
the_file.write(' > rpcperf_{server_port}.log'.format(server_port=server_port))
the_file.write(' 2>&1 &\n')
os.chmod(fname, 0777)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="""
Generate all the client-side scripts/configs needed for a test run.
""")
parser.add_argument('--binary', dest='binary', type=str, help='location of rpc-perf binary', required=True)
parser.add_argument('--prefix', dest='prefix', type=str, default=PREFIX, help='folder that contains all the other files to be generated')
parser.add_argument('--instances', dest='instances', type=int, default=INSTANCES, help='number of instances')
parser.add_argument('--server_ip', dest='server_ip', type=str, help='server ip', required=True)
parser.add_argument('--rate', dest='rate', type=int, default=RPCPERF_RATE, help='request rate per instance')
parser.add_argument('--connections', dest='connections', type=int, default=RPCPERF_CONNS, help='number of connections per instance')
parser.add_argument('--vsize', dest='vsize', type=int, default=VSIZE, help='value size')
parser.add_argument('--slab_mem', dest='slab_mem', type=int, default=PELIKAN_SLAB_MEM, help='slab memory')
parser.add_argument('--threads', dest='threads', type=int, default=RPCPERF_THREADS, help='number of worker threads per rpc-perf')

args = parser.parse_args()

if not os.path.exists(args.prefix):
os.makedirs(args.prefix)
os.chdir(args.prefix)

generate_config(args.rate, args.connections, args.vsize, args.slab_mem, args.threads)
generate_runscript(args.binary, args.server_ip, args.instances)
90 changes: 90 additions & 0 deletions scripts/load_testing/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash

conn_configs=(100 1000 10000)
mem_configs=(4 8 16 32)
size_configs=(64 128 256 512 1024 2048)
instances=30
ksize=32
rate=100000
threads=2
thinkingfish marked this conversation as resolved.
Show resolved Hide resolved

# Initialize our own variables:
client=false
server=false
rpcperf="rpc-perf"
pelikan="pelikan_twemcache"
target="127.0.0.1"

show_help()
{
echo "generate.sh [-c [-r path/to/rpcperf] [-t target/serverIP]] [-s [-p path/to/pelikan]]"
}

get_args()
{
while getopts ":p:r:t:csh" opt; do
case "$opt" in
c) client=true
;;
s) server=true
;;
p) pelikan=$OPTARG
;;
r) rpcperf=$OPTARG
;;
t) target=$OPTARG
;;
h)
show_help
exit 0
;;
\?)
echo "unrecognized option $opt"
show_help
exit 1
;;
esac
done
}

# pelikan configs
gen_pelikan()
{
for size in "${size_configs[@]}"
do
vsize=$((size - ksize))
for mem in "${mem_configs[@]}"
do
slab_mem=$((mem * 1024 * 1024 * 1024))
prefix=pelikan_${size}_${mem}
python server_config.py --prefix="$prefix" --binary="$pelikan" --instances="$instances" --slab_mem "$slab_mem" --vsize "$vsize"
done
done
}

# rpc-perf configs
gen_rpcperf()
{
for conn in "${conn_configs[@]}"
do
for size in "${size_configs[@]}"
do
vsize=$((size - ksize))
for mem in "${mem_configs[@]}"
do
slab_mem=$((mem * 1024 * 1024 * 1024))
prefix=rpcperf_${conn}_${size}_${mem}
python client_config.py --prefix="$prefix" --binary="$rpcperf" --server_ip="$target" --instances="$instances" --rate="$rate" --connections="$conn" --vsize "$vsize" --slab_mem="$slab_mem" --threads="$threads"
done
done
done
}

get_args "${@}"
if [ "$client" = true ]; then
gen_rpcperf
fi
if [ "$server" = true ]; then
gen_pelikan
fi

70 changes: 70 additions & 0 deletions scripts/load_testing/runtest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash

# NOTE(!!): the script only works when all config folders are freshly created,
# i.e. no data from previous runs

# Initialize our own variables:
client_config=""
server_config=""
target=""

show_help()
{
echo "runtest.sh -c <client_config_path> -s <server_config_path> -t <target: host where servers run>"
}

get_args()
{
while getopts ":c:s:t:h" opt; do
case "$opt" in
c) client_config=$OPTARG
;;
s) server_config=$OPTARG
;;
t) target=$OPTARG
;;
h)
show_help
exit 0
;;
\?)
echo "unrecognized option $opt"
show_help
exit 1
;;
esac
done
}

server_launch()
{
ssh -C "$target" "cd $server_config && ./warm-up.sh"
}


client_run()
{
cd "$client_config" || exit 1

./test.sh

local nrunning=1
while [ $nrunning -gt 0 ]
do
nrunning=$(pgrep -c rpc-perf)
echo "$(date): $nrunning clients are still running"
sleep 10
done

cd - > /dev/null || exit 1
}

wrap_up()
{
ssh -C "$target" "pkill -f pelikan_twemcache"
}

get_args "${@}"
server_launch
client_run
wrap_up
130 changes: 130 additions & 0 deletions scripts/load_testing/server_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import argparse
from math import ceil, floor, log
import os

INSTANCES = 3
PREFIX = 'test'
PELIKAN_ADMIN_PORT = 9900
PELIKAN_SERVER_PORT = 12300
PELIKAN_SLAB_MEM = 4294967296
PELIKAN_ITEM_OVERHEAD = 48
KSIZE = 32
VSIZE = 32
THREAD_PER_SOCKET = 48
BIND_TO_CORES = False
BIND_TO_NODES = True

def generate_config(instances, vsize, slab_mem):
# create top-level folders under prefix
try:
os.makedirs('config')
except:
pass
try:
os.makedirs('log')
except:
pass

nkey = int(ceil(1.0 * slab_mem / (vsize + KSIZE + PELIKAN_ITEM_OVERHEAD)))
hash_power = int(ceil(log(nkey, 2)))

# create twemcache config file(s)
for i in range(instances):
admin_port = PELIKAN_ADMIN_PORT + i
server_port = PELIKAN_SERVER_PORT + i
config_file = 'twemcache-{server_port}.config'.format(server_port=server_port)
config_str = """\
daemonize: yes
admin_port: {admin_port}
server_port: {server_port}

admin_tw_cap: 2000

buf_init_size: 4096

buf_sock_poolsize: 16384

debug_log_level: 5
debug_log_file: log/twemcache-{server_port}.log
debug_log_nbuf: 1048576

klog_file: log/twemcache-{server_port}.cmd
klog_backup: log/twemcache-{server_port}.cmd.old
klog_sample: 100
klog_max: 1073741824

prefill: yes
prefill_ksize: 32
prefill_vsize: {vsize}
prefill_nkey: {nkey}

request_poolsize: 16384
response_poolsize: 32768

slab_evict_opt: 1
slab_prealloc: yes
slab_hash_power: {hash_power}
slab_mem: {slab_mem}
slab_size: 1048756

stats_intvl: 10000
stats_log_file: log/twemcache-{server_port}.stats

time_type: 2
""".format(admin_port=admin_port, server_port=server_port, vsize=vsize, nkey=nkey, hash_power=hash_power, slab_mem=slab_mem)
with open(os.path.join('config', config_file),'w') as the_file:
the_file.write(config_str)

def generate_runscript(binary, instances):
# create bring-up.sh
fname = 'bring-up.sh'
with open(fname, 'w') as the_file:
for i in range(instances):
config_file = os.path.join('config', 'twemcache-{server_port}.config'.format(server_port=PELIKAN_SERVER_PORT+i))
if BIND_TO_NODES:
the_file.write('sudo numactl --cpunodebind={numa_node} --preferred={numa_node} '.format(
numa_node=i%2))
elif BIND_TO_CORES:
the_file.write('sudo numactl --physcpubind={physical_thread},{logical_thread} '.format(
physical_thread=i,
logical_thread=i+THREAD_PER_SOCKET))
the_file.write('{binary_file} {config_file}\n'.format(
binary_file=binary,
config_file=config_file))
os.chmod(fname, 0777)

# create warm-up.sh
fname = 'warm-up.sh'
with open(fname, 'w') as the_file:
the_file.write("""
./bring-up.sh

nready=0
while [ $nready -lt {instances} ]
do
nready=$(grep -l "prefilling slab" log/twemcache-*.log | wc -l)
echo "$(date): $nready out of {instances} servers are warmed up"
sleep 10
done
""".format(instances=instances))
os.chmod(fname, 0777)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="""
Generate all the server-side scripts/configs needed for a test run.
""")
parser.add_argument('--binary', dest='binary', type=str, help='location of pelikan_twemcache binary', required=True)
parser.add_argument('--prefix', dest='prefix', type=str, default=PREFIX, help='folder that contains all the other files to be generated')
parser.add_argument('--instances', dest='instances', type=int, default=INSTANCES, help='number of instances')
parser.add_argument('--vsize', dest='vsize', type=int, default=VSIZE, help='value size')
parser.add_argument('--slab_mem', dest='slab_mem', type=int, default=PELIKAN_SLAB_MEM, help='total capacity of slab memory, in bytes')

args = parser.parse_args()

if not os.path.exists(args.prefix):
os.makedirs(args.prefix)
os.chdir(args.prefix)

generate_config(args.instances, args.vsize, args.slab_mem)
generate_runscript(args.binary, args.instances)