diff --git a/.gitignore b/.gitignore index 8e1230f5..41c6a703 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,56 @@ test_all *.trs tags +# netbeans project +nbproject +nbproject/* +*.bak +*bak + +# debian packaging +debian/.debhelper/* +debian/.debhelper + +# https://github.com/phusion/debian-packaging-for-the-modern-developer/blob/master/.gitignore +.DS_Store +.debhelper +*.debhelper +*.cache +*.cache/* +*.deb +*.dsc +*.build +*.buildinfo +*.changes +*.tar.gz +*.log +*.substvars +# Ignore generated files +/*.deb +/*.dsc +/*.changes +/*.build +/*.buildinfo +/*.tar.gz + +# Ignore files generated during build +# https://github.com/vincentbernat/pragmatic-debian-packages/blob/master/.gitignore +debian/files +debian/*.substvars +debian/*.log +debian/.debhelper/* +debian/*.debhelper +debian/*-stamp +debian/*.after +debian/*.before +debian/nutcracker/* + +/*/debian/files +/*/debian/*.substvars +/*/debian/*.log +/*/debian/.debhelper/* +/*/debian/*.debhelper +/*/debian/*-stamp +/*/debian/*.after +/*/debian/*.before +/*/debian/nutcracker/* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..254d91a0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: c +script: bash ./travis.sh + diff --git a/Makefile.am b/Makefile.am index 58699741..237405e5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure config.h.in config.h.in~ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = contrib src +SUBDIRS = src dist_man_MANS = man/nutcracker.8 diff --git a/README.md b/README.md index fbe29e47..133fac45 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ The build steps are the same (`./configure; make; sudo make install`). -D, --describe-stats : print stats description and exit -v, --verbose=N : set logging level (default: 5, min: 0, max: 11) -o, --output=S : set logging file (default: stderr) - -c, --conf-file=S : set configuration file (default: conf/nutcracker.yml) + -c, --conf-file=S : set configuration file (default: /etc/nutcracker/nutcracker.yml) -s, --stats-port=N : set stats monitoring port (default: 22222) -a, --stats-addr=S : set stats monitoring ip (default: 0.0.0.0) -i, --stats-interval=N : set stats aggregation interval in msec (default: 30000 msec) @@ -121,7 +121,7 @@ Twemproxy can be configured through a YAML file specified by the -c or --conf-fi + **servers**: A list of server address, port and weight (name:port:weight or ip:port:weight) for this server pool. -For example, the configuration file in [conf/nutcracker.yml](conf/nutcracker.yml), also shown below, configures 5 server pools with names - _alpha_, _beta_, _gamma_, _delta_ and omega. Clients that intend to send requests to one of the 10 servers in pool delta connect to port 22124 on 127.0.0.1. Clients that intend to send request to one of 2 servers in pool omega connect to unix path /tmp/gamma. Requests sent to pool alpha and omega have no timeout and might require timeout functionality to be implemented on the client side. On the other hand, requests sent to pool beta, gamma and delta timeout after 400 msec, 400 msec and 100 msec respectively when no response is received from the server. Of the 5 server pools, only pools alpha, gamma and delta are configured to use server ejection and hence are resilient to server failures. All the 5 server pools use ketama consistent hashing for key distribution with the key hasher for pools alpha, beta, gamma and delta set to fnv1a_64 while that for pool omega set to hsieh. Also only pool beta uses [nodes names](notes/recommendation.md#node-names-for-consistent-hashing) for consistent hashing, while pool alpha, gamma, delta and omega use 'host:port:weight' for consistent hashing. Finally, only pool alpha and beta can speak the redis protocol, while pool gamma, delta and omega speak memcached protocol. +For example, the configuration file in [/etc/nutcracker/nutcracker.yml](/etc/nutcracker/nutcracker.yml), also shown below, configures 5 server pools with names - _alpha_, _beta_, _gamma_, _delta_ and omega. Clients that intend to send requests to one of the 10 servers in pool delta connect to port 22124 on 127.0.0.1. Clients that intend to send request to one of 2 servers in pool omega connect to unix path /tmp/gamma. Requests sent to pool alpha and omega have no timeout and might require timeout functionality to be implemented on the client side. On the other hand, requests sent to pool beta, gamma and delta timeout after 400 msec, 400 msec and 100 msec respectively when no response is received from the server. Of the 5 server pools, only pools alpha, gamma and delta are configured to use server ejection and hence are resilient to server failures. All the 5 server pools use ketama consistent hashing for key distribution with the key hasher for pools alpha, beta, gamma and delta set to fnv1a_64 while that for pool omega set to hsieh. Also only pool beta uses [nodes names](notes/recommendation.md#node-names-for-consistent-hashing) for consistent hashing, while pool alpha, gamma, delta and omega use 'host:port:weight' for consistent hashing. Finally, only pool alpha and beta can speak the redis protocol, while pool gamma, delta and omega speak memcached protocol. alpha: listen: 127.0.0.1:22121 diff --git a/conf/nutcracker.yml b/conf/nutcracker.yml index 0fe2a041..869db80c 100644 --- a/conf/nutcracker.yml +++ b/conf/nutcracker.yml @@ -8,6 +8,7 @@ alpha: server_failure_limit: 1 servers: - 127.0.0.1:6379:1 + - 127.0.0.1:7379:1 beta: listen: 127.0.0.1:22122 @@ -22,6 +23,7 @@ beta: - 127.0.0.1:6381:1 server2 - 127.0.0.1:6382:1 server3 - 127.0.0.1:6383:1 server4 + - 127.0.0.1:7379:1 server5 gamma: listen: 127.0.0.1:22123 @@ -36,6 +38,8 @@ gamma: servers: - 127.0.0.1:11212:1 - 127.0.0.1:11213:1 + - 127.0.0.1:11211:1 + - 127.0.0.1:33211:1 delta: listen: 127.0.0.1:22124 @@ -56,6 +60,7 @@ delta: - 127.0.0.1:11221:1 - 127.0.0.1:11222:1 - 127.0.0.1:11223:1 + - 127.0.0.1:33211:1 omega: listen: /tmp/gamma @@ -65,3 +70,5 @@ omega: servers: - 127.0.0.1:11214:100000 - 127.0.0.1:11215:1 + - 127.0.0.1:11211:1 + - 127.0.0.1:33211:1 diff --git a/configure.ac b/configure.ac index 0f96dbae..86344354 100644 --- a/configure.ac +++ b/configure.ac @@ -197,15 +197,8 @@ AS_IF([test "x$disable_stats" = xyes], [AC_DEFINE([HAVE_STATS], [1], [Define to 1 if stats is not disabled])]) AC_MSG_RESULT($disable_stats) -# Untar the yaml-0.2.5 in contrib/ before config.status is rerun -AC_CONFIG_COMMANDS_PRE([tar xvfz contrib/yaml-0.2.5.tar.gz -C contrib]) - -# Call yaml-0.2.5 ./configure recursively -AC_CONFIG_SUBDIRS([contrib/yaml-0.2.5]) - # Define Makefiles AC_CONFIG_FILES([Makefile - contrib/Makefile src/Makefile src/hashkit/Makefile src/proto/Makefile diff --git a/contrib/Makefile.am b/contrib/Makefile.am index d90e6080..ad172a4f 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = yaml-0.2.5 +#SUBDIRS = yaml-0.2.5 -EXTRA_DIST = yaml-0.2.5.tar.gz +#EXTRA_DIST = yaml-0.2.5.tar.gz diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..bab0c5e6 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,30 @@ +nutcracker (0.4.1+dfsg-1) unstable; urgency=medium + + * New upstream release. + * Bump debhelper compat level to 10; remove dh-autoreconf B-D. + * Update Standards-Version to 3.9.8, no changes needed. + * Fix a few d/copyright structural errors. + * Update Vcs-Git/Vcs-Browser to point to https URLs and cgit. + * Add dependency on lsb-base for /lib/lsb/init-functions. + * Add hardening build flags (hardening=+all). + * Switch pidfile location from /var/run to /run. + * Update debian/patches/sysconfdir to only write a pidfile if --daemonize + was passed. + * Add systemd service file. + * Remove upstart job, dead and unmaintained code. + + -- Faidon Liambotis Sat, 17 Dec 2016 04:48:28 +0200 + +nutcracker (0.4.0+dfsg-1) unstable; urgency=medium + + * New upstream release. + * Switch debian/watch to GitHub instead of Google Code, as this is + apparently where new tarballs are being shipped nowadays. + + -- Faidon Liambotis Tue, 21 Oct 2014 03:26:49 +0300 + +nutcracker (0.3.0+dfsg-1) unstable; urgency=medium + + * Initial release. (Closes: #712107) + + -- Faidon Liambotis Mon, 06 Oct 2014 20:02:52 +0300 diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..f599e28b --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..11c57fd4 --- /dev/null +++ b/debian/control @@ -0,0 +1,26 @@ +Source: nutcracker +Section: net +Priority: optional +Maintainer: Faidon Liambotis +Build-Depends: debhelper (>= 10), libyaml-dev +Standards-Version: 3.9.8 +Homepage: https://github.com/twitter/twemproxy +Vcs-Git: https://anonscm.debian.org/git/collab-maint/nutcracker.git +Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/nutcracker.git + +Package: nutcracker +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, lsb-base (>= 3.0-6) +Description: Fast, light-weight proxy for memcached and Redis + nutcracker, also known as twemproxy (pronounced "two-em-proxy"), is a + fast and lightweight proxy for the memcached and Redis protocols. It was + primarily built to reduce the connection count on backend caching + servers, but it has a number of features, such as: + * Maintains persistent server connections to backend servers. + * Enables pipelining of requests and responses. + * Supports multiple server pools simultaneously. + * Shard data automatically across multiple servers. + * Supports multiple hashing modes including consistent hashing and + distribution. + * High-availability by disabling nodes on failures. + * Observability through stats exposed on stats monitoring port. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..decdc369 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,143 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: nutcracker +Source: https://github.com/twitter/twemproxy + +Files: * +Copyright: 2011, 2013 Twitter, Inc. +License: Apache-2.0 + +Files: debian/* +Copyright: 2014 Faidon Liambotis +License: Apache-2.0 + +Files: src/nc_array.c src/nc_array.h src/nc_rbtree.c src/nc_rbtree.h +Copyright: 2002-2010 Igor Sysoev + 2011 Twitter, Inc. +License: BSD-2-clause + +Files: src/nc_queue.h +Copyright: 1991, 1993 The Regents of the University of California + 2011 Twitter, Inc. +License: BSD-4-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the University of + California, Berkeley and its contributors. + 4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +Files: src/hashkit/* +Copyright: 2007-2010 TangentOrg (Brian Aker) + 2011 Data Differential (http://datadifferential.com/) + 2011 Twitter, Inc. +License: BSD-4-clause-TangentOrg + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + . + * Neither the name of TangentOrg nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Files: src/hashkit/nc_one_at_a_time.c +Copyright: 2009 Brian Aker + 2011 Twitter, Inc. +License: Apache-2.0 + +Files: src/hashkit/nc_hsieh.c +Copyright: 2004, 2005, Paul Hsieh + 2011 Twitter, Inc. +License: PaulHsieh-license + The derivative content includes raw computer source code, ideas, opinions, + and excerpts whose original source is covered under another license and + transformations of such derivatives. Note that mere excerpts by themselves + (with the exception of raw source code) are not considered derivative works + under this license. Use and redistribution is limited to the following + conditions: + * One may not create a derivative work which, in any way, violates the + Paul Hsieh exposition license described above on the original content. + * One may not apply a license to a derivative work that precludes anyone + else from using and redistributing derivative content. + * One may not attribute any derivative content to authors not involved in + the creation of the content, though an attribution to the author is not + necessary. +Comment: http://www.azillionmonkeys.com/qed/weblicense.html + +License: BSD-2-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the complete text of the Apache version 2.0 license + can be found in "/usr/share/common-licenses/Apache-2.0". diff --git a/debian/default b/debian/default new file mode 100644 index 00000000..bf9d599f --- /dev/null +++ b/debian/default @@ -0,0 +1,2 @@ +# extra daemon options; see nutcracker --help +DAEMON_OPTS="" diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 00000000..df002342 --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +etc/nutcracker +var/log/nutcracker diff --git a/debian/docs b/debian/docs new file mode 100644 index 00000000..7dcefd3b --- /dev/null +++ b/debian/docs @@ -0,0 +1,3 @@ +README.md +notes/recommendation.md +notes/redis.md diff --git a/debian/examples b/debian/examples new file mode 100644 index 00000000..cf191d50 --- /dev/null +++ b/debian/examples @@ -0,0 +1 @@ +debian/nutcracker.yml diff --git a/debian/init.d b/debian/init.d new file mode 100644 index 00000000..bad5aaf8 --- /dev/null +++ b/debian/init.d @@ -0,0 +1,92 @@ +#! /bin/sh + +### BEGIN INIT INFO +# Provides: nutcracker +# Required-Start: $remote_fs $network +# Required-Stop: $remote_fs +# Should-Start: $time $named +# Should-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: nutcracker +# Description: Fast, light-weight proxy for memcached and Redis +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/nutcracker +NAME="nutcracker" +DESC="memcached and redis proxy" +PIDFILE=/run/nutcracker/nutcracker.pid +CONFFILE=/etc/nutcracker/nutcracker.yml + +. /lib/lsb/init-functions + +test -x $DAEMON || exit 0 + +[ -f /etc/default/$NAME ] && . /etc/default/$NAME + +DAEMON_OPTS="$DAEMON_OPTS -d" + +case "$1" in + start) + if pidofproc -p $PIDFILE $DAEMON > /dev/null; then + log_failure_msg "Starting $NAME (already started)" + exit 0 + fi + if [ ! -e $CONFFILE ]; then + log_daemon_msg "Not starting $NAME" "$DESC" + log_progress_msg "(unconfigured)" + log_end_msg 0 + exit 0 + fi + if ! $DAEMON -t $DAEMON_OPTS 2> /dev/null; then + log_failure_msg "Checking $NAME config syntax" + exit 1 + fi + log_daemon_msg "Starting $NAME" "$DESC" + mkdir -p /run/nutcracker + chown nutcracker:nutcracker /run/nutcracker + chmod 0755 /run/nutcracker + start-stop-daemon --quiet --start --chuid nutcracker \ + --pidfile $PIDFILE --exec $DAEMON \ + -- $DAEMON_OPTS + log_end_msg $? + ;; + stop) + log_daemon_msg "Stopping $NAME" "$DESC" + start-stop-daemon --quiet --stop --retry 5 \ + --pidfile $PIDFILE --exec $DAEMON + case "$?" in + 0) rm -f $PIDFILE; log_end_msg 0 ;; + 1) log_progress_msg "(already stopped)" + log_end_msg 0 ;; + *) log_end_msg 1 ;; + esac + ;; + reload) + log_daemon_msg "Reloading $NAME" "$DESC" + start-stop-daemon --quiet --stop --signal HUP --oknodo \ + --pidfile $PIDFILE --exec $DAEMON + case "$?" in + 0) log_end_msg 0 ;; + 1) log_progress_msg "(not running)" + log_end_msg 0 ;; + *) log_end_msg 1 ;; + esac + ;; + force-reload|restart) + if ! $DAEMON -t $DAEMON_OPTS 2> /dev/null; then + log_failure_msg "Checking $NAME config syntax" + exit 1 + fi + $0 stop + $0 start + ;; + status) + status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? + ;; + *) + echo "Usage: ${0} {start|stop|reload|force-reload|restart|status}" >&2 + exit 1 + ;; +esac diff --git a/debian/logrotate b/debian/logrotate new file mode 100644 index 00000000..2bf23da2 --- /dev/null +++ b/debian/logrotate @@ -0,0 +1,13 @@ +/var/log/nutcracker/nutcracker.log { + rotate 7 + daily + missingok + compress + delaycompress + notifempty + create 640 nutcracker adm + sharedscripts + postrotate + /usr/sbin/invoke-rc.d nutcracker reload > /dev/null + endscript +} diff --git a/debian/nutcracker.install b/debian/nutcracker.install new file mode 100644 index 00000000..b07b2a90 --- /dev/null +++ b/debian/nutcracker.install @@ -0,0 +1 @@ +debian/nutcracker.yml /etc/nutcracker diff --git a/debian/nutcracker.yml b/debian/nutcracker.yml new file mode 100644 index 00000000..2049b17e --- /dev/null +++ b/debian/nutcracker.yml @@ -0,0 +1,35 @@ +rediskeydb_srv: + redis: true + listen: 127.0.0.1:27379 + hash: fnv1a_64 + distribution: ketama + auto_eject_hosts: true + server_retry_timeout: 2000 + server_failure_limit: 1 + timeout: 400 + backlog: 1024 + preconnect: true + tcpkeepalive: true + servers: + - 127.0.0.1:6379:1 keydb1 + - 127.0.0.1:7379:1 keydb2 + - 127.0.0.1:17379:1 keydb3 + - 127.0.0.1:37379:1 keydb4 + +memcached_srv: + redis: false + listen: 127.0.0.1:33211 + hash: fnv1a_64 + distribution: ketama + auto_eject_hosts: true + server_retry_timeout: 2000 + server_failure_limit: 1 + timeout: 400 + backlog: 1024 + preconnect: true + tcpkeepalive: true + servers: + - 127.0.0.1:11211:1 memc1 + - 127.0.0.1:11233:1 memc2 + - 127.0.0.1:33211:1 memc3 + - 127.0.0.1:44211:1 memc4 diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 00000000..5ef25219 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,2 @@ +use_system_libyaml +sysconfdir diff --git a/debian/patches/sysconfdir b/debian/patches/sysconfdir new file mode 100644 index 00000000..1d0becd9 --- /dev/null +++ b/debian/patches/sysconfdir @@ -0,0 +1,60 @@ +Description: Use sysconfdir & localstatedir for conf/pid/logs +Author: Faidon Liambotis +Forwarded: https://github.com/twitter/twemproxy/pull/123 +Last-Update: 2016-12-17 + +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -1,4 +1,11 @@ + MAINTAINERCLEANFILES = Makefile.in ++BUILT_SOURCES = cfg-options.h ++CLEANFILES = cfg-options.h ++ ++cfg-options.h: Makefile ++ $(AM_V_GEN)echo '#define NC_CONF_PATH "$(sysconfdir)/nutcracker/nutcracker.yml"' >$@ ++ @echo '#define NC_LOG_PATH "$(localstatedir)/log/nutcracker/nutcracker.log"' >>$@ ++ @echo '#define NC_PID_FILE "/run/nutcracker/nutcracker.pid"' >>$@ + + AM_CPPFLAGS = + if !OS_SOLARIS +--- a/src/nc.c ++++ b/src/nc.c +@@ -28,19 +28,16 @@ + #include + #include + +-#define NC_CONF_PATH "/etc/nutcracker/nutcracker.yml" ++#include "cfg-options.h" + + #define NC_LOG_DEFAULT LOG_NOTICE + #define NC_LOG_MIN LOG_EMERG + #define NC_LOG_MAX LOG_PVERB +-#define NC_LOG_PATH NULL + + #define NC_STATS_PORT STATS_PORT + #define NC_STATS_ADDR STATS_ADDR + #define NC_STATS_INTERVAL STATS_INTERVAL + +-#define NC_PID_FILE NULL +- + #define NC_MBUF_SIZE MBUF_SIZE + #define NC_MBUF_MIN_SIZE MBUF_MIN_SIZE + #define NC_MBUF_MAX_SIZE MBUF_MAX_SIZE +@@ -298,7 +295,7 @@ nc_set_default_options(struct instance * + nci->mbuf_chunk_size = NC_MBUF_SIZE; + + nci->pid = (pid_t)-1; +- nci->pid_filename = NULL; ++ nci->pid_filename = NC_PID_FILE; + nci->pidfile = 0; + } + +@@ -488,7 +485,7 @@ nc_pre_run(struct instance *nci) + return status; + } + +- if (nci->pid_filename) { ++ if (daemonize && nci->pid_filename) { + status = nc_create_pidfile(nci); + if (status != NC_OK) { + return status; diff --git a/debian/patches/use_system_libyaml b/debian/patches/use_system_libyaml new file mode 100644 index 00000000..4b6ab8c8 --- /dev/null +++ b/debian/patches/use_system_libyaml @@ -0,0 +1,58 @@ +Description: Use system libyaml +Author: Faidon Liambotis +Forwarded: https://github.com/twitter/twemproxy/pull/121 +Last-Update: 2014-06-23 + +--- a/configure.ac ++++ b/configure.ac +@@ -196,15 +196,8 @@ AS_IF([test "x$disable_stats" = xyes], + [AC_DEFINE([HAVE_STATS], [1], [Define to 1 if stats is not disabled])]) + AC_MSG_RESULT($disable_stats) + +-# Untar the yaml-0.2.5 in contrib/ before config.status is rerun +-AC_CONFIG_COMMANDS_PRE([tar xvfz contrib/yaml-0.2.5.tar.gz -C contrib]) +- +-# Call yaml-0.2.5 ./configure recursively +-AC_CONFIG_SUBDIRS([contrib/yaml-0.2.5]) +- + # Define Makefiles + AC_CONFIG_FILES([Makefile +- contrib/Makefile + src/Makefile + src/hashkit/Makefile + src/proto/Makefile +--- a/Makefile.am ++++ b/Makefile.am +@@ -2,7 +2,7 @@ MAINTAINERCLEANFILES = Makefile.in acloc + + ACLOCAL_AMFLAGS = -I m4 + +-SUBDIRS = contrib src ++SUBDIRS = src + + dist_man_MANS = man/nutcracker.8 + +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -7,7 +7,6 @@ endif + AM_CPPFLAGS += -I $(top_srcdir)/src/hashkit + AM_CPPFLAGS += -I $(top_srcdir)/src/proto + AM_CPPFLAGS += -I $(top_srcdir)/src/event +-AM_CPPFLAGS += -I $(top_srcdir)/contrib/yaml-0.2.5/include + + AM_CFLAGS = + # about -fno-strict-aliasing: https://github.com/twitter/twemproxy/issues/276 +@@ -21,7 +20,7 @@ AM_CFLAGS += -Wconversion -Wsign-compare + AM_CFLAGS += -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations + + AM_LDFLAGS = +-AM_LDFLAGS += -lm -lpthread -rdynamic ++AM_LDFLAGS += -lm -lpthread -lyaml -rdynamic + if OS_SOLARIS + AM_LDFLAGS += -lnsl -lsocket + endif +@@ -57,4 +56,3 @@ nutcracker_SOURCES = \ + nutcracker_LDADD = $(top_builddir)/src/hashkit/libhashkit.a + nutcracker_LDADD += $(top_builddir)/src/proto/libproto.a + nutcracker_LDADD += $(top_builddir)/src/event/libevent.a +-nutcracker_LDADD += $(top_builddir)/contrib/yaml-0.2.5/src/.libs/libyaml.a diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 00000000..f422f8e0 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,30 @@ +#!/bin/sh + +set -e + +case "$1" in + configure|reconfigure) + adduser \ + --system \ + --quiet \ + --home /nonexistent \ + --no-create-home \ + --group \ + nutcracker + chown nutcracker:adm /var/log/nutcracker + chmod 02755 /var/log/nutcracker + ;; + abort-upgrade|abort-remove|abort-deconfigure) + exit 0 + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 + diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 00000000..56497355 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,24 @@ +#!/bin/sh + +set -e + +case "$1" in + purge) + if [ -x "$(command -v deluser)" ]; then + rm -rf /var/log/nutcracker/ + deluser --quiet --system nutcracker > /dev/null || true + deluser --group --system --quiet --only-if-empty nutcracker || true + else + echo >&2 "not removing nutcracker system account, group and logfiles because deluser command was not found" + fi + ;; + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 +esac + +#DEBHELPER# + +exit 0 diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..057e8306 --- /dev/null +++ b/debian/rules @@ -0,0 +1,22 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/buildflags.mk + +%: + dh $@ + +override_dh_clean: + rm -rf $(CURDIR)/debian/.debhelper $(CURDIR)/autom4te.cache + find -L $(CURDIR) -type f -name "Makefile" -delete + find -L $(CURDIR) -type f -name "Makefile.in" -delete + find -L $(CURDIR) -type f -name "config.log" -delete + find -L $(CURDIR) -type f -name "config.status" -delete + find -L $(CURDIR) -type f -name "libtool" -delete + find -L $(CURDIR) -type f -name "*.debhelper" -delete + find -L $(CURDIR) -type f -name "*.substvars" -delete + find -L $(CURDIR) -type f -name "*-build-stamp" -delete + dh_clean diff --git a/debian/service b/debian/service new file mode 100644 index 00000000..348d1d51 --- /dev/null +++ b/debian/service @@ -0,0 +1,23 @@ +[Unit] +Description=nutcracker proxy for memcached and Redis +Documentation=man:nutcracker +After=local-fs.target +After=network.target +ConditionPathExists=/etc/nutcracker/nutcracker.yml + +[Service] +Type=simple +EnvironmentFile=-/etc/default/nutcracker +ExecStart=/usr/sbin/nutcracker -c /etc/nutcracker/nutcracker.yml $DAEMON_OPTS +User=nutcracker +Group=nutcracker + +MountFlags=slave +DevicePolicy=closed +PrivateDevices=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=read-only + +[Install] +WantedBy=multi-user.target diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 00000000..163aaf8d --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/watch b/debian/watch new file mode 100644 index 00000000..7685b045 --- /dev/null +++ b/debian/watch @@ -0,0 +1,4 @@ +version=3 + +opts=dversionmangle=s/\+dfsg// \ +https://github.com/twitter/twemproxy/releases /twitter/twemproxy/archive/v(\d\.\d+\.\d+)\.tar\.gz diff --git a/notes/redis.md b/notes/redis.md index 641401a9..0c1da7f8 100644 --- a/notes/redis.md +++ b/notes/redis.md @@ -21,7 +21,7 @@ +-------------------+------------+---------------------------------------------------------------------------------------------------------------------+ | MOVE | No | MOVE key db | +-------------------+------------+---------------------------------------------------------------------------------------------------------------------+ - | OBJECT | No | OBJECT subcommand [arguments [arguments …]] | + | OBJECT | Yes | OBJECT subcommand argument | +-------------------+------------+---------------------------------------------------------------------------------------------------------------------+ | PERSIST | Yes | PERSIST key | +-------------------+------------+---------------------------------------------------------------------------------------------------------------------+ diff --git a/scripts/commands.json b/scripts/commands.json new file mode 100644 index 00000000..66aa8704 --- /dev/null +++ b/scripts/commands.json @@ -0,0 +1,5787 @@ +{ + "ACL LOAD": { + "summary": "Reload the ACLs from the configured ACL file", + "complexity": "O(N). Where N is the number of configured users.", + "since": "6.0.0", + "group": "server" + }, + "ACL SAVE": { + "summary": "Save the current ACL rules in the configured ACL file", + "complexity": "O(N). Where N is the number of configured users.", + "since": "6.0.0", + "group": "server" + }, + "ACL LIST": { + "summary": "List the current ACL rules in ACL config file format", + "complexity": "O(N). Where N is the number of configured users.", + "since": "6.0.0", + "group": "server" + }, + "ACL USERS": { + "summary": "List the username of all the configured ACL rules", + "complexity": "O(N). Where N is the number of configured users.", + "since": "6.0.0", + "group": "server" + }, + "ACL GETUSER": { + "summary": "Get the rules for a specific ACL user", + "complexity": "O(N). Where N is the number of password, command and pattern rules that the user has.", + "arguments": [ + { + "name": "username", + "type": "string" + } + ], + "since": "6.0.0", + "group": "server" + }, + "ACL SETUSER": { + "summary": "Modify or create the rules for a specific ACL user", + "complexity": "O(N). Where N is the number of rules provided.", + "arguments": [ + { + "name": "username", + "type": "string" + }, + { + "name": "rule", + "type": "string", + "multiple": true, + "optional": true + } + ], + "since": "6.0.0", + "group": "server" + }, + "ACL DELUSER": { + "summary": "Remove the specified ACL users and the associated rules", + "complexity": "O(1) amortized time considering the typical user.", + "arguments": [ + { + "name": "username", + "type": "string", + "multiple": true + } + ], + "since": "6.0.0", + "group": "server" + }, + "ACL CAT": { + "summary": "List the ACL categories or the commands inside a category", + "complexity": "O(1) since the categories and commands are a fixed set.", + "arguments": [ + { + "name": "categoryname", + "type": "string", + "optional": true + } + ], + "since": "6.0.0", + "group": "server" + }, + "ACL GENPASS": { + "summary": "Generate a pseudorandom secure password to use for ACL users", + "complexity": "O(1)", + "arguments": [ + { + "name": "bits", + "type": "integer", + "optional": true + } + ], + "since": "6.0.0", + "group": "server" + }, + "ACL WHOAMI": { + "summary": "Return the name of the user associated to the current connection", + "complexity": "O(1)", + "since": "6.0.0", + "group": "server" + }, + "ACL LOG": { + "summary": "List latest events denied because of ACLs in place", + "complexity": "O(N) with N being the number of entries shown.", + "arguments": [ + { + "name": "count or RESET", + "type": "string", + "optional": true + } + ], + "since": "6.0.0", + "group": "server" + }, + "ACL HELP": { + "summary": "Show helpful text about the different subcommands", + "complexity": "O(1)", + "since": "6.0.0", + "group": "server" + }, + "APPEND": { + "summary": "Append a value to a key", + "complexity": "O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "2.0.0", + "group": "string" + }, + "AUTH": { + "summary": "Authenticate to the server", + "arguments": [ + { + "name": "username", + "type": "string", + "optional": true + }, + { + "name": "password", + "type": "string" + } + ], + "since": "1.0.0", + "group": "connection" + }, + "BGREWRITEAOF": { + "summary": "Asynchronously rewrite the append-only file", + "since": "1.0.0", + "group": "server" + }, + "BGSAVE": { + "summary": "Asynchronously save the dataset to disk", + "arguments": [ + { + "name": "schedule", + "type": "enum", + "enum": [ + "SCHEDULE" + ], + "optional": true + } + ], + "since": "1.0.0", + "group": "server" + }, + "BITCOUNT": { + "summary": "Count set bits in a string", + "complexity": "O(N)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": [ + "start", + "end" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + } + ], + "since": "2.6.0", + "group": "bitmap" + }, + "BITFIELD": { + "summary": "Perform arbitrary bitfield integer operations on strings", + "complexity": "O(1) for each subcommand specified", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "command": "GET", + "name": [ + "type", + "offset" + ], + "type": [ + "type", + "integer" + ], + "optional": true + }, + { + "command": "SET", + "name": [ + "type", + "offset", + "value" + ], + "type": [ + "type", + "integer", + "integer" + ], + "optional": true + }, + { + "command": "INCRBY", + "name": [ + "type", + "offset", + "increment" + ], + "type": [ + "type", + "integer", + "integer" + ], + "optional": true + }, + { + "command": "OVERFLOW", + "type": "enum", + "enum": [ + "WRAP", + "SAT", + "FAIL" + ], + "optional": true + } + ], + "since": "3.2.0", + "group": "bitmap" + }, + "BITOP": { + "summary": "Perform bitwise operations between strings", + "complexity": "O(N)", + "arguments": [ + { + "name": "operation", + "type": "string" + }, + { + "name": "destkey", + "type": "key" + }, + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "2.6.0", + "group": "bitmap" + }, + "BITPOS": { + "summary": "Find first bit set or clear in a string", + "complexity": "O(N)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "bit", + "type": "integer" + }, + { + "name": "index", + "type": "block", + "optional": true, + "block": [ + { + "name": "start", + "type": "integer" + }, + { + "name": "end", + "type": "integer", + "optional": true + } + ] + } + ], + "since": "2.8.7", + "group": "bitmap" + }, + "BLPOP": { + "summary": "Remove and get the first element in a list, or block until one is available", + "complexity": "O(N) where N is the number of provided keys.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "timeout", + "type": "double" + } + ], + "since": "2.0.0", + "group": "list" + }, + "BRPOP": { + "summary": "Remove and get the last element in a list, or block until one is available", + "complexity": "O(N) where N is the number of provided keys.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "timeout", + "type": "double" + } + ], + "since": "2.0.0", + "group": "list" + }, + "BRPOPLPUSH": { + "summary": "Pop an element from a list, push it to another list and return it; or block until one is available", + "complexity": "O(1)", + "arguments": [ + { + "name": "source", + "type": "key" + }, + { + "name": "destination", + "type": "key" + }, + { + "name": "timeout", + "type": "double" + } + ], + "since": "2.2.0", + "group": "list" + }, + "BLMOVE": { + "summary": "Pop an element from a list, push it to another list and return it; or block until one is available", + "complexity": "O(1)", + "arguments": [ + { + "name": "source", + "type": "key" + }, + { + "name": "destination", + "type": "key" + }, + { + "name": "wherefrom", + "type": "enum", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + { + "name": "whereto", + "type": "enum", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + { + "name": "timeout", + "type": "double" + } + ], + "since": "6.2.0", + "group": "list" + }, + "BZPOPMIN": { + "summary": "Remove and return the member with the lowest score from one or more sorted sets, or block until one is available", + "complexity": "O(log(N)) with N being the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "timeout", + "type": "double" + } + ], + "since": "5.0.0", + "group": "sorted_set" + }, + "BZPOPMAX": { + "summary": "Remove and return the member with the highest score from one or more sorted sets, or block until one is available", + "complexity": "O(log(N)) with N being the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "timeout", + "type": "double" + } + ], + "since": "5.0.0", + "group": "sorted_set" + }, + "CLIENT CACHING": { + "summary": "Instruct the server about tracking or not keys in the next request", + "complexity": "O(1)", + "arguments": [ + { + "name": "mode", + "type": "enum", + "enum": [ + "YES", + "NO" + ] + } + ], + "since": "6.0.0", + "group": "connection" + }, + "CLIENT ID": { + "summary": "Returns the client ID for the current connection", + "complexity": "O(1)", + "since": "5.0.0", + "group": "connection" + }, + "CLIENT INFO": { + "summary": "Returns information about the current client connection.", + "complexity": "O(1)", + "since": "6.2.0", + "group": "connection" + }, + "CLIENT KILL": { + "summary": "Kill the connection of a client", + "complexity": "O(N) where N is the number of client connections", + "arguments": [ + { + "name": "ip:port", + "type": "string", + "optional": true + }, + { + "command": "ID", + "name": "client-id", + "type": "integer", + "optional": true + }, + { + "command": "TYPE", + "type": "enum", + "enum": [ + "normal", + "master", + "slave", + "pubsub" + ], + "optional": true + }, + { + "command": "USER", + "name": "username", + "type": "string", + "optional": true + }, + { + "command": "ADDR", + "name": "ip:port", + "type": "string", + "optional": true + }, + { + "command": "LADDR", + "name": "ip:port", + "type": "string", + "optional": true + }, + { + "command": "SKIPME", + "name": "yes/no", + "type": "string", + "optional": true + } + ], + "since": "2.4.0", + "group": "connection" + }, + "CLIENT LIST": { + "summary": "Get the list of client connections", + "complexity": "O(N) where N is the number of client connections", + "arguments": [ + { + "command": "TYPE", + "type": "enum", + "enum": [ + "normal", + "master", + "replica", + "pubsub" + ], + "optional": true + }, + { + "name": "id", + "type": "block", + "block": [ + { + "command": "ID" + }, + { + "name": "client-id", + "type": "integer", + "multiple": true + } + ], + "optional": true + } + ], + "since": "2.4.0", + "group": "connection" + }, + "CLIENT GETNAME": { + "summary": "Get the current connection name", + "complexity": "O(1)", + "since": "2.6.9", + "group": "connection" + }, + "CLIENT GETREDIR": { + "summary": "Get tracking notifications redirection client ID if any", + "complexity": "O(1)", + "since": "6.0.0", + "group": "connection" + }, + "CLIENT UNPAUSE": { + "summary": "Resume processing of clients that were paused", + "complexity": "O(N) Where N is the number of paused clients", + "since": "6.2.0", + "group": "connection" + }, + "CLIENT PAUSE": { + "summary": "Stop processing commands from clients for some time", + "complexity": "O(1)", + "arguments": [ + { + "name": "timeout", + "type": "integer" + }, + { + "name": "mode", + "type": "enum", + "optional": true, + "enum": [ + "WRITE", + "ALL" + ] + } + ], + "since": "2.9.50", + "group": "connection" + }, + "CLIENT REPLY": { + "summary": "Instruct the server whether to reply to commands", + "complexity": "O(1)", + "arguments": [ + { + "name": "reply-mode", + "type": "enum", + "enum": [ + "ON", + "OFF", + "SKIP" + ] + } + ], + "since": "3.2.0", + "group": "connection" + }, + "CLIENT SETNAME": { + "summary": "Set the current connection name", + "complexity": "O(1)", + "since": "2.6.9", + "arguments": [ + { + "name": "connection-name", + "type": "string" + } + ], + "group": "connection" + }, + "CLIENT TRACKING": { + "summary": "Enable or disable server assisted client side caching support", + "complexity": "O(1). Some options may introduce additional complexity.", + "arguments": [ + { + "name": "status", + "type": "enum", + "enum": [ + "ON", + "OFF" + ] + }, + { + "command": "REDIRECT", + "name": "client-id", + "type": "integer", + "optional": true + }, + { + "command": "PREFIX", + "name": "prefix", + "type": "string", + "optional": true, + "multiple": true + }, + { + "name": "BCAST", + "type": "enum", + "enum": [ + "BCAST" + ], + "optional": true + }, + { + "name": "OPTIN", + "type": "enum", + "enum": [ + "OPTIN" + ], + "optional": true + }, + { + "name": "OPTOUT", + "type": "enum", + "enum": [ + "OPTOUT" + ], + "optional": true + }, + { + "name": "NOLOOP", + "type": "enum", + "enum": [ + "NOLOOP" + ], + "optional": true + } + ], + "since": "6.0.0", + "group": "connection" + }, + "CLIENT TRACKINGINFO": { + "summary": "Return information about server assisted client side caching for the current connection", + "complexity": "O(1)", + "since": "6.2.0", + "group": "connection" + }, + "CLIENT UNBLOCK": { + "summary": "Unblock a client blocked in a blocking command from a different connection", + "complexity": "O(log N) where N is the number of client connections", + "arguments": [ + { + "name": "client-id", + "type": "integer" + }, + { + "name": "unblock-type", + "type": "enum", + "enum": [ + "TIMEOUT", + "ERROR" + ], + "optional": true + } + ], + "since": "5.0.0", + "group": "connection" + }, + "CLUSTER ADDSLOTS": { + "summary": "Assign new hash slots to receiving node", + "complexity": "O(N) where N is the total number of hash slot arguments", + "arguments": [ + { + "name": "slot", + "type": "integer", + "multiple": true + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER BUMPEPOCH": { + "summary": "Advance the cluster config epoch", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER COUNT-FAILURE-REPORTS": { + "summary": "Return the number of failure reports active for a given node", + "complexity": "O(N) where N is the number of failure reports", + "arguments": [ + { + "name": "node-id", + "type": "string" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER COUNTKEYSINSLOT": { + "summary": "Return the number of local keys in the specified hash slot", + "complexity": "O(1)", + "arguments": [ + { + "name": "slot", + "type": "integer" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER DELSLOTS": { + "summary": "Set hash slots as unbound in receiving node", + "complexity": "O(N) where N is the total number of hash slot arguments", + "arguments": [ + { + "name": "slot", + "type": "integer", + "multiple": true + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER FAILOVER": { + "summary": "Forces a replica to perform a manual failover of its master.", + "complexity": "O(1)", + "arguments": [ + { + "name": "options", + "type": "enum", + "enum": [ + "FORCE", + "TAKEOVER" + ], + "optional": true + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER FLUSHSLOTS": { + "summary": "Delete a node's own slots information", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER FORGET": { + "summary": "Remove a node from the nodes table", + "complexity": "O(1)", + "arguments": [ + { + "name": "node-id", + "type": "string" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER GETKEYSINSLOT": { + "summary": "Return local key names in the specified hash slot", + "complexity": "O(log(N)) where N is the number of requested keys", + "arguments": [ + { + "name": "slot", + "type": "integer" + }, + { + "name": "count", + "type": "integer" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER INFO": { + "summary": "Provides info about Redis Cluster node state", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER KEYSLOT": { + "summary": "Returns the hash slot of the specified key", + "complexity": "O(N) where N is the number of bytes in the key", + "arguments": [ + { + "name": "key", + "type": "string" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER MEET": { + "summary": "Force a node cluster to handshake with another node", + "complexity": "O(1)", + "arguments": [ + { + "name": "ip", + "type": "string" + }, + { + "name": "port", + "type": "integer" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER MYID": { + "summary": "Return the node id", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER NODES": { + "summary": "Get Cluster config for the node", + "complexity": "O(N) where N is the total number of Cluster nodes", + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER REPLICATE": { + "summary": "Reconfigure a node as a replica of the specified master node", + "complexity": "O(1)", + "arguments": [ + { + "name": "node-id", + "type": "string" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER RESET": { + "summary": "Reset a Redis Cluster node", + "complexity": "O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.", + "arguments": [ + { + "name": "reset-type", + "type": "enum", + "enum": [ + "HARD", + "SOFT" + ], + "optional": true + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER SAVECONFIG": { + "summary": "Forces the node to save cluster state on disk", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER SET-CONFIG-EPOCH": { + "summary": "Set the configuration epoch in a new node", + "complexity": "O(1)", + "arguments": [ + { + "name": "config-epoch", + "type": "integer" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER SETSLOT": { + "summary": "Bind a hash slot to a specific node", + "complexity": "O(1)", + "arguments": [ + { + "name": "slot", + "type": "integer" + }, + { + "name": "subcommand", + "type": "enum", + "enum": [ + "IMPORTING", + "MIGRATING", + "STABLE", + "NODE" + ] + }, + { + "name": "node-id", + "type": "string", + "optional": true + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER SLAVES": { + "summary": "List replica nodes of the specified master node", + "complexity": "O(1)", + "arguments": [ + { + "name": "node-id", + "type": "string" + } + ], + "since": "3.0.0", + "group": "cluster" + }, + "CLUSTER REPLICAS": { + "summary": "List replica nodes of the specified master node", + "complexity": "O(1)", + "arguments": [ + { + "name": "node-id", + "type": "string" + } + ], + "since": "5.0.0", + "group": "cluster" + }, + "CLUSTER SLOTS": { + "summary": "Get array of Cluster slot to node mappings", + "complexity": "O(N) where N is the total number of Cluster nodes", + "since": "3.0.0", + "group": "cluster" + }, + "COMMAND": { + "summary": "Get array of Redis command details", + "complexity": "O(N) where N is the total number of Redis commands", + "since": "2.8.13", + "group": "server" + }, + "COMMAND COUNT": { + "summary": "Get total number of Redis commands", + "complexity": "O(1)", + "since": "2.8.13", + "group": "server" + }, + "COMMAND GETKEYS": { + "summary": "Extract keys given a full Redis command", + "complexity": "O(N) where N is the number of arguments to the command", + "since": "2.8.13", + "group": "server" + }, + "COMMAND INFO": { + "summary": "Get array of specific Redis command details", + "complexity": "O(N) when N is number of commands to look up", + "since": "2.8.13", + "arguments": [ + { + "name": "command-name", + "type": "string", + "multiple": true + } + ], + "group": "server" + }, + "CONFIG GET": { + "summary": "Get the value of a configuration parameter", + "arguments": [ + { + "name": "parameter", + "type": "string" + } + ], + "since": "2.0.0", + "group": "server" + }, + "CONFIG REWRITE": { + "summary": "Rewrite the configuration file with the in memory configuration", + "since": "2.8.0", + "group": "server" + }, + "CONFIG SET": { + "summary": "Set a configuration parameter to the given value", + "arguments": [ + { + "name": "parameter", + "type": "string" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "2.0.0", + "group": "server" + }, + "CONFIG RESETSTAT": { + "summary": "Reset the stats returned by INFO", + "complexity": "O(1)", + "since": "2.0.0", + "group": "server" + }, + "COPY": { + "summary": "Copy a key", + "complexity": "O(N) worst case for collections, where N is the number of nested items. O(1) for string values.", + "since": "6.2.0", + "arguments": [ + { + "name": "source", + "type": "key" + }, + { + "name": "destination", + "type": "key" + }, + { + "command": "DB", + "name": "destination-db", + "type": "integer", + "optional": true + }, + { + "name": "replace", + "type": "enum", + "enum": [ + "REPLACE" + ], + "optional": true + } + ], + "group": "generic" + }, + "DBSIZE": { + "summary": "Return the number of keys in the selected database", + "since": "1.0.0", + "group": "server" + }, + "DEBUG OBJECT": { + "summary": "Get debugging information about a key", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "server" + }, + "DEBUG SEGFAULT": { + "summary": "Make the server crash", + "since": "1.0.0", + "group": "server" + }, + "DECR": { + "summary": "Decrement the integer value of a key by one", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "string" + }, + "DECRBY": { + "summary": "Decrement the integer value of a key by the given number", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "decrement", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "string" + }, + "DEL": { + "summary": "Delete a key", + "complexity": "O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "generic" + }, + "DISCARD": { + "summary": "Discard all commands issued after MULTI", + "since": "2.0.0", + "group": "transactions" + }, + "DUMP": { + "summary": "Return a serialized version of the value stored at the specified key.", + "complexity": "O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.6.0", + "group": "generic" + }, + "ECHO": { + "summary": "Echo the given string", + "arguments": [ + { + "name": "message", + "type": "string" + } + ], + "since": "1.0.0", + "group": "connection" + }, + "EVAL": { + "summary": "Execute a Lua script server side", + "complexity": "Depends on the script that is executed.", + "arguments": [ + { + "name": "script", + "type": "string" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "optional": true, + "multiple": true + }, + { + "name": "arg", + "type": "string", + "optional": true, + "multiple": true + } + ], + "since": "2.6.0", + "group": "scripting" + }, + "EVAL_RO": { + "summary": "Execute a read-only Lua script server side", + "complexity": "Depends on the script that is executed.", + "arguments": [ + { + "name": "script", + "type": "string" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "arg", + "type": "string", + "multiple": true + } + ], + "since": "7.0.0", + "group": "scripting" + }, + "EVALSHA": { + "summary": "Execute a Lua script server side", + "complexity": "Depends on the script that is executed.", + "arguments": [ + { + "name": "sha1", + "type": "string" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "optional": true, + "multiple": true + }, + { + "name": "arg", + "type": "string", + "optional": true, + "multiple": true + } + ], + "since": "2.6.0", + "group": "scripting" + }, + "EVALSHA_RO": { + "summary": "Execute a read-only Lua script server side", + "complexity": "Depends on the script that is executed.", + "arguments": [ + { + "name": "sha1", + "type": "string" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "arg", + "type": "string", + "multiple": true + } + ], + "since": "7.0.0", + "group": "scripting" + }, + "EXEC": { + "summary": "Execute all commands issued after MULTI", + "since": "1.2.0", + "group": "transactions" + }, + "EXISTS": { + "summary": "Determine if a key exists", + "complexity": "O(N) where N is the number of keys to check.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "generic" + }, + "EXPIRE": { + "summary": "Set a key's time to live in seconds", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "seconds", + "type": "integer" + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX", + "GT", + "LT" + ], + "optional": true + } + ], + "since": "1.0.0", + "group": "generic" + }, + "EXPIREAT": { + "summary": "Set the expiration for a key as a UNIX timestamp", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "timestamp", + "type": "posix time" + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX", + "GT", + "LT" + ], + "optional": true + } + ], + "since": "1.2.0", + "group": "generic" + }, + "EXPIRETIME": { + "summary": "Get the expiration Unix timestamp for a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "7.0.0", + "group": "generic" + }, + "FAILOVER": { + "summary": "Start a coordinated failover between this server and one of its replicas.", + "arguments": [ + { + "name": "target", + "type": "block", + "optional": true, + "block": [ + { + "command": "TO" + }, + { + "name": "host", + "type": "string" + }, + { + "name": "port", + "type": "integer" + }, + { + "command": "FORCE", + "optional": true + } + ] + }, + { + "command": "ABORT", + "optional": true + }, + { + "command": "TIMEOUT", + "name": "milliseconds", + "type": "integer", + "optional": true + } + ], + "since": "6.2.0", + "group": "server" + }, + "FLUSHALL": { + "summary": "Remove all keys from all databases", + "complexity": "O(N) where N is the total number of keys in all databases", + "arguments": [ + { + "name": "async", + "type": "enum", + "enum": [ + "ASYNC", + "SYNC" + ], + "optional": true + } + ], + "since": "1.0.0", + "group": "server" + }, + "FLUSHDB": { + "summary": "Remove all keys from the current database", + "complexity": "O(N) where N is the number of keys in the selected database", + "arguments": [ + { + "name": "async", + "type": "enum", + "enum": [ + "ASYNC", + "SYNC" + ], + "optional": true + } + ], + "since": "1.0.0", + "group": "server" + }, + "GEOADD": { + "summary": "Add one or more geospatial items in the geospatial index represented using a sorted set", + "complexity": "O(log(N)) for each item added, where N is the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX" + ], + "optional": true + }, + { + "name": "change", + "type": "enum", + "enum": [ + "CH" + ], + "optional": true + }, + { + "name": [ + "longitude", + "latitude", + "member" + ], + "type": [ + "double", + "double", + "string" + ], + "multiple": true + } + ], + "since": "3.2.0", + "group": "geo" + }, + "GEOHASH": { + "summary": "Returns members of a geospatial index as standard geohash strings", + "complexity": "O(log(N)) for each member requested, where N is the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "3.2.0", + "group": "geo" + }, + "GEOPOS": { + "summary": "Returns longitude and latitude of members of a geospatial index", + "complexity": "O(N) where N is the number of members requested.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "3.2.0", + "group": "geo" + }, + "GEODIST": { + "summary": "Returns the distance between two members of a geospatial index", + "complexity": "O(log(N))", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member1", + "type": "string" + }, + { + "name": "member2", + "type": "string" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ], + "optional": true + } + ], + "since": "3.2.0", + "group": "geo" + }, + "GEORADIUS": { + "summary": "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point", + "complexity": "O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "longitude", + "type": "double" + }, + { + "name": "latitude", + "type": "double" + }, + { + "name": "radius", + "type": "double" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ] + }, + { + "name": "withcoord", + "type": "enum", + "enum": [ + "WITHCOORD" + ], + "optional": true + }, + { + "name": "withdist", + "type": "enum", + "enum": [ + "WITHDIST" + ], + "optional": true + }, + { + "name": "withhash", + "type": "enum", + "enum": [ + "WITHHASH" + ], + "optional": true + }, + { + "type": "block", + "name": "count", + "block": [ + { + "name": "count", + "command": "COUNT", + "type": "integer" + }, + { + "name": "any", + "type": "enum", + "enum": [ + "ANY" + ], + "optional": true + } + ], + "optional": true + }, + { + "name": "order", + "type": "enum", + "enum": [ + "ASC", + "DESC" + ], + "optional": true + }, + { + "command": "STORE", + "name": "key", + "type": "key", + "optional": true + }, + { + "command": "STOREDIST", + "name": "key", + "type": "key", + "optional": true + } + ], + "since": "3.2.0", + "group": "geo" + }, + "GEORADIUSBYMEMBER": { + "summary": "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member", + "complexity": "O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string" + }, + { + "name": "radius", + "type": "double" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ] + }, + { + "name": "withcoord", + "type": "enum", + "enum": [ + "WITHCOORD" + ], + "optional": true + }, + { + "name": "withdist", + "type": "enum", + "enum": [ + "WITHDIST" + ], + "optional": true + }, + { + "name": "withhash", + "type": "enum", + "enum": [ + "WITHHASH" + ], + "optional": true + }, + { + "type": "block", + "name": "count", + "block": [ + { + "name": "count", + "command": "COUNT", + "type": "integer" + }, + { + "name": "any", + "type": "enum", + "enum": [ + "ANY" + ], + "optional": true + } + ], + "optional": true + }, + { + "name": "order", + "type": "enum", + "enum": [ + "ASC", + "DESC" + ], + "optional": true + }, + { + "command": "STORE", + "name": "key", + "type": "key", + "optional": true + }, + { + "command": "STOREDIST", + "name": "key", + "type": "key", + "optional": true + } + ], + "since": "3.2.0", + "group": "geo" + }, + "GEOSEARCH": { + "summary": "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.", + "complexity": "O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "command": "FROMMEMBER", + "name": "member", + "type": "string", + "optional": true + }, + { + "command": "FROMLONLAT", + "name": [ + "longitude", + "latitude" + ], + "type": [ + "double", + "double" + ], + "optional": true + }, + { + "type": "block", + "name": "circle", + "block": [ + { + "name": "radius", + "command": "BYRADIUS", + "type": "double" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ] + } + ], + "optional": true + }, + { + "type": "block", + "name": "box", + "block": [ + { + "name": "width", + "command": "BYBOX", + "type": "double" + }, + { + "name": "height", + "type": "double" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ] + } + ], + "optional": true + }, + { + "name": "order", + "type": "enum", + "enum": [ + "ASC", + "DESC" + ], + "optional": true + }, + { + "type": "block", + "name": "count", + "block": [ + { + "name": "count", + "command": "COUNT", + "type": "integer" + }, + { + "name": "any", + "type": "enum", + "enum": [ + "ANY" + ], + "optional": true + } + ], + "optional": true + }, + { + "name": "withcoord", + "type": "enum", + "enum": [ + "WITHCOORD" + ], + "optional": true + }, + { + "name": "withdist", + "type": "enum", + "enum": [ + "WITHDIST" + ], + "optional": true + }, + { + "name": "withhash", + "type": "enum", + "enum": [ + "WITHHASH" + ], + "optional": true + } + ], + "since": "6.2", + "group": "geo" + }, + "GEOSEARCHSTORE": { + "summary": "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle, and store the result in another key.", + "complexity": "O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "source", + "type": "key" + }, + { + "command": "FROMMEMBER", + "name": "member", + "type": "string", + "optional": true + }, + { + "command": "FROMLONLAT", + "name": [ + "longitude", + "latitude" + ], + "type": [ + "double", + "double" + ], + "optional": true + }, + { + "type": "block", + "name": "circle", + "block": [ + { + "name": "radius", + "command": "BYRADIUS", + "type": "double" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ] + } + ], + "optional": true + }, + { + "type": "block", + "name": "box", + "block": [ + { + "name": "width", + "command": "BYBOX", + "type": "double" + }, + { + "name": "height", + "type": "double" + }, + { + "name": "unit", + "type": "enum", + "enum": [ + "m", + "km", + "ft", + "mi" + ] + } + ], + "optional": true + }, + { + "name": "order", + "type": "enum", + "enum": [ + "ASC", + "DESC" + ], + "optional": true + }, + { + "type": "block", + "name": "count", + "block": [ + { + "name": "count", + "command": "COUNT", + "type": "integer" + }, + { + "name": "any", + "type": "enum", + "enum": [ + "ANY" + ], + "optional": true + } + ], + "optional": true + }, + { + "name": "storedist", + "type": "enum", + "enum": [ + "STOREDIST" + ], + "optional": true + } + ], + "since": "6.2", + "group": "geo" + }, + "GET": { + "summary": "Get the value of a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "string" + }, + "GETBIT": { + "summary": "Returns the bit value at offset in the string value stored at key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "offset", + "type": "integer" + } + ], + "since": "2.2.0", + "group": "bitmap" + }, + "GETDEL": { + "summary":"Get the value of a key and delete the key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "6.2.0", + "group": "string" + }, + "GETEX": { + "summary": "Get the value of a key and optionally set its expiration", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "expiration", + "type": "enum", + "enum": [ + "EX seconds", + "PX milliseconds", + "EXAT timestamp", + "PXAT milliseconds-timestamp", + "PERSIST" + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "string" + }, + "GETRANGE": { + "summary": "Get a substring of the string stored at a key", + "complexity": "O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "start", + "type": "integer" + }, + { + "name": "end", + "type": "integer" + } + ], + "since": "2.4.0", + "group": "string" + }, + "GETSET": { + "summary": "Set the string value of a key and return its old value", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "1.0.0", + "group": "string" + }, + "HDEL": { + "summary": "Delete one or more hash fields", + "complexity": "O(N) where N is the number of fields to be removed.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string", + "multiple": true + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HELLO": { + "summary": "Handshake with Redis", + "complexity": "O(1)", + "arguments": [ + { + "name": "arguments", + "type": "block", + "block": [ + { + "name": "protover", + "type": "integer" + }, + { + "command": "AUTH", + "name": [ + "username", + "password" + ], + "type": [ + "string", + "string" + ], + "optional": true + }, + { + "command": "SETNAME", + "name": "clientname", + "type": "string", + "optional": true + } + ], + "optional": true + } + ], + "since": "6.0.0", + "group": "connection" + }, + "HEXISTS": { + "summary": "Determine if a hash field exists", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HGET": { + "summary": "Get the value of a hash field", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HGETALL": { + "summary": "Get all the fields and values in a hash", + "complexity": "O(N) where N is the size of the hash.", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HINCRBY": { + "summary": "Increment the integer value of a hash field by the given number", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string" + }, + { + "name": "increment", + "type": "integer" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HINCRBYFLOAT": { + "summary": "Increment the float value of a hash field by the given amount", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string" + }, + { + "name": "increment", + "type": "double" + } + ], + "since": "2.6.0", + "group": "hash" + }, + "HKEYS": { + "summary": "Get all the fields in a hash", + "complexity": "O(N) where N is the size of the hash.", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HLEN": { + "summary": "Get the number of fields in a hash", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HMGET": { + "summary": "Get the values of all the given hash fields", + "complexity": "O(N) where N is the number of fields being requested.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string", + "multiple": true + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HMSET": { + "summary": "Set multiple hash fields to multiple values", + "complexity": "O(N) where N is the number of fields being set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": [ + "field", + "value" + ], + "type": [ + "string", + "string" + ], + "multiple": true + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HSET": { + "summary": "Set the string value of a hash field", + "complexity": "O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": [ + "field", + "value" + ], + "type": [ + "string", + "string" + ], + "multiple": true + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HSETNX": { + "summary": "Set the value of a hash field, only if the field does not exist", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "HRANDFIELD": { + "summary": "Get one or multiple random fields from a hash", + "complexity": "O(N) where N is the number of fields returned", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "options", + "type": "block", + "block": [ + { + "name": "count", + "type": "integer" + }, + { + "name": "withvalues", + "type": "enum", + "enum": [ + "WITHVALUES" + ], + "optional": true + } + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "hash" + }, + "HSTRLEN": { + "summary": "Get the length of the value of a hash field", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "field", + "type": "string" + } + ], + "since": "3.2.0", + "group": "hash" + }, + "HVALS": { + "summary": "Get all the values in a hash", + "complexity": "O(N) where N is the size of the hash.", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.0.0", + "group": "hash" + }, + "INCR": { + "summary": "Increment the integer value of a key by one", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "string" + }, + "INCRBY": { + "summary": "Increment the integer value of a key by the given amount", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "increment", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "string" + }, + "INCRBYFLOAT": { + "summary": "Increment the float value of a key by the given amount", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "increment", + "type": "double" + } + ], + "since": "2.6.0", + "group": "string" + }, + "INFO": { + "summary": "Get information and statistics about the server", + "arguments": [ + { + "name": "section", + "type": "string", + "optional": true + } + ], + "since": "1.0.0", + "group": "server" + }, + "LOLWUT": { + "summary": "Display some computer art and the Redis version", + "arguments": [ + { + "command": "VERSION", + "name": "version", + "type": "integer", + "optional": true + } + ], + "since": "5.0.0", + "group": "server" + }, + "KEYS": { + "summary": "Find all keys matching the given pattern", + "complexity": "O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.", + "arguments": [ + { + "name": "pattern", + "type": "pattern" + } + ], + "since": "1.0.0", + "group": "generic" + }, + "LASTSAVE": { + "summary": "Get the UNIX time stamp of the last successful save to disk", + "since": "1.0.0", + "group": "server" + }, + "LINDEX": { + "summary": "Get an element from a list by its index", + "complexity": "O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "index", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "list" + }, + "LINSERT": { + "summary": "Insert an element before or after another element in a list", + "complexity": "O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "where", + "type": "enum", + "enum": [ + "BEFORE", + "AFTER" + ] + }, + { + "name": "pivot", + "type": "string" + }, + { + "name": "element", + "type": "string" + } + ], + "since": "2.2.0", + "group": "list" + }, + "LLEN": { + "summary": "Get the length of a list", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "list" + }, + "LPOP": { + "summary": "Remove and get the first elements in a list", + "complexity": "O(N) where N is the number of elements returned", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "1.0.0", + "group": "list" + }, + "LPOS": { + "summary": "Return the index of matching elements on a list", + "complexity": "O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "element", + "type": "string" + }, + { + "command": "RANK", + "name": "rank", + "type": "integer", + "optional": true + }, + { + "command": "COUNT", + "name": "num-matches", + "type": "integer", + "optional": true + }, + { + "command": "MAXLEN", + "name": "len", + "type": "integer", + "optional": true + } + ], + "since": "6.0.6", + "group": "list" + }, + "LPUSH": { + "summary": "Prepend one or multiple elements to a list", + "complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "element", + "type": "string", + "multiple": true + } + ], + "since": "1.0.0", + "group": "list" + }, + "LPUSHX": { + "summary": "Prepend an element to a list, only if the list exists", + "complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "element", + "type": "string", + "multiple": true + } + ], + "since": "2.2.0", + "group": "list" + }, + "LRANGE": { + "summary": "Get a range of elements from a list", + "complexity": "O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "start", + "type": "integer" + }, + { + "name": "stop", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "list" + }, + "LREM": { + "summary": "Remove elements from a list", + "complexity": "O(N+M) where N is the length of the list and M is the number of elements removed.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer" + }, + { + "name": "element", + "type": "string" + } + ], + "since": "1.0.0", + "group": "list" + }, + "LSET": { + "summary": "Set the value of an element in a list by its index", + "complexity": "O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "index", + "type": "integer" + }, + { + "name": "element", + "type": "string" + } + ], + "since": "1.0.0", + "group": "list" + }, + "LTRIM": { + "summary": "Trim a list to the specified range", + "complexity": "O(N) where N is the number of elements to be removed by the operation.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "start", + "type": "integer" + }, + { + "name": "stop", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "list" + }, + "MEMORY DOCTOR": { + "summary": "Outputs memory problems report", + "since": "4.0.0", + "group": "server" + }, + "MEMORY HELP": { + "summary": "Show helpful text about the different subcommands", + "since": "4.0.0", + "group": "server" + }, + "MEMORY MALLOC-STATS": { + "summary": "Show allocator internal stats", + "since": "4.0.0", + "group": "server" + }, + "MEMORY PURGE": { + "summary": "Ask the allocator to release memory", + "since": "4.0.0", + "group": "server" + }, + "MEMORY STATS": { + "summary": "Show memory usage details", + "since": "4.0.0", + "group": "server" + }, + "MEMORY USAGE": { + "summary": "Estimate the memory usage of a key", + "complexity": "O(N) where N is the number of samples.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "command": "SAMPLES", + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "4.0.0", + "group": "server" + }, + "MGET": { + "summary": "Get the values of all the given keys", + "complexity": "O(N) where N is the number of keys to retrieve.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "string" + }, + "MIGRATE": { + "summary": "Atomically transfer a key from a Redis instance to another one.", + "complexity": "This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.", + "arguments": [ + { + "name": "host", + "type": "string" + }, + { + "name": "port", + "type": "string" + }, + { + "name": "key", + "type": "enum", + "enum": [ + "key", + "\"\"" + ] + }, + { + "name": "destination-db", + "type": "integer" + }, + { + "name": "timeout", + "type": "integer" + }, + { + "name": "copy", + "type": "enum", + "enum": [ + "COPY" + ], + "optional": true + }, + { + "name": "replace", + "type": "enum", + "enum": [ + "REPLACE" + ], + "optional": true + }, + { + "command": "AUTH", + "name": "password", + "type": "string", + "optional": true + }, + { + "command": "AUTH2", + "name": "username password", + "type": "string", + "optional": true + }, + { + "name": "key", + "command": "KEYS", + "type": "key", + "variadic": true, + "optional": true + } + ], + "since": "2.6.0", + "group": "generic" + }, + "MODULE LIST": { + "summary": "List all modules loaded by the server", + "complexity": "O(N) where N is the number of loaded modules.", + "since": "4.0.0", + "group": "server" + }, + "MODULE LOAD": { + "summary": "Load a module", + "complexity": "O(1)", + "arguments": [ + { + "name": "path", + "type": "string" + }, + { + "name": "arg", + "type": "string", + "variadic": true, + "optional": true + } + ], + "since": "4.0.0", + "group": "server" + }, + "MODULE UNLOAD": { + "summary": "Unload a module", + "complexity": "O(1)", + "arguments": [ + { + "name": "name", + "type": "string" + } + ], + "since": "4.0.0", + "group": "server" + }, + "MONITOR": { + "summary": "Listen for all requests received by the server in real time", + "since": "1.0.0", + "group": "server" + }, + "MOVE": { + "summary": "Move a key to another database", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "db", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "generic" + }, + "MSET": { + "summary": "Set multiple keys to multiple values", + "complexity": "O(N) where N is the number of keys to set.", + "arguments": [ + { + "name": [ + "key", + "value" + ], + "type": [ + "key", + "string" + ], + "multiple": true + } + ], + "since": "1.0.1", + "group": "string" + }, + "MSETNX": { + "summary": "Set multiple keys to multiple values, only if none of the keys exist", + "complexity": "O(N) where N is the number of keys to set.", + "arguments": [ + { + "name": [ + "key", + "value" + ], + "type": [ + "key", + "string" + ], + "multiple": true + } + ], + "since": "1.0.1", + "group": "string" + }, + "MULTI": { + "summary": "Mark the start of a transaction block", + "since": "1.2.0", + "group": "transactions" + }, + "OBJECT": { + "summary": "Inspect the internals of Redis objects", + "complexity": "O(1) for all the currently implemented subcommands.", + "since": "2.2.3", + "group": "generic", + "arguments": [ + { + "name": "subcommand", + "type": "string" + }, + { + "name": "arguments", + "type": "string", + "optional": true, + "multiple": true + } + ] + }, + "PERSIST": { + "summary": "Remove the expiration from a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.2.0", + "group": "generic" + }, + "PEXPIRE": { + "summary": "Set a key's time to live in milliseconds", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "milliseconds", + "type": "integer" + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX", + "GT", + "LT" + ], + "optional": true + } + ], + "since": "2.6.0", + "group": "generic" + }, + "PEXPIREAT": { + "summary": "Set the expiration for a key as a UNIX timestamp specified in milliseconds", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "milliseconds-timestamp", + "type": "posix time" + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX", + "GT", + "LT" + ], + "optional": true + } + ], + "since": "2.6.0", + "group": "generic" + }, + "PEXPIRETIME": { + "summary": "Get the expiration Unix timestamp for a key in milliseconds", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "7.0.0", + "group": "generic" + }, + "PFADD": { + "summary": "Adds the specified elements to the specified HyperLogLog.", + "complexity": "O(1) to add every element.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "element", + "type": "string", + "optional": true, + "multiple": true + } + ], + "since": "2.8.9", + "group": "hyperloglog" + }, + "PFCOUNT": { + "summary": "Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).", + "complexity": "O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "2.8.9", + "group": "hyperloglog" + }, + "PFMERGE": { + "summary": "Merge N different HyperLogLogs into a single one.", + "complexity": "O(N) to merge N HyperLogLogs, but with high constant times.", + "arguments": [ + { + "name": "destkey", + "type": "key" + }, + { + "name": "sourcekey", + "type": "key", + "multiple": true + } + ], + "since": "2.8.9", + "group": "hyperloglog" + }, + "PING": { + "summary": "Ping the server", + "arguments": [ + { + "name": "message", + "type": "string", + "optional": true + } + ], + "since": "1.0.0", + "group": "connection" + }, + "PSETEX": { + "summary": "Set the value and expiration in milliseconds of a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "milliseconds", + "type": "integer" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "2.6.0", + "group": "string" + }, + "PSUBSCRIBE": { + "summary": "Listen for messages published to channels matching the given patterns", + "complexity": "O(N) where N is the number of patterns the client is already subscribed to.", + "arguments": [ + { + "name": [ + "pattern" + ], + "type": [ + "pattern" + ], + "multiple": true + } + ], + "since": "2.0.0", + "group": "pubsub" + }, + "PUBSUB": { + "summary": "Inspect the state of the Pub/Sub subsystem", + "complexity": "O(N) for the CHANNELS subcommand, where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns). O(N) for the NUMSUB subcommand, where N is the number of requested channels. O(1) for the NUMPAT subcommand.", + "arguments": [ + { + "name": "subcommand", + "type": "string" + }, + { + "name": "argument", + "type": "string", + "optional": true, + "multiple": true + } + ], + "since": "2.8.0", + "group": "pubsub" + }, + "PTTL": { + "summary": "Get the time to live for a key in milliseconds", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.6.0", + "group": "generic" + }, + "PUBLISH": { + "summary": "Post a message to a channel", + "complexity": "O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).", + "arguments": [ + { + "name": "channel", + "type": "string" + }, + { + "name": "message", + "type": "string" + } + ], + "since": "2.0.0", + "group": "pubsub" + }, + "PUNSUBSCRIBE": { + "summary": "Stop listening for messages posted to channels matching the given patterns", + "complexity": "O(N+M) where N is the number of patterns the client is already subscribed and M is the number of total patterns subscribed in the system (by any client).", + "arguments": [ + { + "name": "pattern", + "type": "pattern", + "optional": true, + "multiple": true + } + ], + "since": "2.0.0", + "group": "pubsub" + }, + "QUIT": { + "summary": "Close the connection", + "since": "1.0.0", + "group": "connection" + }, + "RANDOMKEY": { + "summary": "Return a random key from the keyspace", + "complexity": "O(1)", + "since": "1.0.0", + "group": "generic" + }, + "READONLY": { + "summary": "Enables read queries for a connection to a cluster replica node", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "READWRITE": { + "summary": "Disables read queries for a connection to a cluster replica node", + "complexity": "O(1)", + "since": "3.0.0", + "group": "cluster" + }, + "RENAME": { + "summary": "Rename a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "newkey", + "type": "key" + } + ], + "since": "1.0.0", + "group": "generic" + }, + "RENAMENX": { + "summary": "Rename a key, only if the new key does not exist", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "newkey", + "type": "key" + } + ], + "since": "1.0.0", + "group": "generic" + }, + "RESET": { + "summary": "Reset the connection", + "since": "6.2", + "group": "connection" + }, + "RESTORE": { + "summary": "Create a key using the provided serialized value, previously obtained using DUMP.", + "complexity": "O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "ttl", + "type": "integer" + }, + { + "name": "serialized-value", + "type": "string" + }, + { + "name": "replace", + "type": "enum", + "enum": [ + "REPLACE" + ], + "optional": true + }, + { + "name": "absttl", + "type": "enum", + "enum": [ + "ABSTTL" + ], + "optional": true + }, + { + "command": "IDLETIME", + "name": "seconds", + "type": "integer", + "optional": true + }, + { + "command": "FREQ", + "name": "frequency", + "type": "integer", + "optional": true + } + ], + "since": "2.6.0", + "group": "generic" + }, + "ROLE": { + "summary": "Return the role of the instance in the context of replication", + "since": "2.8.12", + "group": "server" + }, + "RPOP": { + "summary": "Remove and get the last elements in a list", + "complexity": "O(N) where N is the number of elements returned", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "1.0.0", + "group": "list" + }, + "RPOPLPUSH": { + "summary": "Remove the last element in a list, prepend it to another list and return it", + "complexity": "O(1)", + "arguments": [ + { + "name": "source", + "type": "key" + }, + { + "name": "destination", + "type": "key" + } + ], + "since": "1.2.0", + "group": "list" + }, + "LMOVE": { + "summary": "Pop an element from a list, push it to another list and return it", + "complexity": "O(1)", + "arguments": [ + { + "name": "source", + "type": "key" + }, + { + "name": "destination", + "type": "key" + }, + { + "name": "wherefrom", + "type": "enum", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + { + "name": "whereto", + "type": "enum", + "enum": [ + "LEFT", + "RIGHT" + ] + } + ], + "since": "6.2.0", + "group": "list" + }, + "RPUSH": { + "summary": "Append one or multiple elements to a list", + "complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "element", + "type": "string", + "multiple": true + } + ], + "since": "1.0.0", + "group": "list" + }, + "RPUSHX": { + "summary": "Append an element to a list, only if the list exists", + "complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "element", + "type": "string", + "multiple": true + } + ], + "since": "2.2.0", + "group": "list" + }, + "SADD": { + "summary": "Add one or more members to a set", + "complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SAVE": { + "summary": "Synchronously save the dataset to disk", + "since": "1.0.0", + "group": "server" + }, + "SCARD": { + "summary": "Get the number of members in a set", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "set" + }, + "SCRIPT DEBUG": { + "summary": "Set the debug mode for executed scripts.", + "complexity": "O(1)", + "arguments": [ + { + "name": "mode", + "type": "enum", + "enum": [ + "YES", + "SYNC", + "NO" + ] + } + ], + "since": "3.2.0", + "group": "scripting" + }, + "SCRIPT EXISTS": { + "summary": "Check existence of scripts in the script cache.", + "complexity": "O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).", + "arguments": [ + { + "name": "sha1", + "type": "string", + "multiple": true + } + ], + "since": "2.6.0", + "group": "scripting" + }, + "SCRIPT FLUSH": { + "summary": "Remove all the scripts from the script cache.", + "arguments": [ + { + "name": "async", + "type": "enum", + "enum": [ + "ASYNC", + "SYNC" + ], + "optional": true + } + ], + "complexity": "O(N) with N being the number of scripts in cache", + "since": "2.6.0", + "group": "scripting" + }, + "SCRIPT KILL": { + "summary": "Kill the script currently in execution.", + "complexity": "O(1)", + "since": "2.6.0", + "group": "scripting" + }, + "SCRIPT LOAD": { + "summary": "Load the specified Lua script into the script cache.", + "complexity": "O(N) with N being the length in bytes of the script body.", + "arguments": [ + { + "name": "script", + "type": "string" + } + ], + "since": "2.6.0", + "group": "scripting" + }, + "SDIFF": { + "summary": "Subtract multiple sets", + "complexity": "O(N) where N is the total number of elements in all given sets.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SDIFFSTORE": { + "summary": "Subtract multiple sets and store the resulting set in a key", + "complexity": "O(N) where N is the total number of elements in all given sets.", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SELECT": { + "summary": "Change the selected database for the current connection", + "arguments": [ + { + "name": "index", + "type": "integer" + } + ], + "since": "1.0.0", + "group": "connection" + }, + "SET": { + "summary": "Set the string value of a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "value", + "type": "string" + }, + { + "name": "expiration", + "type": "enum", + "enum": [ + "EX seconds", + "PX milliseconds", + "EXAT timestamp", + "PXAT milliseconds-timestamp", + "KEEPTTL" + ], + "optional": true + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX" + ], + "optional": true + }, + { + "name": "get", + "type": "enum", + "enum": [ + "GET" + ], + "optional": true + } + ], + "since": "1.0.0", + "group": "string" + }, + "SETBIT": { + "summary": "Sets or clears the bit at offset in the string value stored at key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "offset", + "type": "integer" + }, + { + "name": "value", + "type": "integer" + } + ], + "since": "2.2.0", + "group": "bitmap" + }, + "SETEX": { + "summary": "Set the value and expiration of a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "seconds", + "type": "integer" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "2.0.0", + "group": "string" + }, + "SETNX": { + "summary": "Set the value of a key, only if the key does not exist", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "1.0.0", + "group": "string" + }, + "SETRANGE": { + "summary": "Overwrite part of a string at key starting at the specified offset", + "complexity": "O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "offset", + "type": "integer" + }, + { + "name": "value", + "type": "string" + } + ], + "since": "2.2.0", + "group": "string" + }, + "SHUTDOWN": { + "summary": "Synchronously save the dataset to disk and then shut down the server", + "arguments": [ + { + "name": "save-mode", + "type": "enum", + "enum": [ + "NOSAVE", + "SAVE" + ], + "optional": true + } + ], + "since": "1.0.0", + "group": "server" + }, + "SINTER": { + "summary": "Intersect multiple sets", + "complexity": "O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SINTERCARD": { + "summary": "Intersect multiple sets and return the cardinality of the result", + "complexity": "O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "7.0.0", + "group": "set" + }, + "SINTERSTORE": { + "summary": "Intersect multiple sets and store the resulting set in a key", + "complexity": "O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SISMEMBER": { + "summary": "Determine if a given value is a member of a set", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string" + } + ], + "since": "1.0.0", + "group": "set" + }, + "SMISMEMBER": { + "summary": "Returns the membership associated with the given elements for a set", + "complexity": "O(N) where N is the number of elements being checked for membership", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "6.2.0", + "group": "set" + }, + "SLAVEOF": { + "summary": "Make the server a replica of another instance, or promote it as master. Deprecated starting with Redis 5. Use REPLICAOF instead.", + "arguments": [ + { + "name": "host", + "type": "string" + }, + { + "name": "port", + "type": "string" + } + ], + "since": "1.0.0", + "group": "server" + }, + "REPLICAOF": { + "summary": "Make the server a replica of another instance, or promote it as master.", + "arguments": [ + { + "name": "host", + "type": "string" + }, + { + "name": "port", + "type": "string" + } + ], + "since": "5.0.0", + "group": "server" + }, + "SLOWLOG": { + "summary": "Manages the Redis slow queries log", + "arguments": [ + { + "name": "subcommand", + "type": "string" + }, + { + "name": "argument", + "type": "string", + "optional": true + } + ], + "since": "2.2.12", + "group": "server" + }, + "SMEMBERS": { + "summary": "Get all the members in a set", + "complexity": "O(N) where N is the set cardinality.", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "set" + }, + "SMOVE": { + "summary": "Move a member from one set to another", + "complexity": "O(1)", + "arguments": [ + { + "name": "source", + "type": "key" + }, + { + "name": "destination", + "type": "key" + }, + { + "name": "member", + "type": "string" + } + ], + "since": "1.0.0", + "group": "set" + }, + "SORT": { + "summary": "Sort the elements in a list, set or sorted set", + "complexity": "O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "command": "BY", + "name": "pattern", + "type": "pattern", + "optional": true + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + }, + { + "command": "GET", + "name": "pattern", + "type": "string", + "optional": true, + "multiple": true + }, + { + "name": "order", + "type": "enum", + "enum": [ + "ASC", + "DESC" + ], + "optional": true + }, + { + "name": "sorting", + "type": "enum", + "enum": [ + "ALPHA" + ], + "optional": true + }, + { + "command": "STORE", + "name": "destination", + "type": "key", + "optional": true + } + ], + "since": "1.0.0", + "group": "generic" + }, + "SORT_RO": { + "summary": "Sort the elements in a list, set or sorted set. Read-only variant of SORT.", + "complexity": "O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "command": "BY", + "name": "pattern", + "type": "pattern", + "optional": true + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + }, + { + "command": "GET", + "name": "pattern", + "type": "string", + "optional": true, + "multiple": true + }, + { + "name": "order", + "type": "enum", + "enum": [ + "ASC", + "DESC" + ], + "optional": true + }, + { + "name": "sorting", + "type": "enum", + "enum": [ + "ALPHA" + ], + "optional": true + } + ], + "since": "7.0.0", + "group": "generic" + }, + "SPOP": { + "summary": "Remove and return one or multiple random members from a set", + "complexity": "Without the count argument O(1), otherwise O(N) where N is the value of the passed count.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SRANDMEMBER": { + "summary": "Get one or multiple random members from a set", + "complexity": "Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SREM": { + "summary": "Remove one or more members from a set", + "complexity": "O(N) where N is the number of members to be removed.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "STRALGO": { + "summary": "Run algorithms (currently LCS) against strings", + "complexity": "For LCS O(strlen(s1)*strlen(s2))", + "arguments": [ + { + "name": "algorithm", + "type": "enum", + "enum": [ + "LCS" + ] + }, + { + "name": "algo-specific-argument", + "type": "string", + "multiple": true + } + ], + "since": "6.0.0", + "group": "string" + }, + "STRLEN": { + "summary": "Get the length of the value stored in a key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "2.2.0", + "group": "string" + }, + "SUBSCRIBE": { + "summary": "Listen for messages published to the given channels", + "complexity": "O(N) where N is the number of channels to subscribe to.", + "arguments": [ + { + "name": "channel", + "type": "string", + "multiple": true + } + ], + "since": "2.0.0", + "group": "pubsub" + }, + "SUNION": { + "summary": "Add multiple sets", + "complexity": "O(N) where N is the total number of elements in all given sets.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SUNIONSTORE": { + "summary": "Add multiple sets and store the resulting set in a key", + "complexity": "O(N) where N is the total number of elements in all given sets.", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "1.0.0", + "group": "set" + }, + "SWAPDB": { + "summary": "Swaps two Redis databases", + "complexity": "O(N) where N is the count of clients watching or blocking on keys from both databases.", + "arguments": [ + { + "name": "index1", + "type": "integer" + }, + { + "name": "index2", + "type": "integer" + } + ], + "since": "4.0.0", + "group": "server" + }, + "SYNC": { + "summary": "Internal command used for replication", + "since": "1.0.0", + "group": "server" + }, + "PSYNC": { + "summary": "Internal command used for replication", + "arguments": [ + { + "name": "replicationid", + "type": "integer" + }, + { + "name": "offset", + "type": "integer" + } + ], + "since": "2.8.0", + "group": "server" + }, + "TIME": { + "summary": "Return the current server time", + "complexity": "O(1)", + "since": "2.6.0", + "group": "server" + }, + "TOUCH": { + "summary": "Alters the last access time of a key(s). Returns the number of existing keys specified.", + "complexity": "O(N) where N is the number of keys that will be touched.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "3.2.1", + "group": "generic" + }, + "TTL": { + "summary": "Get the time to live for a key in seconds", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "generic" + }, + "TYPE": { + "summary": "Determine the type stored at key", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.0.0", + "group": "generic" + }, + "UNSUBSCRIBE": { + "summary": "Stop listening for messages posted to the given channels", + "complexity": "O(N) where N is the number of clients already subscribed to a channel.", + "arguments": [ + { + "name": "channel", + "type": "string", + "optional": true, + "multiple": true + } + ], + "since": "2.0.0", + "group": "pubsub" + }, + "UNLINK": { + "summary": "Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.", + "complexity": "O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "4.0.0", + "group": "generic" + }, + "UNWATCH": { + "summary": "Forget about all watched keys", + "complexity": "O(1)", + "since": "2.2.0", + "group": "transactions" + }, + "WAIT": { + "summary": "Wait for the synchronous replication of all the write commands sent in the context of the current connection", + "complexity": "O(1)", + "arguments": [ + { + "name": "numreplicas", + "type": "integer" + }, + { + "name": "timeout", + "type": "integer" + } + ], + "since": "3.0.0", + "group": "generic" + }, + "WATCH": { + "summary": "Watch the given keys to determine execution of the MULTI/EXEC block", + "complexity": "O(1) for every key.", + "arguments": [ + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "2.2.0", + "group": "transactions" + }, + "ZADD": { + "summary": "Add one or more members to a sorted set, or update its score if it already exists", + "complexity": "O(log(N)) for each item added, where N is the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "condition", + "type": "enum", + "enum": [ + "NX", + "XX" + ], + "optional": true + }, + { + "name": "comparison", + "type": "enum", + "enum": [ + "GT", + "LT" + ], + "optional": true + }, + { + "name": "change", + "type": "enum", + "enum": [ + "CH" + ], + "optional": true + }, + { + "name": "increment", + "type": "enum", + "enum": [ + "INCR" + ], + "optional": true + }, + { + "name": [ + "score", + "member" + ], + "type": [ + "double", + "string" + ], + "multiple": true + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZCARD": { + "summary": "Get the number of members in a sorted set", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZCOUNT": { + "summary": "Count the members in a sorted set with scores within the given values", + "complexity": "O(log(N)) with N being the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "double" + }, + { + "name": "max", + "type": "double" + } + ], + "since": "2.0.0", + "group": "sorted_set" + }, + "ZDIFF": { + "summary": "Subtract multiple sorted sets", + "complexity": "O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.", + "arguments": [ + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZDIFFSTORE": { + "summary": "Subtract multiple sorted sets and store the resulting sorted set in a new key", + "complexity": "O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZINCRBY": { + "summary": "Increment the score of a member in a sorted set", + "complexity": "O(log(N)) where N is the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "increment", + "type": "integer" + }, + { + "name": "member", + "type": "string" + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZINTER": { + "summary": "Intersect multiple sorted sets", + "complexity": "O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.", + "arguments": [ + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "command": "WEIGHTS", + "name": "weight", + "type": "integer", + "variadic": true, + "optional": true + }, + { + "command": "AGGREGATE", + "name": "aggregate", + "type": "enum", + "enum": [ + "SUM", + "MIN", + "MAX" + ], + "optional": true + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZINTERCARD": { + "summary": "Intersect multiple sorted sets and return the cardinality of the result", + "complexity": "O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.", + "arguments": [ + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + } + ], + "since": "7.0.0", + "group": "sorted_set" + }, + "ZINTERSTORE": { + "summary": "Intersect multiple sorted sets and store the resulting sorted set in a new key", + "complexity": "O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "command": "WEIGHTS", + "name": "weight", + "type": "integer", + "variadic": true, + "optional": true + }, + { + "command": "AGGREGATE", + "name": "aggregate", + "type": "enum", + "enum": [ + "SUM", + "MIN", + "MAX" + ], + "optional": true + } + ], + "since": "2.0.0", + "group": "sorted_set" + }, + "ZLEXCOUNT": { + "summary": "Count the number of members in a sorted set between a given lexicographical range", + "complexity": "O(log(N)) with N being the number of elements in the sorted set.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "string" + }, + { + "name": "max", + "type": "string" + } + ], + "since": "2.8.9", + "group": "sorted_set" + }, + "ZPOPMAX": { + "summary": "Remove and return members with the highest scores in a sorted set", + "complexity": "O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "5.0.0", + "group": "sorted_set" + }, + "ZPOPMIN": { + "summary": "Remove and return members with the lowest scores in a sorted set", + "complexity": "O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "5.0.0", + "group": "sorted_set" + }, + "ZRANDMEMBER": { + "summary": "Get one or multiple random elements from a sorted set", + "complexity": "O(N) where N is the number of elements returned", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "options", + "type": "block", + "block": [ + { + "name": "count", + "type": "integer" + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + } + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZRANGESTORE": { + "summary": "Store a range of members from sorted set into another key", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.", + "arguments": [ + { + "name": "dst", + "type": "key" + }, + { + "name": "src", + "type": "key" + }, + { + "name": "min", + "type": "string" + }, + { + "name": "max", + "type": "string" + }, + { + "name": "sortby", + "type": "enum", + "enum": [ + "BYSCORE", + "BYLEX" + ], + "optional": true + }, + { + "name": "rev", + "type": "enum", + "enum": [ + "REV" + ], + "optional": true + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZRANGE": { + "summary": "Return a range of members in a sorted set", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "string" + }, + { + "name": "max", + "type": "string" + }, + { + "name": "sortby", + "type": "enum", + "enum": [ + "BYSCORE", + "BYLEX" + ], + "optional": true + }, + { + "name": "rev", + "type": "enum", + "enum": [ + "REV" + ], + "optional": true + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZRANGEBYLEX": { + "summary": "Return a range of members in a sorted set, by lexicographical range", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "string" + }, + { + "name": "max", + "type": "string" + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + } + ], + "since": "2.8.9", + "group": "sorted_set" + }, + "ZREVRANGEBYLEX": { + "summary": "Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "max", + "type": "string" + }, + { + "name": "min", + "type": "string" + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + } + ], + "since": "2.8.9", + "group": "sorted_set" + }, + "ZRANGEBYSCORE": { + "summary": "Return a range of members in a sorted set, by score", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "double" + }, + { + "name": "max", + "type": "double" + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + } + ], + "since": "1.0.5", + "group": "sorted_set" + }, + "ZRANK": { + "summary": "Determine the index of a member in a sorted set", + "complexity": "O(log(N))", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string" + } + ], + "since": "2.0.0", + "group": "sorted_set" + }, + "ZREM": { + "summary": "Remove one or more members from a sorted set", + "complexity": "O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZREMRANGEBYLEX": { + "summary": "Remove all members in a sorted set between the given lexicographical range", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "string" + }, + { + "name": "max", + "type": "string" + } + ], + "since": "2.8.9", + "group": "sorted_set" + }, + "ZREMRANGEBYRANK": { + "summary": "Remove all members in a sorted set within the given indexes", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "start", + "type": "integer" + }, + { + "name": "stop", + "type": "integer" + } + ], + "since": "2.0.0", + "group": "sorted_set" + }, + "ZREMRANGEBYSCORE": { + "summary": "Remove all members in a sorted set within the given scores", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "min", + "type": "double" + }, + { + "name": "max", + "type": "double" + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZREVRANGE": { + "summary": "Return a range of members in a sorted set, by index, with scores ordered from high to low", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "start", + "type": "integer" + }, + { + "name": "stop", + "type": "integer" + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZREVRANGEBYSCORE": { + "summary": "Return a range of members in a sorted set, by score, with scores ordered from high to low", + "complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "max", + "type": "double" + }, + { + "name": "min", + "type": "double" + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + }, + { + "command": "LIMIT", + "name": [ + "offset", + "count" + ], + "type": [ + "integer", + "integer" + ], + "optional": true + } + ], + "since": "2.2.0", + "group": "sorted_set" + }, + "ZREVRANK": { + "summary": "Determine the index of a member in a sorted set, with scores ordered from high to low", + "complexity": "O(log(N))", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string" + } + ], + "since": "2.0.0", + "group": "sorted_set" + }, + "ZSCORE": { + "summary": "Get the score associated with the given member in a sorted set", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string" + } + ], + "since": "1.2.0", + "group": "sorted_set" + }, + "ZUNION": { + "summary": "Add multiple sorted sets", + "complexity": "O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.", + "arguments": [ + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "command": "WEIGHTS", + "name": "weight", + "type": "integer", + "variadic": true, + "optional": true + }, + { + "command": "AGGREGATE", + "name": "aggregate", + "type": "enum", + "enum": [ + "SUM", + "MIN", + "MAX" + ], + "optional": true + }, + { + "name": "withscores", + "type": "enum", + "enum": [ + "WITHSCORES" + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZMSCORE": { + "summary": "Get the score associated with the given members in a sorted set", + "complexity": "O(N) where N is the number of members being requested.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "member", + "type": "string", + "multiple": true + } + ], + "since": "6.2.0", + "group": "sorted_set" + }, + "ZUNIONSTORE": { + "summary": "Add multiple sorted sets and store the resulting sorted set in a new key", + "complexity": "O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.", + "arguments": [ + { + "name": "destination", + "type": "key" + }, + { + "name": "numkeys", + "type": "integer" + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "command": "WEIGHTS", + "name": "weight", + "type": "integer", + "variadic": true, + "optional": true + }, + { + "command": "AGGREGATE", + "name": "aggregate", + "type": "enum", + "enum": [ + "SUM", + "MIN", + "MAX" + ], + "optional": true + } + ], + "since": "2.0.0", + "group": "sorted_set" + }, + "SCAN": { + "summary": "Incrementally iterate the keys space", + "complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.", + "arguments": [ + { + "name": "cursor", + "type": "integer" + }, + { + "command": "MATCH", + "name": "pattern", + "type": "pattern", + "optional": true + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + }, + { + "command": "TYPE", + "name": "type", + "type": "string", + "optional": true + } + ], + "since": "2.8.0", + "group": "generic" + }, + "SSCAN": { + "summary": "Incrementally iterate Set elements", + "complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection..", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "cursor", + "type": "integer" + }, + { + "command": "MATCH", + "name": "pattern", + "type": "pattern", + "optional": true + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "2.8.0", + "group": "set" + }, + "HSCAN": { + "summary": "Incrementally iterate hash fields and associated values", + "complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection..", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "cursor", + "type": "integer" + }, + { + "command": "MATCH", + "name": "pattern", + "type": "pattern", + "optional": true + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "2.8.0", + "group": "hash" + }, + "ZSCAN": { + "summary": "Incrementally iterate sorted sets elements and associated scores", + "complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection..", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "cursor", + "type": "integer" + }, + { + "command": "MATCH", + "name": "pattern", + "type": "pattern", + "optional": true + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "2.8.0", + "group": "sorted_set" + }, + "XINFO": { + "summary": "Get information on streams and consumer groups", + "complexity": "O(N) with N being the number of returned items for the subcommands CONSUMERS and GROUPS. The STREAM subcommand is O(log N) with N being the number of items in the stream.", + "arguments": [ + { + "command": "CONSUMERS", + "name": [ + "key", + "groupname" + ], + "type": [ + "key", + "string" + ], + "optional": true + }, + { + "command": "GROUPS", + "name": "key", + "type": "key", + "optional": true + }, + { + "command": "STREAM", + "name": "key", + "type": "key", + "optional": true + }, + { + "name": "help", + "type": "enum", + "enum": [ + "HELP" + ], + "optional": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XADD": { + "summary": "Appends a new entry to a stream", + "complexity": "O(1) when adding a new entry, O(N) when trimming where N being the number of entires evicted.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "command": "NOMKSTREAM", + "optional": true + }, + { + "name": "trim", + "type": "block", + "optional": true, + "block": [ + { + "name": "strategy", + "type": "enum", + "enum": [ + "MAXLEN", + "MINID" + ] + }, + { + "name": "operator", + "type": "enum", + "enum": [ + "=", + "~" + ], + "optional": true + }, + { + "name": "threshold", + "type": "string" + }, + { + "command": "LIMIT", + "name": "count", + "type": "integer", + "optional": true + } + ] + }, + { + "type": "enum", + "enum": [ + "*", + "ID" + ] + }, + { + "name": [ + "field", + "value" + ], + "type": [ + "string", + "string" + ], + "multiple": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XTRIM": { + "summary": "Trims the stream to (approximately if '~' is passed) a certain size", + "complexity": "O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "trim", + "type": "block", + "block": [ + { + "name": "strategy", + "type": "enum", + "enum": [ + "MAXLEN", + "MINID" + ] + }, + { + "name": "operator", + "type": "enum", + "enum": [ + "=", + "~" + ], + "optional": true + }, + { + "name": "threshold", + "type": "string" + }, + { + "command": "LIMIT", + "name": "count", + "type": "integer", + "optional": true + } + ] + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XDEL": { + "summary": "Removes the specified entries from the stream. Returns the number of items actually deleted, that may be different from the number of IDs passed in case certain IDs do not exist.", + "complexity": "O(1) for each single item to delete in the stream, regardless of the stream size.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "ID", + "type": "string", + "multiple": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XRANGE": { + "summary": "Return a range of elements in a stream, with IDs matching the specified IDs interval", + "complexity": "O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "start", + "type": "string" + }, + { + "name": "end", + "type": "string" + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XREVRANGE": { + "summary": "Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE", + "complexity": "O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "end", + "type": "string" + }, + { + "name": "start", + "type": "string" + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XLEN": { + "summary": "Return the number of entries in a stream", + "complexity": "O(1)", + "arguments": [ + { + "name": "key", + "type": "key" + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XREAD": { + "summary": "Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.", + "complexity": "For each stream mentioned: O(N) with N being the number of elements being returned, it means that XREAD-ing with a fixed COUNT is O(1). Note that when the BLOCK option is used, XADD will pay O(M) time in order to serve the M clients blocked on the stream getting new data.", + "arguments": [ + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + }, + { + "command": "BLOCK", + "name": "milliseconds", + "type": "integer", + "optional": true + }, + { + "name": "streams", + "type": "enum", + "enum": [ + "STREAMS" + ] + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "ID", + "type": "string", + "multiple": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XGROUP": { + "summary": "Create, destroy, and manage consumer groups.", + "complexity": "O(1) for all the subcommands, with the exception of the DESTROY subcommand which takes an additional O(M) time in order to delete the M entries inside the consumer group pending entries list (PEL).", + "arguments": [ + { + "name": "create", + "type": "block", + "block": [ + { + "command": "CREATE", + "name": [ + "key", + "groupname" + ], + "type": [ + "key", + "string" + ] + }, + { + "name": "id", + "type": "enum", + "enum": [ + "ID", + "$" + ] + }, + { + "command": "MKSTREAM", + "optional": true + } + ], + "optional": true + }, + { + "name": "setid", + "type": "block", + "block": [ + { + "command": "SETID", + "name": [ + "key", + "groupname" + ], + "type": [ + "key", + "string" + ] + }, + { + "name": "id", + "type": "enum", + "enum": [ + "ID", + "$" + ] + } + ], + "optional": true + }, + { + "command": "DESTROY", + "name": [ + "key", + "groupname" + ], + "type": [ + "key", + "string" + ], + "optional": true + }, + { + "command": "CREATECONSUMER", + "name": [ + "key", + "groupname", + "consumername" + ], + "type": [ + "key", + "string", + "string" + ], + "optional": true + }, + { + "command": "DELCONSUMER", + "name": [ + "key", + "groupname", + "consumername" + ], + "type": [ + "key", + "string", + "string" + ], + "optional": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XREADGROUP": { + "summary": "Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.", + "complexity": "For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.", + "arguments": [ + { + "command": "GROUP", + "name": [ + "group", + "consumer" + ], + "type": [ + "string", + "string" + ] + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + }, + { + "command": "BLOCK", + "name": "milliseconds", + "type": "integer", + "optional": true + }, + { + "name": "noack", + "type": "enum", + "enum": [ + "NOACK" + ], + "optional": true + }, + { + "name": "streams", + "type": "enum", + "enum": [ + "STREAMS" + ] + }, + { + "name": "key", + "type": "key", + "multiple": true + }, + { + "name": "ID", + "type": "string", + "multiple": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XACK": { + "summary": "Marks a pending message as correctly processed, effectively removing it from the pending entries list of the consumer group. Return value of the command is the number of messages successfully acknowledged, that is, the IDs we were actually able to resolve in the PEL.", + "complexity": "O(1) for each message ID processed.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "group", + "type": "string" + }, + { + "name": "ID", + "type": "string", + "multiple": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XCLAIM": { + "summary": "Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.", + "complexity": "O(log N) with N being the number of messages in the PEL of the consumer group.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "group", + "type": "string" + }, + { + "name": "consumer", + "type": "string" + }, + { + "name": "min-idle-time", + "type": "string" + }, + { + "name": "ID", + "type": "string", + "multiple": true + }, + { + "command": "IDLE", + "name": "ms", + "type": "integer", + "optional": true + }, + { + "command": "TIME", + "name": "ms-unix-time", + "type": "integer", + "optional": true + }, + { + "command": "RETRYCOUNT", + "name": "count", + "type": "integer", + "optional": true + }, + { + "name": "force", + "enum": [ + "FORCE" + ], + "optional": true + }, + { + "name": "justid", + "enum": [ + "JUSTID" + ], + "optional": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "XAUTOCLAIM": { + "summary": "Changes (or acquires) ownership of messages in a consumer group, as if the messages were delivered to the specified consumer.", + "complexity": "O(1) if COUNT is small.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "group", + "type": "string" + }, + { + "name": "consumer", + "type": "string" + }, + { + "name": "min-idle-time", + "type": "string" + }, + { + "name": "start", + "type": "string" + }, + { + "command": "COUNT", + "name": "count", + "type": "integer", + "optional": true + }, + { + "name": "justid", + "enum": [ + "JUSTID" + ], + "optional": true + } + ], + "since": "6.2.0", + "group": "stream" + }, + "XPENDING": { + "summary": "Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.", + "complexity": "O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.", + "arguments": [ + { + "name": "key", + "type": "key" + }, + { + "name": "group", + "type": "string" + }, + { + "type": "block", + "name": "filters", + "block": [ + { + "command": "IDLE", + "name": "min-idle-time", + "type": "integer", + "optional": true + }, + { + "name": "start", + "type": "string" + }, + { + "name": "end", + "type": "string" + }, + { + "name": "count", + "type": "integer" + }, + { + "name": "consumer", + "type": "string", + "optional": true + } + ], + "optional": true + } + ], + "since": "5.0.0", + "group": "stream" + }, + "LATENCY DOCTOR": { + "summary": "Return a human readable latency analysis report.", + "since": "2.8.13", + "group": "server" + }, + "LATENCY GRAPH": { + "summary": "Return a latency graph for the event.", + "arguments": [ + { + "name": "event", + "type": "string" + } + ], + "since": "2.8.13", + "group": "server" + }, + "LATENCY HISTORY": { + "summary": "Return timestamp-latency samples for the event.", + "arguments": [ + { + "name": "event", + "type": "string" + } + ], + "since": "2.8.13", + "group": "server" + }, + "LATENCY LATEST": { + "summary": "Return the latest latency samples for all events.", + "since": "2.8.13", + "group": "server" + }, + "LATENCY RESET": { + "summary": "Reset latency data for one or more events.", + "arguments": [ + { + "name": "event", + "type": "string", + "optional": true, + "multiple": true + } + ], + "since": "2.8.13", + "group": "server" + }, + "LATENCY HELP": { + "summary": "Show helpful text about the different subcommands.", + "since": "2.8.13", + "group": "server" + } +} diff --git a/scripts/extract_redis_commands_argcounts.php b/scripts/extract_redis_commands_argcounts.php index fbeeeeec..f22d414e 100755 --- a/scripts/extract_redis_commands_argcounts.php +++ b/scripts/extract_redis_commands_argcounts.php @@ -7,17 +7,24 @@ * they'd fall under for twemproxy's request parsing logic. */ -if (count($argv) !== 2) { - echo "Usage: ${argv[0]} commands.json\n"; - echo "commands.json can be downloaded from https://github.com/redis/redis-doc\n"; - exit(1); +if ($argc < 2){ + $contents = file_get_contents('https://raw.githubusercontent.com/redis/redis-doc/master/commands.json'); + file_put_contents('commands.json', $contents); } -$path = $argv[1]; -$contents = file_get_contents($path); -if (!is_string($contents)) { - echo "Failed to read $path\n"; - exit(1); +else { + if (count($argv) !== 2) { + echo "Usage: ${argv[0]} commands.json\n"; + echo "commands.json can be downloaded from https://github.com/redis/redis-doc\n"; + exit(1); + } + $path = $argv[1]; + $contents = file_get_contents($path); + if (!is_string($contents)) { + echo "Failed to read $path\n"; + exit(1); + } } + $commands = json_decode($contents, true); uasort($commands, fn($a, $b) => version_compare($b['since'], $a['since'])); diff --git a/scripts/redis-check.sh b/scripts/redis-check.sh index a6241dff..0692ccec 100755 --- a/scripts/redis-check.sh +++ b/scripts/redis-check.sh @@ -1,622 +1,639 @@ #!/bin/sh -port=6379 -port=22121 +port=7379 -debug="-v -d" -debug="-d" +#debug="-v -d" +#debug="-d" timeout="-t 1" -timeout="" +#timeout="" + +# ping + +printf '\nping\n' +printf '*2\r\n$4\r\nping\r\n$11\r\nhello world\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} + # keys printf '\ndel\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\ndel\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nbar\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$3\r\ndel\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$6\r\nfoobar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\ndel\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nbar\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$3\r\ndel\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$6\r\nfoobar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$3\r\ndel\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$6\r\nfoobar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ndump\n' -printf '*2\r\n$4\r\ndump\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\ndump\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\ndump\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\ndump\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nexists\n' -printf '*2\r\n$6\r\nexists\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$6\r\nexists\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$6\r\nexists\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$6\r\nexists\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nexpire\n' -printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\npersist\n' -printf '*2\r\n$7\r\npersist\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$2\r\n10\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\npersist\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\npersist\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$7\r\npersist\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$2\r\n10\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\npersist\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\npersist\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nexpireat\n' -printf '*3\r\n$8\r\nexpireat\r\n$3\r\nfoo\r\n$10\r\n1282463464\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$8\r\nexpireat\r\n$3\r\nfoo\r\n$10\r\n1282463464\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$8\r\nexpireat\r\n$3\r\nfoo\r\n$10\r\n1282463464\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$8\r\nexpireat\r\n$3\r\nfoo\r\n$10\r\n1282463464\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nexpire\n' -printf '*3\r\n$7\r\npexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$7\r\npexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$7\r\npexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$7\r\npexpire\r\n$3\r\nfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nrestore\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$7\r\nrestore\r\n$3\r\nfoo\r\n$1\r\n0\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$7\r\nrestore\r\n$3\r\nfoo\r\n$1\r\n0\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\npttl\n' -printf '*2\r\n$4\r\npttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$7\r\npexpire\r\n$3\r\nfoo\r\n$7\r\n1000000\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\npttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\npttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$7\r\npexpire\r\n$3\r\nfoo\r\n$7\r\n1000000\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\npttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nttl\n' -printf '*2\r\n$4\r\npttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$2\r\n10\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\npttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nexpire\r\n$3\r\nfoo\r\n$2\r\n10\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nttl\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ntype\n' -printf '*2\r\n$4\r\ntype\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\ntype\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\ntype\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\ntype\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # strings printf '\nappend\n' -printf '*3\r\n$6\r\nappend\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nappend\r\n$3\r\n999\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$6\r\nappend\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nappend\r\n$3\r\n999\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nbitcount\n' -printf '*2\r\n$8\r\nbitcount\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$8\r\nbitcount\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$8\r\nbitcount\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$8\r\nbitcount\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ndecr\n' -printf '*2\r\n$4\r\ndecr\r\n$7\r\ncounter\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\ndecr\r\n$7\r\ncounter\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ndecrby\n' -printf '*3\r\n$6\r\ndecrby\r\n$7\r\ncounter\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$6\r\ndecrby\r\n$7\r\ncounter\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nget\n' -printf '*2\r\n$3\r\nget\r\n$16\r\nnon-existent-key\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\nget\r\n$16\r\nnon-existent-key\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ngetbit\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\ngetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\ngetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\ngetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\ngetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ngetrange\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$8\r\ngetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$8\r\ngetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$8\r\ngetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$8\r\ngetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$8\r\ngetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$8\r\ngetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\ngetset\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\ngetset\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\ngetset\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nincr\n' -printf '*2\r\n$3\r\ndel\r\n$7\r\ncounter\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nincr\r\n$7\r\ncounter\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$7\r\ncounter\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nincr\r\n$7\r\ncounter\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nincrby\n' -printf '*3\r\n$6\r\nincrby\r\n$7\r\ncounter\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$6\r\nincrby\r\n$7\r\ncounter\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nincrbyfloat\n' -printf '*3\r\n$11\r\nincrbyfloat\r\n$7\r\ncounter\r\n$5\r\n10.10\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$11\r\nincrbyfloat\r\n$7\r\ncounter\r\n$5\r\n10.10\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nmget\n' -printf '*2\r\n$4\r\nmget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nmget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nmget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*13\r\n$4\r\nmget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\nmget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nmget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nmget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*13\r\n$4\r\nmget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\npsetex\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\npsetex\r\n$3\r\nfoo\r\n$4\r\n1000\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\npsetex\r\n$3\r\nfoo\r\n$4\r\n1000\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nset\n' -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsetbit\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nsetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\n000\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nsetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nsetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\n000\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nsetbit\r\n$3\r\nfoo\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\npsetex\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nsetex\r\n$3\r\nfoo\r\n$4\r\n1000\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nsetex\r\n$3\r\nfoo\r\n$4\r\n1000\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsetnx\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nsetnx\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nsetnx\r\n$3\r\nfoo\r\n$3\r\nooo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nsetnx\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nsetnx\r\n$3\r\nfoo\r\n$3\r\nooo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsetrange\n' -printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$8\r\nsetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$8\r\nsetrange\r\n$3\r\nfoo\r\n$1\r\n4\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$8\r\nsetrange\r\n$3\r\nfoo\r\n$1\r\n1\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$8\r\nsetrange\r\n$3\r\nfoo\r\n$1\r\n4\r\n$3\r\noof\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # hashes printf '\nhdel\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhexists\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$7\r\nhexists\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$7\r\nhexists\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$7\r\nhexists\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$7\r\nhexists\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhget\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nhget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nhget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhdel\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nhget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nhget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhgetall\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhincrby\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$7\r\nhincrby\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$7\r\nhincrby\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhincrbyfloat\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$12\r\nhincrbyfloat\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$6\r\n100.12\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$12\r\nhincrbyfloat\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$6\r\n100.12\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhkeys\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nhkeys\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nhkeys\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nhkeys\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nhkeys\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nhkeys\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nhkeys\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhlen\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nhlen\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nhlen\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nhlen\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nhlen\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhmget\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nhmget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nhmget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$6\r\n1dleif\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nhmget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$6\r\n1dleif\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nhmget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nhmget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$6\r\n1dleif\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\n1dleif\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nhmget\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$6\r\n1dleif\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhmset\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$5\r\nhmset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n$6\r\nfield2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$5\r\nhmset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n$6\r\nfield2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhset\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nhset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhsetnx\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nhsetnx\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nhsetnx\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nhsetnx\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nhsetnx\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\nhgetall\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nhvals\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nhvals\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$5\r\nhmset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n$6\r\nfield2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nhvals\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nhvals\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$5\r\nhmset\r\n$4\r\nhfoo\r\n$6\r\nfield1\r\n$3\r\nbar\r\n$6\r\nfield2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nhvals\r\n$4\r\nhfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # lists printf '\nlindex\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nlindex\r\n$4\r\nlfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nlindex\r\n$4\r\nlfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nlindex\r\n$4\r\nlfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nlindex\r\n$4\r\nlfoo\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlinsert\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$7\r\nlinsert\r\n$4\r\nlfoo\r\n$6\r\nBEFORE\r\n$3\r\nbar\r\n$3\r\nbaq\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nlindex\r\n$4\r\nlfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$7\r\nlinsert\r\n$4\r\nlfoo\r\n$6\r\nBEFORE\r\n$3\r\nbar\r\n$3\r\nbaq\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nlindex\r\n$4\r\nlfoo\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nllen\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nllen\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nllen\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nllen\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nllen\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlpop\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nlpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlpush\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlpushx\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nlpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nlpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nlpushx\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nlpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nlpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nlpushx\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlrange\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n$3\r\nbat\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n$3\r\nbat\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlrem\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nlrem\r\n$4\r\nlfoo\r\n$1\r\n2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nlrem\r\n$4\r\nlfoo\r\n$1\r\n2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nlrem\r\n$4\r\nlfoo\r\n$1\r\n2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nlrem\r\n$4\r\nlfoo\r\n$1\r\n2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbar\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nlrem\r\n$4\r\nlfoo\r\n$1\r\n2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nlrem\r\n$4\r\nlfoo\r\n$1\r\n2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nlset\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nlset\r\n$4\r\nlfoo\r\n$1\r\n1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nlset\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$3\r\nbaq\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nlset\r\n$4\r\nlfoo\r\n$1\r\n1\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nlset\r\n$4\r\nlfoo\r\n$1\r\n1\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nlset\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$3\r\nbaq\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nlset\r\n$4\r\nlfoo\r\n$1\r\n1\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nltrim\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nltrim\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nltrim\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nrpop\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nlpush\r\n$4\r\nlfoo\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nrpop\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nrpoplpush\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{lfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{lfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$5\r\nlpush\r\n$6\r\n{lfoo}\r\n$3\r\nbar\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$9\r\nrpoplpush\r\n$6\r\n{lfoo}\r\n$7\r\n{lfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$9\r\nrpoplpush\r\n$6\r\n{lfoo}\r\n$7\r\n{lfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$6\r\n{lfoo}\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$7\r\n{lfoo}2\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{lfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{lfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$5\r\nlpush\r\n$6\r\n{lfoo}\r\n$3\r\nbar\r\n$3\r\nbaq\r\n$3\r\nbap\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$9\r\nrpoplpush\r\n$6\r\n{lfoo}\r\n$7\r\n{lfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$9\r\nrpoplpush\r\n$6\r\n{lfoo}\r\n$7\r\n{lfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$6\r\n{lfoo}\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$7\r\n{lfoo}2\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nrpush\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbat\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbat\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nrpushx\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nrpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nrpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nrpushx\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nlfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nrpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nrpush\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nrpushx\r\n$4\r\nlfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nrpushx\r\n$4\r\nlfoo\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nlrange\r\n$4\r\nlfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # sets printf '\nsadd\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nscard\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nscard\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nscard\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nscard\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nscard\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsdiff\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nsdiff\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nsdiff\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsdiffstore\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$10\r\nsdiffstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$10\r\nsdiffstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsinter\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nsinter\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nsinter\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsinterstore\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$11\r\nsinterstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$11\r\nsinterstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsismember\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsmembers\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsmove\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\nsmove\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\nsmove\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nspop\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nspop\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$4\r\nspop\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nspop\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$4\r\nspop\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsrandmember\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsrem\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsrem\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n$3\r\nbat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsrem\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$4\r\nsrem\r\n$4\r\nsfoo\r\n$3\r\nbas\r\n$3\r\nbat\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nsfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsrem\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n$3\r\nbat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsrem\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$4\r\nsrem\r\n$4\r\nsfoo\r\n$3\r\nbas\r\n$3\r\nbat\r\n$3\r\nrab\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsunion\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nsunion\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nsunion\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nsunionstore\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$11\r\nsunionstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{sfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nsadd\r\n$6\r\n{sfoo}\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$11\r\nsunionstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$7\r\n{sfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # sorted sets printf '\nzadd\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzcard\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nzcard\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$5\r\nzcard\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nzcard\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$5\r\nzcard\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzcount\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nzcount\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nzcount\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nzcount\r\n$4\r\nzfoo\r\n$4\r\n-inf\r\n$4\r\n+inf\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nzcount\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nzcount\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nzcount\r\n$4\r\nzfoo\r\n$4\r\n-inf\r\n$4\r\n+inf\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzincrby\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$7\r\nzincrby\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$7\r\nzincrby\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$7\r\nzincrby\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$7\r\nzincrby\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzinterstore\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{zfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$6\r\n{zfoo}\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$4\r\nzadd\r\n$7\r\n{zfoo}2\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$11\r\nzinterstore\r\n$7\r\n{zfoo}3\r\n$1\r\n2\r\n$6\r\n{zfoo}\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$6\r\nzrange\r\n$7\r\n{zfoo}3\r\n$1\r\n0\r\n$1\r\n3\r\n$10\r\nWITHSCORES\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{zfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$6\r\n{zfoo}\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$4\r\nzadd\r\n$7\r\n{zfoo}2\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$11\r\nzinterstore\r\n$7\r\n{zfoo}3\r\n$1\r\n2\r\n$6\r\n{zfoo}\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$6\r\nzrange\r\n$7\r\n{zfoo}3\r\n$1\r\n0\r\n$1\r\n3\r\n$10\r\nWITHSCORES\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrange\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nzrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$6\r\nzrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$6\r\nzrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n3\r\n$10\r\nWITHSCORES\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nzrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$6\r\nzrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$6\r\nzrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n3\r\n$10\r\nWITHSCORES\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrangebyscore\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$13\r\nzrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$13\r\nzrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$13\r\nzrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$13\r\nzrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrank\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nzrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\nzrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nzrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\nzrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrem\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\nzrem\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzrem\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\nzrem\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzrem\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzremrangebyrank\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$15\r\nzremrangebyrank\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$15\r\nzremrangebyrank\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$15\r\nzremrangebyrank\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$15\r\nzremrangebyrank\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzremrangebyscore\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$16\r\nzremrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$16\r\nzremrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$16\r\nzremrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$16\r\nzremrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\n101\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrevrange\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$9\r\nzrevrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$9\r\nzrevrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$9\r\nzrevrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$9\r\nzrevrange\r\n$4\r\nzfoo\r\n$1\r\n0\r\n$1\r\n2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrevrangebyscore\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$16\r\nzrevrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n101\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$16\r\nzrevrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n101\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$16\r\nzrevrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n101\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$16\r\nzrevrangebyscore\r\n$4\r\nzfoo\r\n$3\r\n101\r\n$3\r\n100\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrevrank\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$8\r\nzrevrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$8\r\nzrevrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$8\r\nzrevrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$8\r\nzrevrank\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzscore\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nzscore\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$6\r\nzscore\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nzscore\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nzscore\r\n$4\r\nzfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzunionstore\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{zfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$6\r\n{zfoo}\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*6\r\n$4\r\nzadd\r\n$7\r\n{zfoo}2\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$11\r\nzunionstore\r\n$7\r\n{zfoo}3\r\n$1\r\n2\r\n$6\r\n{zfoo}\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$6\r\nzrange\r\n$7\r\n{zfoo}3\r\n$1\r\n0\r\n$1\r\n3\r\n$10\r\nWITHSCORES\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{zfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{zfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$6\r\n{zfoo}\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*6\r\n$4\r\nzadd\r\n$7\r\n{zfoo}2\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$11\r\nzunionstore\r\n$7\r\n{zfoo}3\r\n$1\r\n2\r\n$6\r\n{zfoo}\r\n$7\r\n{zfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$6\r\nzrange\r\n$7\r\n{zfoo}3\r\n$1\r\n0\r\n$1\r\n3\r\n$10\r\nWITHSCORES\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzlexcount\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$9\r\nzlexcount\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$9\r\nzlexcount\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$4\r\n[bat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$9\r\nzlexcount\r\n$4\r\nzfoo\r\n$1\r\n-\r\n$1\r\n+\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$9\r\nzlexcount\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$9\r\nzlexcount\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$4\r\n[bat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$9\r\nzlexcount\r\n$4\r\nzfoo\r\n$1\r\n-\r\n$1\r\n+\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzrangebylex\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$11\r\nzrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$11\r\nzrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$4\r\n[bat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$11\r\nzrangebylex\r\n$4\r\nzfoo\r\n$1\r\n-\r\n$1\r\n+\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$11\r\nzrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$11\r\nzrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$4\r\n[bat\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$11\r\nzrangebylex\r\n$4\r\nzfoo\r\n$1\r\n-\r\n$1\r\n+\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\nzremrangebylex\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$14\r\nzremrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$14\r\nzremrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$14\r\nzremrangebylex\r\n$4\r\nzfoo\r\n$1\r\n-\r\n$1\r\n+\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\nzfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$14\r\nzremrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*8\r\n$4\r\nzadd\r\n$4\r\nzfoo\r\n$3\r\n100\r\n$3\r\nbar\r\n$3\r\n101\r\n$3\r\nbat\r\n$3\r\n102\r\n$3\r\nbau\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$14\r\nzremrangebylex\r\n$4\r\nzfoo\r\n$2\r\n(a\r\n$2\r\n(z\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$14\r\nzremrangebylex\r\n$4\r\nzfoo\r\n$1\r\n-\r\n$1\r\n+\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # hyperloglog printf '\npfadd\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\npfadd\r\n$4\r\npfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\npfcount\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\npfadd\r\n$4\r\npfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\npfcount\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\npfcount\n' -printf '*2\r\n$3\r\ndel\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\npfcount\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\npfadd\r\n$4\r\npfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\npfcount\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\npfcount\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\npfadd\r\n$4\r\npfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\npfcount\r\n$4\r\npfoo\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} printf '\npfmerge\n' -printf '*2\r\n$3\r\ndel\r\n$6\r\n{pfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{pfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$3\r\ndel\r\n$7\r\n{pfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$5\r\npfadd\r\n$6\r\n{pfoo}\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$5\r\npfadd\r\n$7\r\n{pfoo}2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*5\r\n$7\r\npfmerge\r\n$7\r\n{pfoo}3\r\n$1\r\n2\r\n$6\r\n{pfoo}\r\n$7\r\n{pfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*2\r\n$7\r\npfcount\r\n$7\r\n{pfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$3\r\ndel\r\n$6\r\n{pfoo}\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{pfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$3\r\ndel\r\n$7\r\n{pfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$5\r\npfadd\r\n$6\r\n{pfoo}\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$5\r\npfadd\r\n$7\r\n{pfoo}2\r\n$3\r\nbas\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$7\r\npfmerge\r\n$7\r\n{pfoo}3\r\n$1\r\n2\r\n$6\r\n{pfoo}\r\n$7\r\n{pfoo}2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$7\r\npfcount\r\n$7\r\n{pfoo}3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # scripting printf '\neval\n' -printf '*2\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*3\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*7\r\n$4\r\neval\r\n$40\r\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}\r\n$1\r\n2\r\n$9\r\nkey1{tag}\r\n$4\r\narg1\r\n$9\r\nkey2{tag}\r\n$4\r\narg2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*9\r\n$4\r\neval\r\n$56\r\nreturn {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}\r\n$1\r\n3\r\n$9\r\nkey1{tag}\r\n$4\r\narg1\r\n$9\r\nkey2{tag}\r\n$4\r\narg2\r\n$9\r\nkey3{tag}\r\n$4\r\narg3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close -printf '*4\r\n$4\r\neval\r\n$11\r\nreturn {10}\r\n$1\r\n1\r\n$4\r\nTEMP\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*2\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n$1\r\n0\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\neval\r\n$10\r\nreturn 123\r\n$1\r\n1\r\n$1\r\n1\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*7\r\n$4\r\neval\r\n$40\r\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}\r\n$1\r\n2\r\n$9\r\nkey1{tag}\r\n$4\r\narg1\r\n$9\r\nkey2{tag}\r\n$4\r\narg2\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*9\r\n$4\r\neval\r\n$56\r\nreturn {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}\r\n$1\r\n3\r\n$9\r\nkey1{tag}\r\n$4\r\narg1\r\n$9\r\nkey2{tag}\r\n$4\r\narg2\r\n$9\r\nkey3{tag}\r\n$4\r\narg3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*4\r\n$4\r\neval\r\n$11\r\nreturn {10}\r\n$1\r\n1\r\n$4\r\nTEMP\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} + +printf '\nsaddint\n' +printf '*2\r\n$3\r\ndel\r\n$4\r\nsint\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*5\r\n$7\r\nsaddint\r\n$4\r\nsint\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*2\r\n$8\r\nsmembers\r\n$4\r\nsint\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} + +printf '\nobject\n' +printf '*3\r\n$6\r\nobject\r\n$8\r\nencoding\r\n$4\r\nsint\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nobject\r\n$8\r\nrefcount\r\n$4\r\nsint\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nobject\r\n$8\r\nidletime\r\n$4\r\nsint\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} +printf '*3\r\n$6\r\nobject\r\n$4\r\nfreq\r\n$4\r\nsint\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} # quit printf '\nquit\n' -printf '*1\r\n$4\r\nquit\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port},shut-close +printf '*1\r\n$4\r\nquit\r\n' | socat ${debug} ${timeout} - TCP:localhost:${port} diff --git a/src/Makefile.am b/src/Makefile.am index dd07a25a..b0fda81b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,11 @@ MAINTAINERCLEANFILES = Makefile.in +BUILT_SOURCES = cfg-options.h +CLEANFILES = cfg-options.h + +cfg-options.h: Makefile + $(AM_V_GEN)echo '#define NC_CONF_PATH "$(sysconfdir)/nutcracker/nutcracker.yml"' >$@ + @echo '#define NC_LOG_PATH "$(localstatedir)/log/nutcracker/nutcracker.log"' >>$@ + @echo '#define NC_PID_FILE "/run/nutcracker/nutcracker.pid"' >>$@ AM_CPPFLAGS = if !OS_SOLARIS @@ -7,7 +14,6 @@ endif AM_CPPFLAGS += -I $(top_srcdir)/src/hashkit AM_CPPFLAGS += -I $(top_srcdir)/src/proto AM_CPPFLAGS += -I $(top_srcdir)/src/event -AM_CPPFLAGS += -I $(top_srcdir)/contrib/yaml-0.2.5/include AM_CFLAGS = # about -fno-strict-aliasing: https://github.com/twitter/twemproxy/issues/276 @@ -15,14 +21,15 @@ AM_CFLAGS += -fno-strict-aliasing AM_CFLAGS += -Wall -Wshadow AM_CFLAGS += -Wpointer-arith AM_CFLAGS += -Winline -AM_CFLAGS += -Wunused-function -Wunused-variable -Wunused-value +#AM_CFLAGS += -Wunused-function -Wunused-variable -Wunused-value AM_CFLAGS += -Wno-unused-parameter -Wno-unused-value AM_CFLAGS += -Wconversion -Wsign-compare AM_CFLAGS += -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations AM_CFLAGS += -Wno-format-zero-length +AM_CFLAGS += -Wno-unused-but-set-variable -Wno-sign-conversion AM_LDFLAGS = -AM_LDFLAGS += -lm -lpthread -rdynamic +AM_LDFLAGS += -lm -lpthread -rdynamic -lyaml if OS_SOLARIS AM_LDFLAGS += -lnsl -lsocket endif @@ -58,7 +65,6 @@ nutcracker_SOURCES = \ nutcracker_LDADD = $(top_builddir)/src/hashkit/libhashkit.a nutcracker_LDADD += $(top_builddir)/src/proto/libproto.a nutcracker_LDADD += $(top_builddir)/src/event/libevent.a -nutcracker_LDADD += $(top_builddir)/contrib/yaml-0.2.5/src/.libs/libyaml.a TESTS = test_all bin_PROGRAMS = test_all @@ -86,4 +92,3 @@ test_all_SOURCES = test_all.c \ test_all_LDADD = $(top_builddir)/src/hashkit/libhashkit.a test_all_LDADD += $(top_builddir)/src/proto/libproto.a test_all_LDADD += $(top_builddir)/src/event/libevent.a -test_all_LDADD += $(top_builddir)/contrib/yaml-0.2.5/src/.libs/libyaml.a diff --git a/src/nc.c b/src/nc.c index 0d606bad..79163d98 100644 --- a/src/nc.c +++ b/src/nc.c @@ -28,7 +28,7 @@ #include #include -#define NC_CONF_PATH "conf/nutcracker.yml" +#include "cfg-options.h" #define NC_LOG_DEFAULT LOG_NOTICE #define NC_LOG_MIN LOG_EMERG @@ -39,8 +39,6 @@ #define NC_STATS_ADDR STATS_ADDR #define NC_STATS_INTERVAL STATS_INTERVAL -#define NC_PID_FILE NULL - #define NC_MBUF_SIZE MBUF_SIZE #define NC_MBUF_MIN_SIZE MBUF_MIN_SIZE #define NC_MBUF_MAX_SIZE MBUF_MAX_SIZE @@ -298,7 +296,7 @@ nc_set_default_options(struct instance *nci) nci->mbuf_chunk_size = NC_MBUF_SIZE; nci->pid = (pid_t)-1; - nci->pid_filename = NULL; + nci->pid_filename = NC_PID_FILE; nci->pidfile = 0; } @@ -488,7 +486,7 @@ nc_pre_run(struct instance *nci) return status; } - if (nci->pid_filename) { + if (daemonize && nci->pid_filename) { status = nc_create_pidfile(nci); if (status != NC_OK) { return status; diff --git a/src/nc_message.h b/src/nc_message.h index 26a063bc..5f67fb93 100644 --- a/src/nc_message.h +++ b/src/nc_message.h @@ -79,6 +79,7 @@ typedef enum msg_parse_result { ACTION( REQ_REDIS_TTL ) \ ACTION( REQ_REDIS_TYPE ) \ ACTION( REQ_REDIS_UNLINK ) \ + ACTION( REQ_REDIS_OBJECT ) \ ACTION( REQ_REDIS_APPEND ) /* redis requests - string */ \ ACTION( REQ_REDIS_BITCOUNT ) \ ACTION( REQ_REDIS_BITFIELD ) \ @@ -141,6 +142,7 @@ typedef enum msg_parse_result { ACTION( REQ_REDIS_RPUSH ) \ ACTION( REQ_REDIS_RPUSHX ) \ ACTION( REQ_REDIS_SADD ) /* redis requests - sets */ \ + ACTION( REQ_REDIS_SADDINT ) \ ACTION( REQ_REDIS_SCARD ) \ ACTION( REQ_REDIS_SDIFF ) \ ACTION( REQ_REDIS_SDIFFSTORE ) \ diff --git a/src/nc_string.c b/src/nc_string.c index dffaeafd..fac80b67 100644 --- a/src/nc_string.c +++ b/src/nc_string.c @@ -61,7 +61,7 @@ string_empty(const struct string *str) { ASSERT((str->len == 0 && str->data == NULL) || (str->len != 0 && str->data != NULL)); - return str->len == 0; + return str->len == 0 ? true : false; } rstatus_t diff --git a/src/nc_util.c b/src/nc_util.c index a8842958..1e8eaeae 100644 --- a/src/nc_util.c +++ b/src/nc_util.c @@ -645,3 +645,14 @@ nc_unresolve_desc(int sd) return nc_unresolve_addr(addr, addrlen); } + +struct timespec +nc_millisec_to_timespec (int n_millisec) +{ + struct timeval tv = {n_millisec/1000LL, (n_millisec%1000LL)*1000LL}; + struct timespec ts; + + TIMEVAL_TO_TIMESPEC(&tv, &ts); + + return ts; +} diff --git a/src/nc_util.h b/src/nc_util.h index da13aedc..ebe45b11 100644 --- a/src/nc_util.h +++ b/src/nc_util.h @@ -204,6 +204,7 @@ int _scnprintf(char *buf, size_t size, const char *fmt, ...) NC_ATTRIBUTE_FORMAT int _vscnprintf(char *buf, size_t size, const char *fmt, va_list args); int64_t nc_usec_now(void); int64_t nc_msec_now(void); +struct timespec nc_millisec_to_timespec(int millisec); /* * Address resolution for internet (ipv4 and ipv6) and unix domain diff --git a/src/proto/nc_redis.c b/src/proto/nc_redis.c index a5ea210f..7855ebb4 100644 --- a/src/proto/nc_redis.c +++ b/src/proto/nc_redis.c @@ -132,6 +132,8 @@ redis_arg1(const struct msg *r) case MSG_REQ_REDIS_ZRANK: case MSG_REQ_REDIS_ZREVRANK: case MSG_REQ_REDIS_ZSCORE: + + case MSG_REQ_REDIS_OBJECT: return true; default: @@ -236,6 +238,7 @@ redis_argn(const struct msg *r) case MSG_REQ_REDIS_LPOS: case MSG_REQ_REDIS_SADD: + case MSG_REQ_REDIS_SADDINT: case MSG_REQ_REDIS_SDIFF: case MSG_REQ_REDIS_SDIFFSTORE: case MSG_REQ_REDIS_SINTER: @@ -971,6 +974,11 @@ redis_parse_req(struct msg *r) break; } + if (str6icmp(m, 'o', 'b', 'j', 'e', 'c', 't')) { + r->type = MSG_REQ_REDIS_OBJECT; + break; + } + if (str6icmp(m, 'g', 'e', 'o', 'p', 'o', 's')) { r->type = MSG_REQ_REDIS_GEOPOS; break; @@ -1105,6 +1113,11 @@ redis_parse_req(struct msg *r) break; } + if (str7icmp(m, 's', 'a', 'd', 'd', 'i', 'n', 't')) { + r->type = MSG_REQ_REDIS_SADDINT; + break; + } + break; case 8: diff --git a/src/test_all.c b/src/test_all.c index c22ec7c6..af3fd51a 100644 --- a/src/test_all.c +++ b/src/test_all.c @@ -184,6 +184,11 @@ static void test_redis_parse_req_success(void) { test_redis_parse_req_success_case("*3\r\n$4\r\nsadd\r\n$7\r\n{sfoo}2\r\n$3\r\nbar\r\n", MSG_REQ_REDIS_SADD); test_redis_parse_req_success_case("*4\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n", MSG_REQ_REDIS_SADD); test_redis_parse_req_success_case("*5\r\n$4\r\nsadd\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n$3\r\nbas\r\n$3\r\nbat\r\n", MSG_REQ_REDIS_SADD); + + test_redis_parse_req_success_case("*3\r\n$7\r\nsaddint\r\n$7\r\n{sint}2\r\n$1\r\n1\r\n", MSG_REQ_REDIS_SADDINT); + test_redis_parse_req_success_case("*4\r\n$7\r\nsaddint\r\n$4\r\nsint\r\n$1\r\n2\r\n$2\r\n11\r\n", MSG_REQ_REDIS_SADDINT); + test_redis_parse_req_success_case("*5\r\n$7\r\nsaddint\r\n$4\r\nsint\r\n$1\r\n3\r\n$2\r\n12\r\n$3\r\n333\r\n", MSG_REQ_REDIS_SADDINT); + test_redis_parse_req_success_case("*2\r\n$5\r\nscard\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_SCARD); test_redis_parse_req_success_case("*3\r\n$5\r\nsdiff\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n", MSG_REQ_REDIS_SDIFF); test_redis_parse_req_success_case("*4\r\n$10\r\nsdiffstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n", MSG_REQ_REDIS_SDIFFSTORE); @@ -196,6 +201,17 @@ static void test_redis_parse_req_success(void) { test_redis_parse_req_success_case("*3\r\n$6\r\nsinter\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n", MSG_REQ_REDIS_SINTER); test_redis_parse_req_success_case("*4\r\n$11\r\nsinterstore\r\n$7\r\n{sfoo}3\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n", MSG_REQ_REDIS_SINTERSTORE); test_redis_parse_req_success_case("*3\r\n$9\r\nsismember\r\n$4\r\nsfoo\r\n$3\r\nbar\r\n", MSG_REQ_REDIS_SISMEMBER); + + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$8\r\nencoding\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$8\r\nrefcount\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$8\r\nidletime\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$4\r\nfreq\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_OBJECT); + + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$8\r\nencoding\r\n$4\r\nsint\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$8\r\nrefcount\r\n$4\r\nsint\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$8\r\nidletime\r\n$4\r\nsint\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*3\r\n$6\r\nobject\r\n$4\r\nfreq\r\n$4\r\nsint\r\n", MSG_REQ_REDIS_OBJECT); + test_redis_parse_req_success_case("*2\r\n$8\r\nsmembers\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_SMEMBERS); test_redis_parse_req_success_case("*4\r\n$5\r\nsmove\r\n$6\r\n{sfoo}\r\n$7\r\n{sfoo}2\r\n$3\r\nbas\r\n", MSG_REQ_REDIS_SMOVE); test_redis_parse_req_success_case("*2\r\n$11\r\nsrandmember\r\n$4\r\nsfoo\r\n", MSG_REQ_REDIS_SRANDMEMBER); diff --git a/travis.sh b/travis.sh new file mode 100644 index 00000000..4c9803f6 --- /dev/null +++ b/travis.sh @@ -0,0 +1,27 @@ +#!/bin/bash +#file : travis.sh +#author : ning +#date : 2014-05-10 16:54:43 + +#install deps if we are in travis +if [ -n "$TRAVIS" ]; then + sudo apt-get install socat + + #python libs + sudo pip install redis + sudo pip install nose + + sudo pip install git+https://github.com/andymccurdy/redis-py.git@3.5.3 + sudo pip install git+https://github.com/idning/python-memcached.git#egg=memcache +fi + +#build twemproxy +CFLAGS="-ggdb3 -O0" autoreconf -fvi && ./configure --enable-debug=log && make + +ln -s `pwd`/src/nutcracker tests/_binaries/ +cp `which redis-server` tests/_binaries/ +cp `which redis-cli` tests/_binaries/ +cp `which memcached` tests/_binaries/ + +#run test +cd tests/ && nosetests --nologcapture -x -v