The rampart-redis module¶
Preface¶
License¶
The rampart-redis module is released under the MIT license.
What does it do?¶
The rampart-redis module provides a client interface from Rampart to an existing Redis server.
How does it work?¶
The rampart-redis module connects to an existing Redis server and allows commands to be sent, and responses to be retrieved from Redis. For most operations, commands and replies are synchronous, meaning that the module waits for a return value from Redis and provides it to the script before moving on. However, any command may be optionally run asynchronously. If so, the command will be sent immediately to the server with the reply from the server handled in Rampart’s event loop.
Note: Unlike Redis modules in other frameworks (e.g., Node), a separate connection is maintained for synchronous and asynchronous requests from a single handle. However, only one asynchronous request can be active at a time per handle.
Loading and Using the Module¶
Loading¶
Loading the module is a simple matter of using the require()
function:
var redis = require("rampart-redis");
Main Function¶
The rampart-redis module exports a single constructor function init() which may be used to establish a connection with a redis server.
init()¶
Usage:
var redis = require("rampart-redis"); var rcl = new redis.init([ip, ][port, ][timeout]); /* or */ var rcl = new redis.init(options);Where:
options
is an Object with the optional propertiesip
,port
and/ortimeout
as described below.ip
is a String, the ip address of the Redis server. The default if not specified is127.0.0.1
.port
is a Number, the port number of the Redis server. The default if not specified is6379
.timeout
is a Number, the amount of time to wait for redis to reply to a synchronous request for data in seconds. The default is-1
, which means to wait forever.
- Return Value:
- A Object - the handle representing the connection to the Redis server as well as containing the functions/redis commands listed below.
Client Functions¶
The majority of the client functions provided by the rampart-redis
module are
one-to-one mappings of the Published Redis Commands. There are also specialized and shortcut
commands available.
Client Commands¶
The set of standard client commands includes Supported Commands and Unsupported Commands as listed below.
Usage:
var redis = require("rampart-redis"); var rcl = new redis.init([ip, ][port, ][timeout]); rcl.command_name([options ][, param1][, param2][, ...][, callback_function]);Where:
command_name
is the name of one of the functions listed below.
paramX
is one or more parameters, required or optional for the given Redis command.
callback_function
is an optional (if synchronous) or required (if asynchronous) function which takes afunction(res,err)
.
options
is an optional Object with the following optional properties:
timeout
- the Number of seconds to wait for a response from the redis server when using a synchronous command. It overrides the timeout set inredis.init
for the single command.async
- a Boolean, which iftrue
, the client will send the command to server and wait for reply in the Rampart event loop. Iftrue
, a callback function must be provided.returnBuffer
- a Boolean, which if true, Redisstrings
andbulk strings
will be returned in Buffers.
- Return Value:
undefined
if there is a callback.Null
if error. Otherwise, on success see return values below.- Errors:
- Error messages are written to
rcl.errMsg
. For calls with a callback function, it is also passed to the function as the second parameter.- Note:
All commands are by default synchronous commands, except for shortcut commands which end in
*_async
and for thesubscribe
/psubscribe
commands.Only one
async
command may be active at any given time. Additional concurrent asynchronous requests require that multiple handles be opened.The
rcl
handle opens separate connections for synchronous and asynchronous commands.
Shortcut Commands¶
The following commands do not map one-to-one to the Published Redis Commands.
_async Commands¶
blmove_async
,brpop_async
,bzpopmax_async
,bzpopmin_async
andformat_async
are equivalent to their non-async counterparts with theoptions
Object set to{async:true}
.
format¶
Send a formatted command. Return value is not specially reformatted (returns the same as format
2
below).Usage:
var redis = require("rampart-redis"); var rcl = new redis.init([ip, ][port, ][timeout]); rcl.format([options,] format_string [, param2][, ...][, callback_function]);Where:
options
is the same as above.
format_string
is a String with format codes corresponding to the parameters that follow (much in the same way as printf). However only a limited number of format codes are available:
%s
- the corresponding parameter is and will be send as a String%c
- the corresponding parameter is a String, the first character of which will be send as a single character.%i
- the corresponding parameter is a Number and will be converted and sent as an integer.%d
the corresponding parameter is a Number and will be sent as a double float.%b
the corresponding parameter is a Buffer or a String and will be sent as buffer data.
- Return Value:
- An Array, the return values from the server. Note that unlike commands below,
format
does not attempt to organize return values into Objects based on the command given. Instead the raw values returned from redis are each members of the Array.Example:
var redis = require("rampart-redis"); var rcl = new redis.init(); //localhost and default port rcl.format("HMSET %s %s %d %s %d", "myset", "one", 1, "two", 2); rampart.utils.printf("%3J\n", rcl.format("HGETALL myset") ); /* expected output (generic array) [ "one", 1, "two", 2 ] */ rampart.utils.printf("%3J\n", rcl.hgetall("myset")); /* expected output (formatted for the HGETALL command) { "one": 1, "two": 2 } */
xread_block_async¶
This is equivalent toxread({async:true}, "BLOCK", ...)
.
xread_auto_async¶
This is equivalent to
xread_block_async
above, except that the returned IDs are tracked and theXREAD BLOCK
command is reissued with the proper IDs each time a new Stream Message is received. In this way it acts more likesubscribe
and continually accepts new streamed messages similar to a PUB/SUB model.For more information, see the redis xread command and this section of the tutorials, as well as #12 in the return values section below.
Supported Commands¶
The following commands have been tested and return values formatted appropriately for the given command (number in parantheses is the format of the return value):
bitcount
→(1),bitfield
→(2),bitop
→(1),bitpos
→(1),blmove
→(1),blmove_async
→(1),blpop
→(13),blpop_async
→(13),brpop
→(13),brpop_async
→(13),brpoplpush
→(1),bzpopmax
→(14),bzpopmax_async
→(14),bzpopmin
→(14),bzpopmin_async
→(14),decr
→(1),decrby
→(1),del
→(1),discard
→(1),dump
→(1),echo
→(1),exists
→(6),expire
→(1),expireat
→(1),flushall
→(1),flushdb
→(1),format
→(2),format_async
→(2),get
→(1),getbit
→(1),getdel
→(1),getex
→(1),getrange
→(1),getset
→(1),hdel
→(1),hexists
→(6),hget
→(1),hgetall
→(4),hincrby
→(1),hincrbyfloat
→(1),hkeys
→(2),hlen
→(1),hmget
→(2),hmset
→(1),hrandfield
→(2),hscan
→(7),hset
→(1),hsetnx
→(6),hstrlen
→(1),hvals
→(2),incr
→(1),incrby
→(1),incrbyfloat
→(1),info
→(2),keys
→(2),lindex
→(1),linsert
→(1),llen
→(1),lmove
→(1),lpop
→(2),lpos
→(1),lpush
→(1),lpushx
→(1),lrange
→(2),lrem
→(1),lset
→(1),ltrim
→(1),mget
→(2),move
→(1),mset
→(1),msetnx
→(6),object
→(1),persist
→(1),pexpire
→(1),pexpireat
→(1),psetex
→(1),psubscribe
→(1),pttl
→(1),publish
→(1),pubsub
→(1),punsubscribe
→(1),rpop
→(2),rpoplpush
→(1),rpush
→(1),rpushx
→(1),sadd
→(1),save
→(1),scan
→(8),scard
→(1),sdiff
→(2),sdiffstore
→(1),set
→(1),setbit
→(1),setex
→(1),setnx
→(6),setrange
→(1),sinter
→(2),sinterstore
→(1),sismember
→(6),smembers
→(2),smismember
→(9),smove
→(1),sort
→(2),spop
→(2),srandmember
→(2),srem
→(1),sscan
→(8),stralgo
→(4),strlen
→(1),subscribe
→(3),sunion
→(2),sunionstore
→(1),time
→(2),touch
→(1),ttl
→(1),type
→(1),unlink
→(1),unsubscribe
→(1),xadd
→(1),xdel
→(1),xinfo
→(4),xlen
→(1),xrange
→(5),xread
→(12),xread_auto_async
→(12),xread_block_async
→(12),xrevrange
→(5),xtrim
→(1),zadd
→(1),zcard
→(1),zcount
→(1),zdiff
→(10),zdiffstore
→(10),zincrby
→(1),zinter
→(10),zinterstore
→(10),zlexcount
→(1),zmscore
→(2),zpopmax
→(10),zpopmin
→(10),zrandmember
→(10),zrange
→(10),zrangebylex
→(2),zrangebyscore
→(10),zrangestore
→(1),zrank
→(1),zrem
→(1),zremrangebylex
→(1),zremrangebyrank
→(1),zremrangebyscore
→(1),zrevrange
→(10),zrevrangebylex
→(10),zrevrangebyscore
→(10),zrevrank
→(1),zscan
→(11),zscore
→(1),zunion
→(10),zunionstore
→(10)
Return Values from Supported Commands¶
Single Value - return is a String, Buffer, Number or
null
.Multi-Value - return is an Array. When used with a callback, the callback will be called once for each member of the Array.
Multi-Value - return is an Array of Arrays. When used with a callback, the callback will be called once for each member of the outer Array and have its sole parameter one of each of the inner Arrays.
Associative - return is an Object with one or more key:value pairs.
Associative - for
xrange
andxrevrange
only. Format is as follow:[ { "id": "1620437646452-0", "value": { "key1": "val1", "key2": "val2" } }, { "id":"1620437646452-1", "value": { "key3": "val3" } } ]Boolean - return is a Boolean.
Associative - for
hscan
only. Format is as follows:{ "cursor": 0, "values": { "key1": 99, "key2": 100, "key3": 101 } }With a callback, the return value is set to cursor and the callback function is called for each key:value pair in its own Object.
Multi-Value - for
scan
andsscan
only. Format is as follows:{ "cursor": 0, "values": [ "one", "three", "four", "two" ] }With a callback, the return value is set to cursor and the callback function is called for each value.
Boolean - for
sismember
only. An Array of Booleans.Multi-Value - for
zset
commands which specifyWITHSCORES
. Values are returned in an object along with the score. With a callback, a single Object with value and score passed to the function. Example:[ { "value": "b", "score": 0 }, { "value": "c", "score": 1 }, ]If
WITHSCORES
is not specified, the format is the same as2
above.Multi-Value - for
zscan
only. Combination of10
and8
above. Example:{ "cursor": 0, "values": [ { "value": "b", "score": 0 }, { "value": "c", "score": 1 } ] }Associative - for
xread
commands only. Format is as follows:[ { "stream": "mystream1", "data": [ { "id": "1620441129127-1", "value": { "key1": "val1", "key2": "val2" } }, { "id": "1620441129127-2", "value": { "key3": "val3" } } ] }, { "stream": "mystream2", "data": [ { "id": "1620441129127-0", "value": { "key4": "val4", "key5": "val5" } } ] } ]With a callback function, one member of the outer Array is passed for each iteration of the callback.
Single Value - returns a value along with the Redis key name in an Object (e.g.
{key:"mylist",value:"three"}
).Single Value - for blocking
zset
commands. Same as13
above with the addition of a score (e.g.{key:"mylist",value:"three",score:1}
).
Unsupported Commands¶
The following functions will send the appropriate commands. However they have not (yet) been tested for the format of their return value:
acl_cat
,acl_deluser
,acl_genpass
,acl_getuser
,acl_help
,acl_list
,acl_load
,acl_log
,acl_save
,acl_setuser
,acl_users
,acl_whoami
,append
,auth
,bgrewriteaof
,bgsave
,client_caching
,client_getname
,client_getredir
,client_id
,client_kill
,client_list
,client_pause
,client_reply
,client_setname
,client_tracking
,client_unblock
,cluster_addslots
,cluster_bumpepoch
,cluster_count-failure-reports
,cluster_countkeysinslot
,cluster_delslots
,cluster_failover
,cluster_flushslots
,cluster_forget
,cluster_getkeysinslot
,cluster_info
,cluster_keyslot
,cluster_meet
,cluster_myid
,cluster_nodes
,cluster_replicas
,cluster_replicate
,cluster_reset
,cluster_saveconfig
,cluster_set-config-epoch
,cluster_setslot
,cluster_slaves
,cluster_slots
,command
,command_count
,command_getkeys
,command_info
,config_get
,config_resetstat
,config_rewrite
,config_set
,dbsize
,debug_object
,debug_segfault
,eval
,evalsha
,exec
,geoadd
,geodist
,geohash
,geopos
,georadius
,georadiusbymember
,hello
,lastsave
,latency_doctor
,latency_graph
,latency_help
,latency_history
,latency_latest
,latency_reset
,lolwut
,memory_doctor
,memory_help
,memory_malloc-stats
,memory_purge
,memory_stats
,memory_usage
,migrate
,module_list
,module_load
,module_unload
,monitor
,multi
,pfadd
,pfcount
,pfmerge
,ping
,psync
,psync
,quit
,randomkey
,readonly
,readwrite
,rename
,renamenx
,replicaof
,reset
,restore
,role
,script_debug
,script_exists
,script_flush
,script_kill
,script_load
,select
,shutdown
,slaveof
,slowlog
,swapdb
,swapdb
,sync
,sync
,unwatch
,wait
,watch
,xack
,xclaim
,xgroup
,xpending
andxreadgroup
If any of the above commands returns values in an unsuitable format, the format command above may be used to get a generic response.
Proxy Objects¶
A Rampart Redis Proxy Object is an JavaScript Object whose properties
are backed by a Redis Hash and for which operations
on the Object serves as a shortcut for the hset
, hget
, hgetall
and hdel
commands. They are intended
as a quick and easy storage system for occassionally used variables that are shared between
scripts or threads. They should not be used as a robust databasing solution.
Creation¶
A proxy object is created as follows:
var redis = require("rampart-redis"); var rcl = new redis.init([ip, ][port, ][timeout]); var myProxyObject = new rcl.proxyObj(name);Where
name
is the name of the Redis key that will store the Redis Hash.
Usage¶
Setting and reading the property keys of the returned proxy object will automatically set and retrieve the keys in the specified Hash Key. These values will then be available in any other thread or script which connects to the same Redis Server instance using the identical proxy object.
Example:
/* script 1 - insert new data */ var redis = require("rampart-redis"); var rcl = new redis.init(); //localhost and default port var users = new rcl.proxyObj("Users"); /* populate variables for insertion */ var cl = [ "principal", "principal", "salary", "salary", "hourly", "intern" ]; var name = [ "Debbie Dreamer", "Rusty Grump","Georgia Geek", "Sydney Slacker", "Pat Particular", "Billie Barista" ]; var id = [ "dd", "rg", "gg", "ss", "pp", "bb" ]; var age = [ 63, 58, 44, 44, 32, 22 ]; var salary = [ 250000, 250000, 100000, 100000, 80000, 0 ]; var title = [ "Chief Executive Officer", "Chief Financial Officer", "Lead Programmer", "Programmer", "Systems Administrator", "Intern" ]; var startDate = [ '1999-12-31', '1999-12-31', '2001-3-15', '2002-5-12', '2003-7-14', '2020-3-18' ]; /* insert rows */ for (var i=0; i<6; i++) { users[id] = { employeeClass: cl[i], name: name[i], age: age[i], salary: salary[i], title: title[i], startDate: startDate[i] } } /* end script 1 */ /* script 2 - access data */ var redis = require("rampart-redis"); var rcl = new redis.init(); //localhost and default port var users = new rcl.proxyObj("Users"); delete users.bb; //Billie's internship is finished. rampart.utils.printf("%3J\n", users); /* expected output: { "rg": { "employeeClass": "principal", "name": "Rusty Grump", "age": 58, "salary": 250000, "title": "Chief Financial Officer", "startDate": "1999-12-31" }, "ss": { "employeeClass": "salary", "name": "Sydney Slacker", "age": 44, "salary": 100000, "title": "Programmer", "startDate": "2002-5-12" }, "dd": { "employeeClass": "principal", "name": "Debbie Dreamer", "age": 63, "salary": 250000, "title": "Chief Executive Officer", "startDate": "1999-12-31" }, "pp": { "employeeClass": "hourly", "name": "Pat Particular", "age": 32, "salary": 80000, "title": "Systems Administrator", "startDate": "2003-7-14" }, "gg": { "employeeClass": "salary", "name": "Georgia Geek", "age": 44, "salary": 100000, "title": "Lead Programmer", "startDate": "2001-3-15" } } */
Caveats¶
JavaScript primitives and Objects are stored and retrieved by converting to and from CBOR which limits what can be stored (no Dates or Functions);
Proxy Objects are saved to the Redis Key specified when the object is created (i.e.
new rcl.proxyObj(redisKeyName)
). If the Redis Key already exists and is not a Redis Hash, an error will be thrown.Any Proxy Object key that begins with
_
(underscore) will not be saved to the Redis Hash and will only exist locally. The following are preset:
_destroy
- A Function which when called, deletes the Redis Hash from the database. Example:var rcl = new redis.init(); //localhost and default port var users = new rcl.proxyObj("Users"); users._destroy(); // same as rcl.del("Users");
_hname
- A String set to the name of the Redis Key. In the above example, it would be set to"Users"
;A Proxy Object is a shallow proxy - meaning that only key/value pairs of the Object returned from
new rcl.proxyObj()
will be stored to the Redis database. Example:var rcl = new redis.init(); //localhost and default port var users = new rcl.proxyObj("Users"); users.myid = {name:"Joe"}; //myid.name is stored in Redis users.myid.age = 42; // users.myid.age is not set; console.log(users.myid) // {name:"Joe"} var myid = users.myid; // var myid is created from the Redis Hash "myid" // which is CBOR decoded into a JavaScript Object myid.age = 42; users.myid = myid; // myid.age and myid.name are now stored in Redis console.log(users.myid) // {name:"Joe", age: 42}Operations which may otherwise appear to be atomic will not work properly with concurrency. Example:
users.count++
will indeed increment the variable stored under the Hash keycount
. However, it will retrieve the value, add one, then set it again, during which time another script may have retrieved the value to do the same. Thus, doing this simultaneously in several scripts or threads will not work as intended.