You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Minor improvement for .batch and .multi for small values
Improve the speed by round about 5% for small values Add Multi.exec_atomic
This commit is contained in:
105
README.md
105
README.md
@@ -491,8 +491,15 @@ client.multi([
|
||||
console.log(replies);
|
||||
});
|
||||
```
|
||||
|
||||
### Multi.exec_atomic( callback )
|
||||
|
||||
Identical to Multi.exec but with the difference that executing a single command will not use transactions.
|
||||
|
||||
## client.batch([commands])
|
||||
|
||||
Identical to .multi without transactions. This is recommended if you want to execute many commands at once but don't have to rely on transactions.
|
||||
|
||||
`BATCH` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by
|
||||
Redis. The interface in `node_redis` is to return an individual `Batch` object by calling `client.batch()`.
|
||||
The only difference between .batch and .multi is that no transaction is going to be used.
|
||||
@@ -643,55 +650,55 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from
|
||||
hiredis parser (Lenovo T450s i7-5600U):
|
||||
|
||||
Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis
|
||||
PING, 1/5 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 1537ms total, 32530.90 ops/sec
|
||||
PING, 50/5 min/max/avg/p95: 0/ 4/ 0.49/ 1.00 491ms total, 101832.99 ops/sec
|
||||
PING, batch 50/5 min/max/avg/p95: 0/ 2/ 0.17/ 1.00 178ms total, 280898.88 ops/sec
|
||||
SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1400ms total, 35714.29 ops/sec
|
||||
SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.61/ 1.00 610ms total, 81967.21 ops/sec
|
||||
SET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 198ms total, 252525.25 ops/sec
|
||||
SET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 0.00 2349ms total, 21285.65 ops/sec
|
||||
SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 1.63/ 3.00 1632ms total, 30637.25 ops/sec
|
||||
SET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.37/ 1.00 366ms total, 136612.02 ops/sec
|
||||
GET 4B str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1348ms total, 37091.99 ops/sec
|
||||
GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.51/ 1.00 513ms total, 97465.89 ops/sec
|
||||
GET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 177ms total, 282485.88 ops/sec
|
||||
GET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1336ms total, 37425.15 ops/sec
|
||||
GET 4B buf, 50/5 min/max/avg/p95: 0/ 4/ 0.52/ 1.00 525ms total, 95238.10 ops/sec
|
||||
GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 177ms total, 282485.88 ops/sec
|
||||
SET 4KiB str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1674ms total, 29868.58 ops/sec
|
||||
SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.77/ 1.00 775ms total, 64516.13 ops/sec
|
||||
SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 500ms total, 100000.00 ops/sec
|
||||
SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.00 2410ms total, 20746.89 ops/sec
|
||||
SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.64/ 3.00 1643ms total, 30432.14 ops/sec
|
||||
SET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.41/ 1.00 409ms total, 122249.39 ops/sec
|
||||
GET 4KiB str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1422ms total, 35161.74 ops/sec
|
||||
GET 4KiB str, 50/5 min/max/avg/p95: 0/ 4/ 0.68/ 1.00 680ms total, 73529.41 ops/sec
|
||||
GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 391ms total, 127877.24 ops/sec
|
||||
GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1420ms total, 35211.27 ops/sec
|
||||
GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 4/ 0.68/ 1.00 681ms total, 73421.44 ops/sec
|
||||
GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 387ms total, 129198.97 ops/sec
|
||||
INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1334ms total, 37481.26 ops/sec
|
||||
INCR, 50/5 min/max/avg/p95: 0/ 4/ 0.51/ 1.00 513ms total, 97465.89 ops/sec
|
||||
INCR, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 179ms total, 279329.61 ops/sec
|
||||
LPUSH, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1351ms total, 37009.62 ops/sec
|
||||
LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.52/ 1.00 521ms total, 95969.29 ops/sec
|
||||
LPUSH, batch 50/5 min/max/avg/p95: 0/ 2/ 0.20/ 1.00 200ms total, 250000.00 ops/sec
|
||||
LRANGE 10, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1562ms total, 32010.24 ops/sec
|
||||
LRANGE 10, 50/5 min/max/avg/p95: 0/ 4/ 0.69/ 1.00 690ms total, 72463.77 ops/sec
|
||||
LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 393ms total, 127226.46 ops/sec
|
||||
LRANGE 100, 1/5 min/max/avg/p95: 0/ 3/ 0.06/ 1.00 3009ms total, 16616.82 ops/sec
|
||||
LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.85/ 3.00 1850ms total, 27027.03 ops/sec
|
||||
LRANGE 100, batch 50/5 min/max/avg/p95: 2/ 4/ 2.15/ 3.00 2153ms total, 23223.41 ops/sec
|
||||
SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 5/ 1.91/ 3.00 957ms total, 522.47 ops/sec
|
||||
SET 4MiB buf, 50/5 min/max/avg/p95: 13/ 109/ 94.20/ 102.00 987ms total, 506.59 ops/sec
|
||||
SET 4MiB buf, batch 50/5 min/max/avg/p95: 90/ 107/ 93.10/ 107.00 931ms total, 537.06 ops/sec
|
||||
GET 4MiB str, 1/5 min/max/avg/p95: 4/ 16/ 5.97/ 10.00 598ms total, 167.22 ops/sec
|
||||
GET 4MiB str, 50/5 min/max/avg/p95: 10/ 249/ 179.47/ 231.90 454ms total, 220.26 ops/sec
|
||||
GET 4MiB str, batch 50/5 min/max/avg/p95: 215/ 226/ 220.50/ 226.00 441ms total, 226.76 ops/sec
|
||||
GET 4MiB buf, 1/5 min/max/avg/p95: 3/ 26/ 6.55/ 11.95 658ms total, 151.98 ops/sec
|
||||
GET 4MiB buf, 50/5 min/max/avg/p95: 11/ 265/ 186.73/ 241.90 469ms total, 213.22 ops/sec
|
||||
GET 4MiB buf, batch 50/5 min/max/avg/p95: 226/ 247/ 236.50/ 247.00 473ms total, 211.42 ops/sec
|
||||
End of tests. Total time elapsed: 44952 ms
|
||||
PING, 1/5 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 1223ms total, 40883.07 ops/sec
|
||||
PING, 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 497ms total, 100603.62 ops/sec
|
||||
PING, batch 50/5 min/max/avg/p95: 0/ 1/ 0.15/ 1.00 308ms total, 324675.32 ops/sec
|
||||
SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1402ms total, 35663.34 ops/sec
|
||||
SET 4B str, 50/5 min/max/avg/p95: 0/ 2/ 0.53/ 1.00 534ms total, 93632.96 ops/sec
|
||||
SET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 392ms total, 255102.04 ops/sec
|
||||
SET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 1.00 2433ms total, 20550.76 ops/sec
|
||||
SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 1.65/ 3.00 1652ms total, 30266.34 ops/sec
|
||||
SET 4B buf, batch 50/5 min/max/avg/p95: 0/ 3/ 0.36/ 1.00 726ms total, 137741.05 ops/sec
|
||||
GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1314ms total, 38051.75 ops/sec
|
||||
GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.53/ 1.00 529ms total, 94517.96 ops/sec
|
||||
GET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.16/ 1.00 328ms total, 304878.05 ops/sec
|
||||
GET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1389ms total, 35997.12 ops/sec
|
||||
GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.52/ 1.00 519ms total, 96339.11 ops/sec
|
||||
GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.16/ 1.00 168ms total, 297619.05 ops/sec
|
||||
SET 4KiB str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1670ms total, 29940.12 ops/sec
|
||||
SET 4KiB str, 50/5 min/max/avg/p95: 0/ 5/ 0.94/ 2.00 941ms total, 53134.96 ops/sec
|
||||
SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.49/ 1.00 984ms total, 101626.02 ops/sec
|
||||
SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 0.00 2423ms total, 20635.58 ops/sec
|
||||
SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.60/ 3.00 1598ms total, 31289.11 ops/sec
|
||||
SET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.41/ 1.00 825ms total, 121212.12 ops/sec
|
||||
GET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1483ms total, 33715.44 ops/sec
|
||||
GET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.69/ 1.00 691ms total, 72358.90 ops/sec
|
||||
GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.38/ 1.00 759ms total, 131752.31 ops/sec
|
||||
GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1485ms total, 33670.03 ops/sec
|
||||
GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 3/ 0.80/ 2.00 797ms total, 62735.26 ops/sec
|
||||
GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 396ms total, 126262.63 ops/sec
|
||||
INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1376ms total, 36337.21 ops/sec
|
||||
INCR, 50/5 min/max/avg/p95: 0/ 3/ 0.53/ 1.00 529ms total, 94517.96 ops/sec
|
||||
INCR, batch 50/5 min/max/avg/p95: 0/ 1/ 0.17/ 1.00 339ms total, 294985.25 ops/sec
|
||||
LPUSH, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1394ms total, 35868.01 ops/sec
|
||||
LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.58/ 1.00 584ms total, 85616.44 ops/sec
|
||||
LPUSH, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 383ms total, 261096.61 ops/sec
|
||||
LRANGE 10, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1706ms total, 29308.32 ops/sec
|
||||
LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 0.71/ 1.00 712ms total, 70224.72 ops/sec
|
||||
LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 1/ 0.38/ 1.00 772ms total, 129533.68 ops/sec
|
||||
LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 3026ms total, 16523.46 ops/sec
|
||||
LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.88/ 3.00 1882ms total, 26567.48 ops/sec
|
||||
LRANGE 100, batch 50/5 min/max/avg/p95: 2/ 4/ 2.09/ 3.00 4189ms total, 23872.05 ops/sec
|
||||
SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 7/ 2.08/ 3.00 1044ms total, 478.93 ops/sec
|
||||
SET 4MiB buf, 20/5 min/max/avg/p95: 17/ 50/ 40.02/ 46.00 1022ms total, 489.24 ops/sec
|
||||
SET 4MiB buf, batch 20/5 min/max/avg/p95: 37/ 45/ 39.00/ 44.40 975ms total, 512.82 ops/sec
|
||||
GET 4MiB str, 1/5 min/max/avg/p95: 4/ 15/ 6.31/ 10.00 634ms total, 157.73 ops/sec
|
||||
GET 4MiB str, 20/5 min/max/avg/p95: 7/ 124/ 88.27/ 110.80 476ms total, 210.08 ops/sec
|
||||
GET 4MiB str, batch 20/5 min/max/avg/p95: 76/ 99/ 89.00/ 99.00 446ms total, 224.22 ops/sec
|
||||
GET 4MiB buf, 1/5 min/max/avg/p95: 4/ 12/ 5.67/ 10.00 568ms total, 176.06 ops/sec
|
||||
GET 4MiB buf, 20/5 min/max/avg/p95: 14/ 133/ 85.34/ 107.95 458ms total, 218.34 ops/sec
|
||||
GET 4MiB buf, batch 20/5 min/max/avg/p95: 78/ 96/ 88.00/ 96.00 440ms total, 227.27 ops/sec
|
||||
End of tests. Total time elapsed: 50421 ms
|
||||
|
||||
The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though.
|
||||
Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do:
|
||||
|
@@ -200,19 +200,19 @@ very_large_buf = new Buffer(very_large_str);
|
||||
|
||||
tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50}));
|
||||
tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50}));
|
||||
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50}));
|
||||
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50}));
|
||||
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} }));
|
||||
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} }));
|
||||
@@ -220,15 +220,15 @@ tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000
|
||||
|
||||
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50}));
|
||||
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50}));
|
||||
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50}));
|
||||
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} }));
|
||||
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} }));
|
||||
@@ -236,31 +236,31 @@ tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand0000
|
||||
|
||||
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50}));
|
||||
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50}));
|
||||
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50}));
|
||||
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1}));
|
||||
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50}));
|
||||
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50}));
|
||||
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50, reqs: num_requests * 2}));
|
||||
|
||||
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1, reqs: 500}));
|
||||
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 50, reqs: 500}));
|
||||
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 50, reqs: 500}));
|
||||
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20, reqs: 500}));
|
||||
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20, reqs: 500}));
|
||||
|
||||
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100}));
|
||||
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100}));
|
||||
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 50, reqs: 100}));
|
||||
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, reqs: 100}));
|
||||
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20, reqs: 100}));
|
||||
|
||||
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} }));
|
||||
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} }));
|
||||
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 50, reqs: 100, client_opts: { return_buffers: true} }));
|
||||
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, reqs: 100, client_opts: { return_buffers: true} }));
|
||||
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, reqs: 100, client_opts: { return_buffers: true} }));
|
||||
|
||||
function next() {
|
||||
var test = tests.shift();
|
||||
|
28
changelog.md
28
changelog.md
@@ -1,7 +1,7 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
## v.2.2.0 - 08, 2015 - The peregrino falcon
|
||||
## v.2.2.0 - xx Oct, 2015 - The peregrino falcon
|
||||
|
||||
The peregrino falcon is the fasted bird on earth and this is what this release is all about: We increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details.
|
||||
|
||||
@@ -26,6 +26,32 @@ Both .multi and .batch are from now on going to cache the commands and release t
|
||||
|
||||
Please consider using .batch instead of looping through a lot of commands one by one. This will significantly improve your performance.
|
||||
|
||||
Here are some stats compared to ioredis 1.9.1:
|
||||
|
||||
simple set
|
||||
82,496 op/s » ioredis
|
||||
112,617 op/s » node_redis
|
||||
|
||||
simple get
|
||||
82,015 op/s » ioredis
|
||||
105,701 op/s » node_redis
|
||||
|
||||
simple get with pipeline
|
||||
10,233 op/s » ioredis
|
||||
26,541 op/s » node_redis
|
||||
|
||||
lrange 100
|
||||
7,321 op/s » ioredis
|
||||
26,155 op/s » node_redis
|
||||
|
||||
publish
|
||||
90,524 op/s » ioredis
|
||||
112,823 op/s » node_redis
|
||||
|
||||
subscribe
|
||||
43,783 op/s » ioredis
|
||||
61,889 op/s » node_redis
|
||||
|
||||
To conclude: we can proudly say that node_redis is very likely outperforming any other node redis client.
|
||||
|
||||
## v2.1.0 - Oct 02, 2015
|
||||
|
100
index.js
100
index.js
@@ -34,17 +34,19 @@ parsers.push(require('./lib/parsers/javascript'));
|
||||
|
||||
function RedisClient(stream, options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
|
||||
this.pipeline = 0;
|
||||
if (!stream.cork) {
|
||||
stream.cork = function noop() {};
|
||||
stream.cork = function noop() {
|
||||
self.pipeline_queue = new Queue();
|
||||
};
|
||||
stream.uncork = function noop() {};
|
||||
stream.__write = stream.write;
|
||||
stream.write = this.writeStream.bind(this);
|
||||
this.write = this.writeStream;
|
||||
}
|
||||
|
||||
this.stream = stream;
|
||||
this.options = options;
|
||||
|
||||
this.connection_id = ++connection_id;
|
||||
this.connected = false;
|
||||
this.ready = false;
|
||||
@@ -73,14 +75,11 @@ function RedisClient(stream, options) {
|
||||
this.command_queue_high_water = options.command_queue_high_water || 1000;
|
||||
this.command_queue_low_water = options.command_queue_low_water || 0;
|
||||
this.max_attempts = +options.max_attempts || 0;
|
||||
this.command_queue = new Queue(); // holds sent commands to de-pipeline them
|
||||
this.offline_queue = new Queue(); // holds commands issued but not able to be sent
|
||||
this.command_queue = new Queue(); // Holds sent commands to de-pipeline them
|
||||
this.offline_queue = new Queue(); // Holds commands issued but not able to be sent
|
||||
this.commands_sent = 0;
|
||||
this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms
|
||||
this.enable_offline_queue = true;
|
||||
if (options.enable_offline_queue === false) {
|
||||
this.enable_offline_queue = false;
|
||||
}
|
||||
this.enable_offline_queue = options.enable_offline_queue === false ? false : true;
|
||||
this.retry_max_delay = +options.retry_max_delay || null;
|
||||
this.initialize_retry_vars();
|
||||
this.pub_sub_mode = false;
|
||||
@@ -90,10 +89,8 @@ function RedisClient(stream, options) {
|
||||
this.server_info = {};
|
||||
this.auth_pass = options.auth_pass;
|
||||
this.parser_module = null;
|
||||
this.selected_db = null; // save the selected db here, used when reconnecting
|
||||
this.selected_db = null; // Save the selected db here, used when reconnecting
|
||||
this.old_state = null;
|
||||
this.pipeline = 0;
|
||||
this.pipeline_queue = new Queue();
|
||||
|
||||
this.install_stream_listeners();
|
||||
events.EventEmitter.call(this);
|
||||
@@ -154,21 +151,19 @@ RedisClient.prototype.unref = function () {
|
||||
// flush offline_queue and command_queue, erroring any items with a callback first
|
||||
RedisClient.prototype.flush_and_error = function (error) {
|
||||
var command_obj;
|
||||
|
||||
while (command_obj = this.offline_queue.shift()) {
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
error.command = command_obj.command.toUpperCase();
|
||||
command_obj.callback(error);
|
||||
}
|
||||
}
|
||||
this.offline_queue = new Queue();
|
||||
|
||||
while (command_obj = this.command_queue.shift()) {
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
error.command = command_obj.command.toUpperCase();
|
||||
command_obj.callback(error);
|
||||
}
|
||||
}
|
||||
this.offline_queue = new Queue();
|
||||
this.command_queue = new Queue();
|
||||
};
|
||||
|
||||
@@ -249,7 +244,6 @@ RedisClient.prototype.on_connect = function () {
|
||||
this.connected = true;
|
||||
this.ready = false;
|
||||
this.connections += 1;
|
||||
this.command_queue = new Queue();
|
||||
this.emitted_end = false;
|
||||
if (this.options.socket_nodelay) {
|
||||
this.stream.setNoDelay();
|
||||
@@ -446,8 +440,8 @@ RedisClient.prototype.send_offline_queue = function () {
|
||||
debug('Sending offline command: ' + command_obj.command);
|
||||
buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback);
|
||||
}
|
||||
this.offline_queue = new Queue();
|
||||
// Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue
|
||||
this.offline_queue = new Queue();
|
||||
|
||||
if (buffered_writes === 0) {
|
||||
this.should_buffer = false;
|
||||
@@ -558,7 +552,8 @@ RedisClient.prototype.return_error = function (err) {
|
||||
|
||||
RedisClient.prototype.emit_drain_idle = function (queue_len) {
|
||||
if (this.pub_sub_mode === false && queue_len === 0) {
|
||||
this.command_queue.clear();
|
||||
// Free the queue capacity memory by using a new queue
|
||||
this.command_queue = new Queue();
|
||||
this.emit('idle');
|
||||
}
|
||||
|
||||
@@ -763,21 +758,21 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
||||
command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n';
|
||||
}
|
||||
debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str);
|
||||
buffered_writes += !stream.write(command_str);
|
||||
buffered_writes += !this.write(command_str);
|
||||
} else {
|
||||
debug('Send command (' + command_str + ') has Buffer arguments');
|
||||
buffered_writes += !stream.write(command_str);
|
||||
buffered_writes += !this.write(command_str);
|
||||
|
||||
for (i = 0; i < args.length; i += 1) {
|
||||
arg = args[i];
|
||||
if (Buffer.isBuffer(arg)) {
|
||||
if (arg.length === 0) {
|
||||
debug('send_command: using empty string for 0 length buffer');
|
||||
buffered_writes += !stream.write('$0\r\n\r\n');
|
||||
buffered_writes += !this.write('$0\r\n\r\n');
|
||||
} else {
|
||||
buffered_writes += !stream.write('$' + arg.length + '\r\n');
|
||||
buffered_writes += !stream.write(arg);
|
||||
buffered_writes += !stream.write('\r\n');
|
||||
buffered_writes += !this.write('$' + arg.length + '\r\n');
|
||||
buffered_writes += !this.write(arg);
|
||||
buffered_writes += !this.write('\r\n');
|
||||
debug('send_command: buffer send ' + arg.length + ' bytes');
|
||||
}
|
||||
} else {
|
||||
@@ -785,7 +780,7 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
||||
arg = String(arg);
|
||||
}
|
||||
debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg);
|
||||
buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n');
|
||||
buffered_writes += !this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -796,22 +791,28 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
||||
return !this.should_buffer;
|
||||
};
|
||||
|
||||
RedisClient.prototype.write = function (data) {
|
||||
return this.stream.write(data);
|
||||
};
|
||||
|
||||
RedisClient.prototype.writeStream = function (data) {
|
||||
var nr = 0;
|
||||
|
||||
// Do not use a pipeline
|
||||
if (this.pipeline === 0) {
|
||||
return this.stream.__write(data);
|
||||
return this.stream.write(data);
|
||||
}
|
||||
|
||||
this.pipeline--;
|
||||
this.pipeline_queue.push(data);
|
||||
if (this.pipeline === 0) {
|
||||
var len = this.pipeline_queue.length;
|
||||
while (len--) {
|
||||
nr += !this.stream.__write(this.pipeline_queue.shift());
|
||||
var command;
|
||||
while (command = this.pipeline_queue.shift()) {
|
||||
nr += !this.stream.write(command);
|
||||
}
|
||||
nr += !this.stream.write(data);
|
||||
return !nr;
|
||||
}
|
||||
|
||||
this.pipeline_queue.push(data);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -871,13 +872,11 @@ RedisClient.prototype.end = function (flush) {
|
||||
function Multi(client, args, transaction) {
|
||||
client.stream.cork();
|
||||
this._client = client;
|
||||
this.queue = [];
|
||||
this.queue = new Queue();
|
||||
if (transaction) {
|
||||
this.exec = this.exec_transaction;
|
||||
this.EXEC = this.exec_transaction;
|
||||
this.queue.push(['multi']);
|
||||
}
|
||||
this._client.pipeline_queue.clear();
|
||||
var command, tmp_args;
|
||||
if (Array.isArray(args)) {
|
||||
while (tmp_args = args.shift()) {
|
||||
@@ -908,7 +907,7 @@ commands.forEach(function (fullCommand) {
|
||||
return;
|
||||
}
|
||||
|
||||
RedisClient.prototype[command] = function (key, arg, callback) {
|
||||
RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) {
|
||||
if (Array.isArray(key)) {
|
||||
return this.send_command(command, key, arg);
|
||||
}
|
||||
@@ -926,9 +925,8 @@ commands.forEach(function (fullCommand) {
|
||||
}
|
||||
return this.send_command(command, utils.to_array(arguments));
|
||||
};
|
||||
RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command];
|
||||
|
||||
Multi.prototype[command] = function (key, arg, callback) {
|
||||
Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function (key, arg, callback) {
|
||||
if (Array.isArray(key)) {
|
||||
if (arg) {
|
||||
key = key.concat([arg]);
|
||||
@@ -952,7 +950,6 @@ commands.forEach(function (fullCommand) {
|
||||
}
|
||||
return this;
|
||||
};
|
||||
Multi.prototype[command.toUpperCase()] = Multi.prototype[command];
|
||||
});
|
||||
|
||||
// store db in this.select_db to restore it on reconnect
|
||||
@@ -1061,23 +1058,31 @@ Multi.prototype.send_command = function (command, args, index, cb) {
|
||||
if (cb) {
|
||||
cb(err);
|
||||
}
|
||||
err.position = index - 1;
|
||||
err.position = index;
|
||||
self.errors.push(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Multi.prototype.exec_atomic = function (callback) {
|
||||
if (this.queue.length < 2) {
|
||||
return this.exec_batch(callback);
|
||||
}
|
||||
return this.exec(callback);
|
||||
};
|
||||
|
||||
Multi.prototype.exec_transaction = function (callback) {
|
||||
var self = this;
|
||||
var len = this.queue.length;
|
||||
var cb;
|
||||
this.errors = [];
|
||||
this.callback = callback;
|
||||
this._client.pipeline = len;
|
||||
this._client.pipeline = len + 2;
|
||||
this.wants_buffers = new Array(len);
|
||||
this.send_command('multi', []);
|
||||
// drain queue, callback will catch 'QUEUED' or error
|
||||
for (var index = 0; index < len; index++) {
|
||||
var args = this.queue[index].slice(0);
|
||||
var args = this.queue.get(index).slice(0);
|
||||
var command = args.shift();
|
||||
if (typeof args[args.length - 1] === 'function') {
|
||||
cb = args.pop();
|
||||
@@ -1108,7 +1113,7 @@ Multi.prototype.exec_transaction = function (callback) {
|
||||
};
|
||||
|
||||
Multi.prototype.execute_callback = function (err, replies) {
|
||||
var i, args;
|
||||
var i = 0, args;
|
||||
|
||||
if (err) {
|
||||
if (err.code !== 'CONNECTION_BROKEN') {
|
||||
@@ -1124,9 +1129,7 @@ Multi.prototype.execute_callback = function (err, replies) {
|
||||
}
|
||||
|
||||
if (replies) {
|
||||
for (i = 0; i < this.queue.length - 1; i += 1) {
|
||||
args = this.queue[i + 1];
|
||||
|
||||
while (args = this.queue.shift()) {
|
||||
// If we asked for strings, even in detect_buffers mode, then return strings:
|
||||
if (replies[i] instanceof Error) {
|
||||
var match = replies[i].message.match(utils.errCode);
|
||||
@@ -1136,7 +1139,7 @@ Multi.prototype.execute_callback = function (err, replies) {
|
||||
}
|
||||
replies[i].command = args[0].toUpperCase();
|
||||
} else if (replies[i]) {
|
||||
if (this.wants_buffers[i + 1] === false) {
|
||||
if (this.wants_buffers[i] === false) {
|
||||
replies[i] = utils.reply_to_strings(replies[i]);
|
||||
}
|
||||
if (args[0] === 'hgetall') {
|
||||
@@ -1152,6 +1155,7 @@ Multi.prototype.execute_callback = function (err, replies) {
|
||||
args[args.length - 1](null, replies[i]);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1176,7 +1180,7 @@ Multi.prototype.callback = function (cb, command, i) {
|
||||
};
|
||||
};
|
||||
|
||||
Multi.prototype.exec = Multi.prototype.EXEC = function (callback) {
|
||||
Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = function (callback) {
|
||||
var len = this.queue.length;
|
||||
var self = this;
|
||||
var index = 0;
|
||||
|
@@ -385,6 +385,41 @@ describe("The 'multi' method", function () {
|
||||
client.get('foo', helper.isString('bar', done));
|
||||
});
|
||||
|
||||
it("should not use a transaction with exec_atomic if only no command is used", function () {
|
||||
var multi = client.multi();
|
||||
var test = false;
|
||||
multi.exec_batch = function () {
|
||||
test = true;
|
||||
};
|
||||
multi.exec_atomic();
|
||||
assert(test);
|
||||
});
|
||||
|
||||
it("should not use a transaction with exec_atomic if only one command is used", function () {
|
||||
var multi = client.multi();
|
||||
var test = false;
|
||||
multi.exec_batch = function () {
|
||||
test = true;
|
||||
};
|
||||
multi.set("baz", "binary");
|
||||
multi.exec_atomic();
|
||||
assert(test);
|
||||
});
|
||||
|
||||
it("should use transaction with exec_atomic and more than one command used", function (done) {
|
||||
helper.serverVersionAtLeast.call(this, client, [2, 6, 5]);
|
||||
|
||||
var multi = client.multi();
|
||||
var test = false;
|
||||
multi.exec_batch = function () {
|
||||
test = true;
|
||||
};
|
||||
multi.set("baz", "binary");
|
||||
multi.get('baz');
|
||||
multi.exec_atomic(done);
|
||||
assert(!test);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -17,13 +17,7 @@ describe("rename commands", function () {
|
||||
describe("using " + parser + " and " + ip, function () {
|
||||
var client = null;
|
||||
|
||||
afterEach(function () {
|
||||
client.end();
|
||||
});
|
||||
|
||||
it("allows to use renamed functions", function (done) {
|
||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||
|
||||
beforeEach(function(done) {
|
||||
client = redis.createClient({
|
||||
rename_commands: {
|
||||
set: '807081f5afa96845a02816a28b7258c3',
|
||||
@@ -31,6 +25,18 @@ describe("rename commands", function () {
|
||||
}
|
||||
});
|
||||
|
||||
client.on('ready', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
client.end();
|
||||
});
|
||||
|
||||
it("allows to use renamed functions", function (done) {
|
||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||
|
||||
client.set('key', 'value', function(err, reply) {
|
||||
assert.strictEqual(reply, 'OK');
|
||||
});
|
||||
@@ -48,16 +54,26 @@ describe("rename commands", function () {
|
||||
});
|
||||
});
|
||||
|
||||
it("should also work with multi", function (done) {
|
||||
it("should also work with batch", function (done) {
|
||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||
|
||||
client = redis.createClient({
|
||||
rename_commands: {
|
||||
SET: '807081f5afa96845a02816a28b7258c3',
|
||||
getrange: '9e3102b15cf231c4e9e940f284744fe0'
|
||||
}
|
||||
client.batch([['set', 'key', 'value']]).exec(function (err, res) {
|
||||
assert.strictEqual(res[0], 'OK');
|
||||
});
|
||||
|
||||
var batch = client.batch();
|
||||
batch.getrange('key', 1, -1);
|
||||
batch.exec(function (err, res) {
|
||||
assert(!err);
|
||||
assert.strictEqual(res.length, 1);
|
||||
assert.strictEqual(res[0], 'alue');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should also work with multi", function (done) {
|
||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||
|
||||
client.multi([['set', 'key', 'value']]).exec(function (err, res) {
|
||||
assert.strictEqual(res[0], 'OK');
|
||||
});
|
||||
@@ -75,13 +91,6 @@ describe("rename commands", function () {
|
||||
it("should also work with multi and abort transaction", function (done) {
|
||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||
|
||||
client = redis.createClient({
|
||||
rename_commands: {
|
||||
SET: '807081f5afa96845a02816a28b7258c3',
|
||||
getrange: '9e3102b15cf231c4e9e940f284744fe0'
|
||||
}
|
||||
});
|
||||
|
||||
var multi = client.multi();
|
||||
multi.get('key');
|
||||
multi.getrange('key', 1, -1, function(err, reply) {
|
||||
|
Reference in New Issue
Block a user