Noderis
A standalone Node.js client for Redis
Features
- No external dependencies, it uses only standard Node.js modules.
- Can be used with standard callback, Promises/A and async-await syntaxes.
- Support for pipelines and transactions with MULTI-EXEC (shortcut: pmulti).
- The callback based functions and pipelines are chainable.
- Pipeline results emmitted to the commands callbacks.
- MSET can accept JS objects as well.
- Connection pools to handle more commands at the same time. If all clients in the pool are busy, commands are waiting for a free client.
- Connection pool is transparent, it is just a proxy for the 1st available connection. So you even don't notice that you use a pool.
- Clear, understandable, not too complicated code. Redis commands are real functions, IDEs (like PHPStorm or Webstorm) can understand it.
- Methods and commands have a JSDoc comment with types specified.
- Good stack trace on redis error.
Maybe it's not as fast as a C++ based parser, but still very fast though. Official Node-redis has no connection pool feature, so only one command can be run at a time (or there would be collisions). So if you have 100 users at the same time, they need to wait for each other. Connecting to Redis each time a client is connected still not a good solution beacause of the overhead of connecting. Because of overcomplicated design of Node-redis, it is hard to implement a connection pool with it (and would be much slower). So this is why we implemeted the pool feature which I think may make Noderis faster than most clients.
Cons
It is string based API, so no support for binary data (which I don't need, I store all binary data in base64). Although it should not be hard to change it to use buffer instead of utf-8 strings. If you need it, please implement it and send a pull request.
Usage
Initialize
You can start using the module in several ways
Config by global variables
The easiest (IMO) is to create global config variables:
globalREDIS_HOST = 'redis-server'; // The host of Redis serverglobalREDIS_PORT = 6379; // The port of Redis serverglobalREDIS_OPTIONS = {}; // Other Redis options (see in source code)globalREDIS_POOL_SIZE = 10; // How many connections we need (5 is default)
This way we can use module level rclient and rclient_async objects to communicate with Redis. If global.REDIS_POOL_SIZE is specified, connection pool is created instead of a single RedisClient object
Calling createClient or createClientPool
;
By creating objects
let rclient = port host options;let rclient_async = rclient;rclient;
Calling commands
Callback based API
You can call any Redis commands with callRedis method like this:
rclient;
Though we created preprocessors/shortcuts for some (the goal is to create for all) commands:
rclient;
Promise / async-await API
The same in async way:
try let resp = await rclient_async; let resp = await rclient_async; catcherr ;
Events
connected
: Emitted when connection is ready. It is called once. If you use pool, emmitted when 1st client is connected.disconnected
: When the client is disconnected. In pool emitted if all clients are disconnected.connect_error
: When error occured while connecting, or connection timeout occured.error
: Errors in connection and communication with Redis server.redis_error
: Errors from Redis server and protocol errors.
ConnectionPool
All events are the same (because pool is transparent), but you can listen on the following client events if you want:
client_connected
: When a client is connected in the pool.client_disconnected
: When any client is disconnected in the pool.
Pipeline and (MULTI-EXEC) transaction
Creating pipeline is very easy, then you can chain every command:
// Callback basedrclient ;
At the end of the pipeline, you need to send it. It will send all commands at once to Redis, which is faster because of no send-receive overhead.
You can specify an index to the send() method, to get only one command's result. If it is negative we can get the result from bottom:
// ... ;
Async await based example:
let resp = await rclient_async ;console;
Transaction
Transactions in Redis implemented by calling MULTI, then at the end EXEC. Though it is not very useful without pipeline it can have problems as well:
- MULTI calls cannot be nested!
- If you start a MULTI transaction and other users start commands, it will be included in the same transaction until EXEC is called
- If you use clientPool, it is handled there, but that client will be marked busy until EXEC is called.
So we suggest to use our pmulti() method which starts a pipeline then put a MULTI as 1st command in it. It even closes the MULTI automatically with EXEC on send() (if not disabled in options).
Example:
let resp = await rclient_async ;
Our pipeline implementation uses the results of EXEC - if it is the 1st command in the pipeline -, not the whole pipeline, where every additional command returns with a "QUEUED" response. Instead we will get the real results.
Further goals
- Implement all Redis commands
- Create documentation for every command
- Not blindly implement all, but if it ispossible, make it more JS and programmer friendly (e.g. SET, MSET... in source code)
- Create benchmarks
Status
Currently the base of the module is done, but not all commands have shortcut and comments.
If you use this module and need to use commands which are not supported, or simply want to help, it is very easy to add new commands. If you extend Noderis, please send pull request!
Adding new commands
All commands should have doc comments in JSDoc standard. You need to add the implementation in the prototype of RedisClient:
RedisClientprototype = // ... /** * Command description * @param * @param * @param * @return */ { return this; } //...
Then you need to add it to the Promise based proxy:
RedisClientAsyncProxyprototype = // ... /** * Command description * @param * @param * @param * @return */ command: // ...
Unit tests
Unit tests use Mocha. If you have mocha and redis server installed on your local machine you can simple run
mocha
. If not, you need Docker to test with dockunit-plus. Simple run npm test
to start test in a docker
container.