├── .gitignore ├── .gitmodules ├── redismodule ├── Makefile ├── ycsbe.c └── redismodule.h ├── README.md ├── Makefile └── scripts ├── driver.py └── fabfile.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.pyc 3 | *.o 4 | *.so 5 | results/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "r2p2"] 2 | path = r2p2 3 | url = https://github.com/epfl-dcsl/r2p2 4 | [submodule "raft"] 5 | path = raft 6 | url = https://github.com/epfl-dcsl/raft/ 7 | [submodule "lancet-tool"] 8 | path = lancet-tool 9 | url = https://github.com/epfl-dcsl/lancet-tool 10 | [submodule "distbenchr"] 11 | path = distbenchr 12 | url = https://github.com/marioskogias/distbenchr/ 13 | [submodule "redis"] 14 | path = redis 15 | url = https://github.com/epfl-dcsl/redis/ 16 | -------------------------------------------------------------------------------- /redismodule/Makefile: -------------------------------------------------------------------------------- 1 | SRC_C := ycsbe.c 2 | OBJ_C := $(patsubst %.c, %.o, $(SRC_C)) 3 | 4 | #WERROR_FLAGS := -W -Wall -Wstrict-prototypes -Wmissing-prototypes 5 | #WERROR_FLAGS += -Wmissing-declarations -Wold-style-definition -Wpointer-arith 6 | #WERROR_FLAGS += -Wcast-align -Wnested-externs -Wcast-qual 7 | #WERROR_FLAGS += -Wformat-nonliteral -Wformat-security 8 | #WERROR_FLAGS += -Wundef -Wwrite-strings 9 | 10 | CFLAGS := $(WERROR_FLAGS) -fPIC -g -O3 11 | LDFLAGS = -shared 12 | 13 | ycsbe.so: $(OBJ_C) 14 | $(CC) -o $@ $(OBJ_C) $(LDFLAGS) 15 | 16 | clean: 17 | rm -f *.o 18 | 19 | distclean: 20 | make clean 21 | rm -f ycsbe.so 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HovercRaft 2 | 3 | This repository contains the code for the paper: 4 | 5 | > HovercRaft: Achieving Scalability and Fault-tolerance for microsecond-scale Datacenter Services 6 | > Marios Kogias, Edouard Bugnion 7 | > Eurosys 2020 8 | 9 | You can find the paper [here](https://infoscience.epfl.ch/record/276586) 10 | 11 | ## Contents 12 | 13 | The repository contains several submodules and directories. 14 | 15 |
16 |
r2p2
17 |
The codebase for the Request Response Pair Protocol for datacenter RPCs
18 | 19 |
raft
20 |
The raft codebase used with its modifications.
21 | 22 |
redis
23 |
The redis codebase with the R2P2 modifications
24 | 25 |
lancet
26 |
The lancet latency measuring tool and load generator used for the experiments
27 | 28 |
redismodule
29 |
A redis module that implements the YCSB-E workload used in HovercRaft's evaluation
30 | 31 |
distbenchr
32 |
A python package used to run distributed experiments
33 | 34 |
scripts
35 |
Basic scripts to deploy and run HovercRaft for the synthetic and Redis experiments.
36 |
37 | 38 | ## Build 39 | 40 | To get the dependencies and build an example run: 41 | ``` 42 | git submodule update --init --recursive 43 | make hovercraft-stss 44 | ``` 45 | 46 | Check the Makefile for more build options. 47 | 48 | ## Experiments 49 | Check the `scipts` directory for running experiments. Configure the `fabfile.py` and `driver.py` scripts accordingly. 50 | 51 | Also, configure the `r2p2.conf` for HovercRaft as described in the R2P2 [repository](https://github.com/epfl-dcsl/r2p2). 52 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | 24 | # Flags explanation 25 | # ACCELERATED=1 - split request data and metadata 26 | # LB_REPLIES=1 - load balance who replies to the client, default policy rand 27 | # SMART_LB=1 - load balance with JBSQ 28 | # SKIP_NO_SE=1 - skip no side effect operations on all nodes but the replier 29 | # SWITCH_AGG=1 - hovercraft++ (support for in-network aggregation) 30 | 31 | .PHONY: vanilla-stss hovercraft-stss hovercraftplus redismodule 32 | 33 | ROOTDIR=$(shell git rev-parse --show-toplevel) 34 | R2P2_DIR=$(ROOTDIR)/r2p2 35 | 36 | vanilla-stss: 37 | make clean-all 38 | make -C raft 39 | make -C r2p2/dpdk-apps stss 40 | 41 | hovercraft-stss: 42 | make clean-all 43 | make -C raft 44 | make -C r2p2/dpdk-apps stss ACCELERATED=1 LB_REPLIES=1 SKIP_NO_SE=1 # SMART_LB=1 45 | 46 | 47 | hovercraftplus-stss: 48 | make clean-all 49 | make -C raft SWITCH_AGG=1 50 | make -C r2p2/dpdk-apps stss SWITCH_AGG=1 ACCELERATED=1 LB_REPLIES=1 SKIP_NO_SE=1 SMART_LB=1 51 | 52 | lancet: 53 | make -C lancet-tool coordinator 54 | make -C lancet-tool agents_r2p2_nic_ts R2P2=$(R2P2_DIR)/r2p2 55 | make -C lancet-tool manager 56 | make -C lancet-tool deploy HOSTS=icnals01,icnals02,icnals03,icnals04 57 | 58 | redismodule: 59 | make -C redismodule 60 | 61 | clean-all: 62 | make -C raft clean 63 | make -C r2p2 clean 64 | make -C redismodule clean 65 | -------------------------------------------------------------------------------- /scripts/driver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from fabric.tasks import execute 4 | from fabric.api import env 5 | from fabfile import * 6 | from distbenchr import * 7 | import time 8 | import signal 9 | 10 | PEERS = [ 11 | ("10.90.44.216", 8000), 12 | ("10.90.44.217", 8000), 13 | ("10.90.44.220", 8000), 14 | ] 15 | 16 | PATTERNS = { 17 | 10: "step:10000:200000:5000:20000", 18 | "redis": "step:1000:180000:2000", 19 | } 20 | 21 | CFGS = { 22 | "unrep": ( 23 | [0], 24 | [ 25 | ("vanilla", "") 26 | ] 27 | ), 28 | "rep": ( 29 | [3], 30 | [ 31 | ("vanilla", "WITH_RAFT=1") 32 | ("lb_rep_skip_no_se_smart", "WITH_RAFT=1 LB_REPLIES=1 SMART_LB=1 SKIP_NO_SE=1"), 33 | ] 34 | ), 35 | "rep_accel": ( 36 | [3], 37 | [ 38 | ("vanilla", "WITH_RAFT=1 ACCELERATED=1") 39 | ("lb_rep_skip_no_se_smart", "WITH_RAFT=1 ACCELERATED=1 LB_REPLIES=1 SMART_LB=1 SKIP_NO_SE=1"), 40 | ] 41 | ) 42 | } 43 | 44 | FOLLOWERS_FN = { 45 | 3: run_followers3, 46 | } 47 | 48 | NO_SE_RATIO = 0.75 49 | 50 | def synthetic_time(): 51 | program = "stss" 52 | service_times = [10] 53 | for mode, info in CFGS.iteritems(): 54 | if "rep_accel" in mode: 55 | target = "multicast" 56 | else: 57 | target = "master" 58 | for p in info[0]: 59 | for cname, cflags in info[1]: 60 | execute(build, program, flags=cflags) 61 | execute(deploy, program) 62 | for s in service_times: 63 | mnt = Monitor() 64 | if p > 0: 65 | mnt.bg_execute(run_master, program, should_wait=False) 66 | time.sleep(2) 67 | mnt.bg_execute(FOLLOWERS_FN[p], program, should_wait=False) 68 | proto = "stssr_fixed:{}_fixed:0_fixed:0_{}".format(s, NO_SE_RATIO) 69 | else: 70 | mnt.bg_execute(run_unrep, program, should_wait=False) 71 | proto = "stss_fixed:{}_fixed:0_fixed:0".format(s) 72 | time.sleep(60) 73 | fname = "st_fixed_{}_NO_SE_{}_{}_peers_{}_{}.txt".format(s, NO_SE_RATIO, mode, p, cname) 74 | mnt.bg_execute(run_lancet_sym_hw, PATTERNS[s], 75 | proto, fname, target=target, should_wait=True) 76 | mnt.monitor() 77 | mnt.killall() 78 | 79 | def redis_ycsbe(): 80 | for mode, info in CFGS.iteritems(): 81 | if "accel" in mode: 82 | target = "multicast" 83 | else: 84 | target = "master" 85 | for p in info[0]: 86 | for cname, cflags in info[1]: 87 | execute(build, "stss", flags=cflags) 88 | execute(build_redis, flags=cflags) 89 | execute(kill_redis) 90 | execute(deploy_redis) 91 | mnt = Monitor() 92 | if p > 0: 93 | mnt.bg_execute(run_redis_master, should_wait=False) 94 | time.sleep(2) 95 | mnt.bg_execute(run_redis_followers, should_wait=False) 96 | proto = "redis-ycsber" 97 | else: 98 | mnt.bg_execute(run_redis_single, should_wait=False) 99 | proto = "redis-ycsbe" 100 | time.sleep(60) 101 | fname = "redis_ycsbe_{}_peers_{}_{}.txt".format(mode, p, cname) 102 | mnt.bg_execute(run_lancet_sym_hw, PATTERNS["redis"], 103 | proto, fname, target=target, should_wait=True) 104 | mnt.monitor() 105 | mnt.killall() 106 | 107 | def main(): 108 | execute(build_raft) 109 | execute(prepare_clients) 110 | execute(configure_peers, PEERS) 111 | 112 | print "Execute synthetic time experiment" 113 | synthetic_time() 114 | 115 | print "Run redis ycsbe experiment" 116 | redis_ycsbe() 117 | 118 | if __name__ == "__main__": 119 | os.setpgrp() # create new process group, become its leader 120 | try: 121 | main() 122 | except: 123 | import traceback 124 | traceback.print_exc() 125 | finally: 126 | os.killpg(0, signal.SIGKILL) # kill all processes in my group 127 | -------------------------------------------------------------------------------- /redismodule/ycsbe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "redismodule.h" 5 | 6 | #define FIELD_NO 10 7 | #define IDX_SET "indices" 8 | 9 | static RedisModuleString *indices_set; 10 | static RedisModuleString *f1; 11 | static RedisModuleString *f2; 12 | static RedisModuleString *f3; 13 | static RedisModuleString *f4; 14 | static RedisModuleString *f5; 15 | static RedisModuleString *f6; 16 | static RedisModuleString *f7; 17 | static RedisModuleString *f8; 18 | static RedisModuleString *f9; 19 | static RedisModuleString *f10; 20 | 21 | static unsigned long hash(const unsigned char *str, int len) 22 | { 23 | unsigned long hash = 5381; 24 | int c, i; 25 | 26 | for (i=0;i $i/cpufreq/scaling_governor; echo $FREQ > $i/cpufreq/scaling_setspeed; done'") 58 | run("sudo ethtool -C enp65s0 adaptive-rx off adaptive-tx off rx-usecs 0 rx-frames 0 tx-usecs 0 tx-frames 0 || true") 59 | 60 | def _prepare_huge_pages(hugepages): 61 | run('sudo sh -c "echo never > /sys/kernel/mm/transparent_hugepage/enabled"') 62 | run('sudo sh -c "echo %d > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages"' % hugepages) 63 | run('sudo sh -c "echo 4096 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages"') 64 | run("sudo find /dev/hugepages -name 'rtemap_*' -delete") 65 | 66 | def _prepare_dpdk(pcie="01:00.0"): 67 | #_prepare_huge_pages(4096) 68 | run("rm -rf /tmp/{} && mkdir -p /tmp/{}".format(os.getlogin(), os.getlogin())) 69 | put("{}/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko".format(DPDK_DIR), '/tmp/{}'.format(os.getlogin())) 70 | put("{}/usertools/dpdk-devbind.py".format(DPDK_DIR), '/tmp/{}'.format(os.getlogin())) 71 | run("sudo ifdown cu0") 72 | run("sudo sudo modprobe uio") 73 | run("sudo sudo insmod /tmp/{}/igb_uio.ko || true".format(os.getlogin())) 74 | run("sudo python /tmp/{}/dpdk-devbind.py --bind=igb_uio {}".format(os.getlogin(), pcie)) 75 | 76 | @roles('master-server', 'followers3') 77 | def configure_peers(peers): 78 | _prepare_dpdk() 79 | # Delete old cfg 80 | cmd1 = "sudo sed -i '/raft/,$d' /etc/r2p2.conf" 81 | run(cmd1) 82 | 83 | # if peers cfg accordingly 84 | if not peers: 85 | return 86 | 87 | def to_string(peer): 88 | return "\t{{\n\t\tip : \"{}\"\n\t\tport : {}\n\t}}".format(peer[0], peer[1]) 89 | peers_str = ",\n".join([to_string(x) for x in peers]) 90 | cfg = "raft=(\n{}\n)".format(peers_str) 91 | cmd2 = "echo \'{}\' | sudo tee -a /etc/r2p2.conf".format(cfg) 92 | run(cmd2) 93 | 94 | # Set freq 95 | run("sudo sh -c 'FREQ=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq` && for i in /sys/devices/system/cpu/cpu[0-9]*; do echo userspace > $i/cpufreq/scaling_governor; echo $FREQ > $i/cpufreq/scaling_setspeed; done'") 96 | run('sudo sh -c "echo never > /sys/kernel/mm/transparent_hugepage/enabled"') 97 | 98 | @run_bg('master-server') 99 | def run_unrep(program_name): 100 | run("ulimit -c unlimited && sudo /tmp/{}/{} -l 2".format(os.getlogin(), program_name)) 101 | 102 | @run_bg('master-server') 103 | def run_master(program_name): 104 | run("ulimit -c unlimited && sudo /tmp/{}/{} -l 2,4".format(os.getlogin(), program_name)) 105 | 106 | @run_bg('followers3') 107 | def run_followers3(program_name): 108 | run("ulimit -c unlimited && sudo /tmp/{}/{} -l 2,4".format(os.getlogin(), program_name)) 109 | 110 | @run_bg('master-server') 111 | def run_redis_single(): 112 | run("ulimit -c unlimited && sudo /tmp/{}/redis-server -l 2 -- --save "" --protected-mode no --loadmodule /tmp/{}/ycsbe.so".format(os.getlogin(), os.getlogin())) 113 | 114 | @run_bg('master-server') 115 | def run_redis_master(): 116 | run("ulimit -c unlimited && sudo /tmp/{}/redis-server -l 2,4 -- --save "" --protected-mode no --loadmodule /tmp/{}/ycsbe.so".format(os.getlogin(), os.getlogin())) 117 | 118 | @run_bg('followers3') 119 | def run_redis_followers(): 120 | run("ulimit -c unlimited && sudo /tmp/{}/redis-server -l 2,4 -- --save "" --protected-mode no --loadmodule /tmp/{}/ycsbe.so".format(os.getlogin(), os.getlogin())) 121 | 122 | @run_bg('coordinator') 123 | def run_lancet_sym_hw(pattern, proto, file_dst, target="master"): 124 | run("mkdir -p {}".format(RES_DIR)) 125 | if target[1:-1] == "master": 126 | dst = icnals_ip(env.roledefs['master-server'][0]) 127 | elif target[1:-1] == "switch": 128 | dst = env.roledefs['switch'][0] 129 | else: 130 | dst = env.roledefs['multicast'][0] 131 | 132 | agents = ",".join(env.roledefs['lancet-agents']) 133 | cmd = "{}/coordinator/coordinator -appProto {} -comProto R2P2\ 134 | -loadThreads 15 -symAgents {} -loadPattern {}\ 135 | -targetHost {}:8000\ 136 | -nicTS > {}/{}".format(LANCET_DIR, proto, agents, pattern, 137 | dst, RES_DIR, file_dst) 138 | run(cmd) 139 | 140 | @roles('master-server', 'followers3') 141 | def kill_redis(): 142 | run("sudo kill -9 `pidof redis-server` || true") 143 | -------------------------------------------------------------------------------- /redismodule/redismodule.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISMODULE_H 2 | #define REDISMODULE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* ---------------- Defines common between core and modules --------------- */ 9 | 10 | /* Error status return values. */ 11 | #define REDISMODULE_OK 0 12 | #define REDISMODULE_ERR 1 13 | 14 | /* API versions. */ 15 | #define REDISMODULE_APIVER_1 1 16 | 17 | /* API flags and constants */ 18 | #define REDISMODULE_READ (1<<0) 19 | #define REDISMODULE_WRITE (1<<1) 20 | 21 | #define REDISMODULE_LIST_HEAD 0 22 | #define REDISMODULE_LIST_TAIL 1 23 | 24 | /* Key types. */ 25 | #define REDISMODULE_KEYTYPE_EMPTY 0 26 | #define REDISMODULE_KEYTYPE_STRING 1 27 | #define REDISMODULE_KEYTYPE_LIST 2 28 | #define REDISMODULE_KEYTYPE_HASH 3 29 | #define REDISMODULE_KEYTYPE_SET 4 30 | #define REDISMODULE_KEYTYPE_ZSET 5 31 | #define REDISMODULE_KEYTYPE_MODULE 6 32 | 33 | /* Reply types. */ 34 | #define REDISMODULE_REPLY_UNKNOWN -1 35 | #define REDISMODULE_REPLY_STRING 0 36 | #define REDISMODULE_REPLY_ERROR 1 37 | #define REDISMODULE_REPLY_INTEGER 2 38 | #define REDISMODULE_REPLY_ARRAY 3 39 | #define REDISMODULE_REPLY_NULL 4 40 | 41 | /* Postponed array length. */ 42 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 43 | 44 | /* Expire */ 45 | #define REDISMODULE_NO_EXPIRE -1 46 | 47 | /* Sorted set API flags. */ 48 | #define REDISMODULE_ZADD_XX (1<<0) 49 | #define REDISMODULE_ZADD_NX (1<<1) 50 | #define REDISMODULE_ZADD_ADDED (1<<2) 51 | #define REDISMODULE_ZADD_UPDATED (1<<3) 52 | #define REDISMODULE_ZADD_NOP (1<<4) 53 | 54 | /* Hash API flags. */ 55 | #define REDISMODULE_HASH_NONE 0 56 | #define REDISMODULE_HASH_NX (1<<0) 57 | #define REDISMODULE_HASH_XX (1<<1) 58 | #define REDISMODULE_HASH_CFIELDS (1<<2) 59 | #define REDISMODULE_HASH_EXISTS (1<<3) 60 | 61 | /* Context Flags: Info about the current context returned by RM_GetContextFlags */ 62 | 63 | /* The command is running in the context of a Lua script */ 64 | #define REDISMODULE_CTX_FLAGS_LUA 0x0001 65 | /* The command is running inside a Redis transaction */ 66 | #define REDISMODULE_CTX_FLAGS_MULTI 0x0002 67 | /* The instance is a master */ 68 | #define REDISMODULE_CTX_FLAGS_MASTER 0x0004 69 | /* The instance is a slave */ 70 | #define REDISMODULE_CTX_FLAGS_SLAVE 0x0008 71 | /* The instance is read-only (usually meaning it's a slave as well) */ 72 | #define REDISMODULE_CTX_FLAGS_READONLY 0x0010 73 | /* The instance is running in cluster mode */ 74 | #define REDISMODULE_CTX_FLAGS_CLUSTER 0x0020 75 | /* The instance has AOF enabled */ 76 | #define REDISMODULE_CTX_FLAGS_AOF 0x0040 // 77 | /* The instance has RDB enabled */ 78 | #define REDISMODULE_CTX_FLAGS_RDB 0x0080 // 79 | /* The instance has Maxmemory set */ 80 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY 0x0100 81 | /* Maxmemory is set and has an eviction policy that may delete keys */ 82 | #define REDISMODULE_CTX_FLAGS_EVICT 0x0200 83 | 84 | 85 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */ 86 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */ 87 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */ 88 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */ 89 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */ 90 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */ 91 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */ 92 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */ 93 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED) /* A */ 94 | 95 | 96 | /* A special pointer that we can use between the core and the module to signal 97 | * field deletion, and that is impossible to be a valid pointer. */ 98 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 99 | 100 | /* Error messages. */ 101 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 102 | 103 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 104 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 105 | 106 | #define REDISMODULE_NOT_USED(V) ((void) V) 107 | 108 | /* ------------------------- End of common defines ------------------------ */ 109 | 110 | #ifndef REDISMODULE_CORE 111 | 112 | typedef long long mstime_t; 113 | 114 | /* Incomplete structures for compiler checks but opaque access. */ 115 | typedef struct RedisModuleCtx RedisModuleCtx; 116 | typedef struct RedisModuleKey RedisModuleKey; 117 | typedef struct RedisModuleString RedisModuleString; 118 | typedef struct RedisModuleCallReply RedisModuleCallReply; 119 | typedef struct RedisModuleIO RedisModuleIO; 120 | typedef struct RedisModuleType RedisModuleType; 121 | typedef struct RedisModuleDigest RedisModuleDigest; 122 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 123 | 124 | typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 125 | 126 | typedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); 127 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 128 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 129 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 130 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value); 131 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 132 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 133 | 134 | #define REDISMODULE_TYPE_METHOD_VERSION 1 135 | typedef struct RedisModuleTypeMethods { 136 | uint64_t version; 137 | RedisModuleTypeLoadFunc rdb_load; 138 | RedisModuleTypeSaveFunc rdb_save; 139 | RedisModuleTypeRewriteFunc aof_rewrite; 140 | RedisModuleTypeMemUsageFunc mem_usage; 141 | RedisModuleTypeDigestFunc digest; 142 | RedisModuleTypeFreeFunc free; 143 | } RedisModuleTypeMethods; 144 | 145 | #define REDISMODULE_GET_API(name) \ 146 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) 147 | 148 | #define REDISMODULE_API_FUNC(x) (*x) 149 | 150 | 151 | void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes); 152 | void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes); 153 | void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr); 154 | void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size); 155 | char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str); 156 | int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); 157 | int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); 158 | void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); 159 | int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name); 160 | int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); 161 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); 162 | int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); 163 | int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); 164 | void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); 165 | void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); 166 | int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); 167 | size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); 168 | int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); 169 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); 170 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 171 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); 172 | void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); 173 | int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); 174 | long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); 175 | size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); 176 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); 177 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); 178 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); 179 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); 180 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); 181 | void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); 182 | const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len); 183 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); 184 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); 185 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); 186 | void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); 187 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); 188 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); 189 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); 190 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); 191 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); 192 | int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll); 193 | int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d); 194 | void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); 195 | int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 196 | int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); 197 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); 198 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); 199 | int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); 200 | int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key); 201 | int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); 202 | char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); 203 | int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); 204 | mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); 205 | int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); 206 | int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); 207 | int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); 208 | int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); 209 | int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); 210 | void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); 211 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 212 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 213 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 214 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 215 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); 216 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); 217 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); 218 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); 219 | int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); 220 | int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); 221 | int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); 222 | void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); 223 | unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); 224 | int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx); 225 | void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes); 226 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods); 227 | int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value); 228 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key); 229 | void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key); 230 | void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value); 231 | uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io); 232 | void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value); 233 | int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io); 234 | void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 235 | void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s); 236 | void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len); 237 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io); 238 | char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr); 239 | void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value); 240 | double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io); 241 | void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value); 242 | float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io); 243 | void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...); 244 | void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...); 245 | int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len); 246 | void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str); 247 | int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b); 248 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io); 249 | long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void); 250 | void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len); 251 | void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele); 252 | void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md); 253 | 254 | /* Experimental APIs */ 255 | #ifdef REDISMODULE_EXPERIMENTAL_API 256 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms); 257 | int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata); 258 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx); 259 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx); 260 | void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx); 261 | int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc); 262 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc); 263 | void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx); 264 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx); 265 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx); 266 | int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb); 267 | 268 | #endif 269 | 270 | /* This is included inline inside each Redis module. */ 271 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); 272 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 273 | void *getapifuncptr = ((void**)ctx)[0]; 274 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; 275 | REDISMODULE_GET_API(Alloc); 276 | REDISMODULE_GET_API(Calloc); 277 | REDISMODULE_GET_API(Free); 278 | REDISMODULE_GET_API(Realloc); 279 | REDISMODULE_GET_API(Strdup); 280 | REDISMODULE_GET_API(CreateCommand); 281 | REDISMODULE_GET_API(SetModuleAttribs); 282 | REDISMODULE_GET_API(IsModuleNameBusy); 283 | REDISMODULE_GET_API(WrongArity); 284 | REDISMODULE_GET_API(ReplyWithLongLong); 285 | REDISMODULE_GET_API(ReplyWithError); 286 | REDISMODULE_GET_API(ReplyWithSimpleString); 287 | REDISMODULE_GET_API(ReplyWithArray); 288 | REDISMODULE_GET_API(ReplySetArrayLength); 289 | REDISMODULE_GET_API(ReplyWithStringBuffer); 290 | REDISMODULE_GET_API(ReplyWithString); 291 | REDISMODULE_GET_API(ReplyWithNull); 292 | REDISMODULE_GET_API(ReplyWithCallReply); 293 | REDISMODULE_GET_API(ReplyWithDouble); 294 | REDISMODULE_GET_API(ReplySetArrayLength); 295 | REDISMODULE_GET_API(GetSelectedDb); 296 | REDISMODULE_GET_API(SelectDb); 297 | REDISMODULE_GET_API(OpenKey); 298 | REDISMODULE_GET_API(CloseKey); 299 | REDISMODULE_GET_API(KeyType); 300 | REDISMODULE_GET_API(ValueLength); 301 | REDISMODULE_GET_API(ListPush); 302 | REDISMODULE_GET_API(ListPop); 303 | REDISMODULE_GET_API(StringToLongLong); 304 | REDISMODULE_GET_API(StringToDouble); 305 | REDISMODULE_GET_API(Call); 306 | REDISMODULE_GET_API(CallReplyProto); 307 | REDISMODULE_GET_API(FreeCallReply); 308 | REDISMODULE_GET_API(CallReplyInteger); 309 | REDISMODULE_GET_API(CallReplyType); 310 | REDISMODULE_GET_API(CallReplyLength); 311 | REDISMODULE_GET_API(CallReplyArrayElement); 312 | REDISMODULE_GET_API(CallReplyStringPtr); 313 | REDISMODULE_GET_API(CreateStringFromCallReply); 314 | REDISMODULE_GET_API(CreateString); 315 | REDISMODULE_GET_API(CreateStringFromLongLong); 316 | REDISMODULE_GET_API(CreateStringFromString); 317 | REDISMODULE_GET_API(CreateStringPrintf); 318 | REDISMODULE_GET_API(FreeString); 319 | REDISMODULE_GET_API(StringPtrLen); 320 | REDISMODULE_GET_API(AutoMemory); 321 | REDISMODULE_GET_API(Replicate); 322 | REDISMODULE_GET_API(ReplicateVerbatim); 323 | REDISMODULE_GET_API(DeleteKey); 324 | REDISMODULE_GET_API(UnlinkKey); 325 | REDISMODULE_GET_API(StringSet); 326 | REDISMODULE_GET_API(StringDMA); 327 | REDISMODULE_GET_API(StringTruncate); 328 | REDISMODULE_GET_API(GetExpire); 329 | REDISMODULE_GET_API(SetExpire); 330 | REDISMODULE_GET_API(ZsetAdd); 331 | REDISMODULE_GET_API(ZsetIncrby); 332 | REDISMODULE_GET_API(ZsetScore); 333 | REDISMODULE_GET_API(ZsetRem); 334 | REDISMODULE_GET_API(ZsetRangeStop); 335 | REDISMODULE_GET_API(ZsetFirstInScoreRange); 336 | REDISMODULE_GET_API(ZsetLastInScoreRange); 337 | REDISMODULE_GET_API(ZsetFirstInLexRange); 338 | REDISMODULE_GET_API(ZsetLastInLexRange); 339 | REDISMODULE_GET_API(ZsetRangeCurrentElement); 340 | REDISMODULE_GET_API(ZsetRangeNext); 341 | REDISMODULE_GET_API(ZsetRangePrev); 342 | REDISMODULE_GET_API(ZsetRangeEndReached); 343 | REDISMODULE_GET_API(HashSet); 344 | REDISMODULE_GET_API(HashGet); 345 | REDISMODULE_GET_API(IsKeysPositionRequest); 346 | REDISMODULE_GET_API(KeyAtPos); 347 | REDISMODULE_GET_API(GetClientId); 348 | REDISMODULE_GET_API(GetContextFlags); 349 | REDISMODULE_GET_API(PoolAlloc); 350 | REDISMODULE_GET_API(CreateDataType); 351 | REDISMODULE_GET_API(ModuleTypeSetValue); 352 | REDISMODULE_GET_API(ModuleTypeGetType); 353 | REDISMODULE_GET_API(ModuleTypeGetValue); 354 | REDISMODULE_GET_API(SaveUnsigned); 355 | REDISMODULE_GET_API(LoadUnsigned); 356 | REDISMODULE_GET_API(SaveSigned); 357 | REDISMODULE_GET_API(LoadSigned); 358 | REDISMODULE_GET_API(SaveString); 359 | REDISMODULE_GET_API(SaveStringBuffer); 360 | REDISMODULE_GET_API(LoadString); 361 | REDISMODULE_GET_API(LoadStringBuffer); 362 | REDISMODULE_GET_API(SaveDouble); 363 | REDISMODULE_GET_API(LoadDouble); 364 | REDISMODULE_GET_API(SaveFloat); 365 | REDISMODULE_GET_API(LoadFloat); 366 | REDISMODULE_GET_API(EmitAOF); 367 | REDISMODULE_GET_API(Log); 368 | REDISMODULE_GET_API(LogIOError); 369 | REDISMODULE_GET_API(StringAppendBuffer); 370 | REDISMODULE_GET_API(RetainString); 371 | REDISMODULE_GET_API(StringCompare); 372 | REDISMODULE_GET_API(GetContextFromIO); 373 | REDISMODULE_GET_API(Milliseconds); 374 | REDISMODULE_GET_API(DigestAddStringBuffer); 375 | REDISMODULE_GET_API(DigestAddLongLong); 376 | REDISMODULE_GET_API(DigestEndSequence); 377 | 378 | #ifdef REDISMODULE_EXPERIMENTAL_API 379 | REDISMODULE_GET_API(GetThreadSafeContext); 380 | REDISMODULE_GET_API(FreeThreadSafeContext); 381 | REDISMODULE_GET_API(ThreadSafeContextLock); 382 | REDISMODULE_GET_API(ThreadSafeContextUnlock); 383 | REDISMODULE_GET_API(BlockClient); 384 | REDISMODULE_GET_API(UnblockClient); 385 | REDISMODULE_GET_API(IsBlockedReplyRequest); 386 | REDISMODULE_GET_API(IsBlockedTimeoutRequest); 387 | REDISMODULE_GET_API(GetBlockedClientPrivateData); 388 | REDISMODULE_GET_API(AbortBlock); 389 | REDISMODULE_GET_API(SubscribeToKeyspaceEvents); 390 | 391 | #endif 392 | 393 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; 394 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 395 | return REDISMODULE_OK; 396 | } 397 | 398 | #else 399 | 400 | /* Things only defined for the modules core, not exported to modules 401 | * including this file. */ 402 | #define RedisModuleString robj 403 | 404 | #endif /* REDISMODULE_CORE */ 405 | #endif /* REDISMOUDLE_H */ 406 | --------------------------------------------------------------------------------