futriix/tests/rdma/run.py

147 lines
5.0 KiB
Python
Raw Normal View History

Introduce Valkey Over RDMA transport (experimental) (#477) Adds an option to build RDMA support as a module: make BUILD_RDMA=module To start valkey-server with RDMA, use a command line like the following: ./src/valkey-server --loadmodule src/valkey-rdma.so \ port=6379 bind=xx.xx.xx.xx * Implement server side of connection module only, this means we can *NOT* compile RDMA support as built-in. * Add necessary information in README.md * Support 'CONFIG SET/GET', for example, 'CONFIG Set rdma.port 6380', then check this by 'rdma res show cm_id' and valkey-cli (with RDMA support, but not implemented in this patch). * The full listeners show like: listener0:name=tcp,bind=*,bind=-::*,port=6379 listener1:name=unix,bind=/var/run/valkey.sock listener2:name=rdma,bind=xx.xx.xx.xx,bind=yy.yy.yy.yy,port=6379 listener3:name=tls,bind=*,bind=-::*,port=16379 Because the lack of RDMA support from TCL, use a simple C program to test Valkey Over RDMA (under tests/rdma/). This is a quite raw version with basic library dependence: libpthread, libibverbs, librdmacm. Run using the script: ./runtest-rdma [ OPTIONS ] To run RDMA in GitHub actions, a kernel module RXE for emulated soft RDMA, needs to be installed. The kernel module source code is fetched a repo containing only the RXE kernel driver from the Linux kernel, but stored in an separate repo to avoid cloning the whole Linux kernel repo. ---- Since 2021/06, I created a [PR](https://github.com/redis/redis/pull/9161) for *Redis Over RDMA* proposal. Then I did some work to [fully abstract connection and make TLS dynamically loadable](https://github.com/redis/redis/pull/9320), a new connection type could be built into Redis statically, or a separated shared library(loaded by Redis on startup) since Redis 7.2.0. Base on the new connection framework, I created a new [PR](https://github.com/redis/redis/pull/11182), some guys(@xiezhq-hermann @zhangyiming1201 @JSpewock @uvletter @FujiZ) noticed, played and tested this PR. However, because of the lack of time and knowledge from the maintainers, this PR has been pending about 2 years. Related doc: [Introduce *Valkey Over RDMA* specification](https://github.com/valkey-io/valkey-doc/pull/123). (same as Redis, and this should be same) Changes in this PR: - implement *Valkey Over RDMA*. (compact the Valkey style) Finally, if this feature is considered to merge, I volunteer to maintain it. --------- Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
2024-07-15 20:04:22 +08:00
#!/usr/bin/python3
"""
==========================================================================
run.py - script for test client for Valkey Over RDMA (Linux only)
--------------------------------------------------------------------------
Copyright (C) 2024 zhenwei pi <pizhenwei@bytedance.com>
This work is licensed under BSD 3-Clause, License 1 of the COPYING file in
the top-level directory.
==========================================================================
"""
import os
import subprocess
import netifaces
import time
import argparse
def build_program():
valkeydir = os.path.dirname(os.path.abspath(__file__)) + "/../.."
cmd = "make -C " + valkeydir + "/tests/rdma"
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
if p.wait():
print("Valkey Over RDMA build rdma-test [FAILED]")
return 1
print("Valkey Over RDMA build rdma-test program [OK]")
return 0
# iterate /sys/class/infiniband, find any usable RDMA device, and return IPv4 address
def find_rdma_dev():
# Ex, /sys/class/infiniband/mlx5_0
# Ex, /sys/class/infiniband/rxe_eth0
# Ex, /sys/class/infiniband/siw_eth0
ibclass = "/sys/class/infiniband/"
try:
for dev in os.listdir(ibclass):
# Ex, /sys/class/infiniband/rxe_eth0/ports/1/gid_attrs/ndevs/0
netdev = ibclass + dev + "/ports/1/gid_attrs/ndevs/0"
with open(netdev) as fp:
addrs = netifaces.ifaddresses(fp.readline().strip("\n"))
if netifaces.AF_INET not in addrs:
continue
ipaddr = addrs[netifaces.AF_INET][0]["addr"]
print("Valkey Over RDMA test prepare " + dev + " <" + ipaddr + "> [OK]")
return ipaddr
except os.error:
return None
return None
def test_rdma(ipaddr):
valkeydir = os.path.dirname(os.path.abspath(__file__)) + "/../.."
retval = 0
# step 1, prepare test directory
tmpdir = valkeydir + "/tests/rdma/tmp"
subprocess.Popen("mkdir -p " + tmpdir, shell=True).wait()
# step 2, start server
svrpath = valkeydir + "/src/valkey-server"
rdmapath = valkeydir + "/src/valkey-rdma.so"
svrcmd = [svrpath, "--port", "0", "--loglevel", "verbose", "--protected-mode", "no",
"--appendonly", "no", "--daemonize", "no", "--dir", valkeydir + "/tests/rdma/tmp",
"--loadmodule", rdmapath, "port=6379", "bind=" + ipaddr]
svr = subprocess.Popen(svrcmd, shell=False, stdout=subprocess.PIPE)
try:
if svr.wait(1):
print("Valkey Over RDMA valkey-server runs less than 1s [FAILED]")
return 1
except subprocess.TimeoutExpired as e:
print("Valkey Over RDMA valkey-server start [OK]")
pass
# step 3, run test client
start = time.time()
clipath = valkeydir + "/tests/rdma/rdma-test"
clicmd = [clipath, "--thread", "4", "-h", ipaddr]
cli = subprocess.Popen(clicmd, shell=False, stdout=subprocess.PIPE)
if cli.wait(60):
outs, _ = cli.communicate()
print("Valkey Over RDMA test [FAILED]")
print("---------------\n" + outs.decode() + "---------------\n")
retval = 1
else:
elapsed = time.time() - start
outs, _ = cli.communicate()
print("Valkey Over RDMA test in " + str(round(elapsed, 2)) + "s [OK]")
print(outs.decode())
retval = 0
# step 4, cleanup
svr.kill()
svr.wait()
subprocess.Popen("rm -rf " + tmpdir, shell=True).wait()
# step 5, report result
return retval
def test_exit(retval, install_rxe):
if install_rxe and not os.geteuid():
rdma_env_py = os.path.dirname(os.path.abspath(__file__)) + "/rdma_env.py"
cmd = rdma_env_py + " -o cleanup"
subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).wait()
os._exit(retval);
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description = "Script to test Valkey Over RDMA",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("-r", "--install-rxe", action='store_true',
help="install RXE driver and setup RXE device")
args = parser.parse_args()
if args.install_rxe:
if os.geteuid():
print("--install-rxe/-r must be root privileged")
test_exit(1, False)
rdma_env_py = os.path.dirname(os.path.abspath(__file__)) + "/rdma_env.py"
cmd = rdma_env_py + " -o setup -d rxe"
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
if p.wait():
print("Valkey Over RDMA setup RXE [FAILED]")
test_exit(1, False)
# build C client into binary
retval = build_program()
if retval:
test_exit(1, args.install_rxe)
ipaddr = find_rdma_dev()
if ipaddr is None:
# not fatal error, continue to create software version: RXE and SIW
print("Valkey Over RDMA test detect existing RDMA device [FAILED]")
else:
retval = test_rdma(ipaddr)
if not retval:
print("Valkey Over RDMA test over " + ipaddr + " [OK]")
test_exit(0, args.install_rxe);