Skip to content

Commit e47ba4a

Browse files
author
Ruben Bridgewater
committed
Improve performance further
1 parent 2232a89 commit e47ba4a

File tree

5 files changed

+54
-50
lines changed

5 files changed

+54
-50
lines changed

changelog.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
Changelog
22
=========
33

4-
## v.2.x.x - xx, 2015
4+
## v.2.2.0 - xx, 2015
55

66
Features
77

88
- Added disable_resubscribing option to prevent a client from resubscribing after reconnecting (@BridgeAR)
99
- Added rename_commands options to handle renamed commands from the redis config (@digmxl & @BridgeAR)
10-
- Increase performance by exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque) and prevent polymorphism (@BridgeAR)
10+
- Increased performance (@BridgeAR)
11+
- exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque)
12+
- prevent polymorphism
13+
- optimize statements
1114

1215
Bugfixes
1316

index.js

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ function RedisClient(stream, options) {
5555
}
5656
}
5757
}
58+
this.options.return_buffers = !!this.options.return_buffers;
59+
this.options.detect_buffers = !!this.options.detect_buffers;
60+
// Override the detect_buffers setting if return_buffers is active and print a warning
61+
if (this.options.return_buffers && this.options.detect_buffers) {
62+
console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.');
63+
this.options.detect_buffers = false;
64+
}
5865
this.should_buffer = false;
5966
this.command_queue_high_water = options.command_queue_high_water || 1000;
6067
this.command_queue_low_water = options.command_queue_low_water || 0;
@@ -433,7 +440,7 @@ RedisClient.prototype.send_offline_queue = function () {
433440
this.offline_queue = new Queue();
434441
// Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue
435442

436-
if (!buffered_writes) {
443+
if (buffered_writes === 0) {
437444
this.should_buffer = false;
438445
this.emit('drain');
439446
}
@@ -531,21 +538,25 @@ RedisClient.prototype.return_error = function (err) {
531538
err.code = match[1];
532539
}
533540

541+
this.emit_drain_idle(queue_len);
542+
543+
if (command_obj.callback) {
544+
command_obj.callback(err);
545+
} else {
546+
this.emit('error', err);
547+
}
548+
};
549+
550+
RedisClient.prototype.emit_drain_idle = function (queue_len) {
534551
if (this.pub_sub_mode === false && queue_len === 0) {
535-
this.command_queue = new Queue();
552+
this.command_queue.clear();
536553
this.emit('idle');
537554
}
538555

539556
if (this.should_buffer && queue_len <= this.command_queue_low_water) {
540557
this.emit('drain');
541558
this.should_buffer = false;
542559
}
543-
544-
if (command_obj.callback) {
545-
command_obj.callback(err);
546-
} else {
547-
this.emit('error', err);
548-
}
549560
};
550561

551562
RedisClient.prototype.return_reply = function (reply) {
@@ -566,37 +577,29 @@ RedisClient.prototype.return_reply = function (reply) {
566577

567578
queue_len = this.command_queue.length;
568579

569-
if (this.pub_sub_mode === false && queue_len === 0) {
570-
this.command_queue = new Queue(); // explicitly reclaim storage from old Queue
571-
this.emit('idle');
572-
}
573-
if (this.should_buffer && queue_len <= this.command_queue_low_water) {
574-
this.emit('drain');
575-
this.should_buffer = false;
576-
}
580+
this.emit_drain_idle(queue_len);
577581

578582
if (command_obj && !command_obj.sub_command) {
579583
if (typeof command_obj.callback === 'function') {
580584
if ('exec' !== command_obj.command) {
581-
if (this.options.detect_buffers && command_obj.buffer_args === false) {
585+
if (command_obj.buffer_args === false) {
582586
// If detect_buffers option was specified, then the reply from the parser will be Buffers.
583587
// If this command did not use Buffer arguments, then convert the reply to Strings here.
584588
reply = utils.reply_to_strings(reply);
585589
}
586590

587591
// TODO - confusing and error-prone that hgetall is special cased in two places
588-
if (reply && 'hgetall' === command_obj.command) {
592+
if ('hgetall' === command_obj.command) {
589593
reply = utils.reply_to_object(reply);
590594
}
591595
}
592-
593596
command_obj.callback(null, reply);
594597
} else {
595598
debug('No callback for reply');
596599
}
597600
} else if (this.pub_sub_mode || command_obj && command_obj.sub_command) {
598601
if (Array.isArray(reply)) {
599-
if (!this.options.return_buffers && (!command_obj || this.options.detect_buffers && command_obj.buffer_args === false)) {
602+
if (!command_obj || command_obj.buffer_args === false) {
600603
reply = utils.reply_to_strings(reply);
601604
}
602605
type = reply[0].toString();
@@ -620,11 +623,9 @@ RedisClient.prototype.return_reply = function (reply) {
620623
this.emit(type, reply[1], reply[2]); // channel, count
621624
} else {
622625
this.emit('error', new Error('subscriptions are active but got unknown reply type ' + type));
623-
return;
624626
}
625627
} else if (!this.closing) {
626628
this.emit('error', new Error('subscriptions are active but got an invalid reply: ' + reply));
627-
return;
628629
}
629630
}
630631
/* istanbul ignore else: this is a safety check that we should not be able to trigger */
@@ -648,7 +649,12 @@ RedisClient.prototype.return_reply = function (reply) {
648649
};
649650

650651
RedisClient.prototype.send_command = function (command, args, callback) {
651-
var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = '', buffered_writes = 0, err;
652+
var arg, command_obj, i, err,
653+
stream = this.stream,
654+
command_str = '',
655+
buffered_writes = 0,
656+
buffer_args = false,
657+
buffer = this.options.return_buffers;
652658

653659
if (args === undefined) {
654660
args = [];
@@ -660,7 +666,7 @@ RedisClient.prototype.send_command = function (command, args, callback) {
660666
}
661667
}
662668

663-
if (process.domain && callback) {
669+
if (callback && process.domain) {
664670
callback = process.domain.bind(callback);
665671
}
666672

@@ -678,15 +684,17 @@ RedisClient.prototype.send_command = function (command, args, callback) {
678684
}
679685
}
680686

681-
buffer_args = false;
682687
for (i = 0; i < args.length; i += 1) {
683688
if (Buffer.isBuffer(args[i])) {
684689
buffer_args = true;
685690
break;
686691
}
687692
}
693+
if (this.options.detect_buffers) {
694+
buffer = buffer_args;
695+
}
688696

689-
command_obj = new Command(command, args, false, buffer_args, callback);
697+
command_obj = new Command(command, args, false, buffer, callback);
690698

691699
if (!this.ready && !this.send_anyway || !stream.writable) {
692700
if (this.closing || !this.enable_offline_queue) {
@@ -725,16 +733,14 @@ RedisClient.prototype.send_command = function (command, args, callback) {
725733
this.command_queue.push(command_obj);
726734
this.commands_sent += 1;
727735

728-
elem_count = args.length + 1;
729-
730736
if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) {
731737
command = this.options.rename_commands[command];
732738
}
733739

734740
// Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg.
735741
// This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer.
736742

737-
command_str = '*' + elem_count + '\r\n$' + command.length + '\r\n' + command + '\r\n';
743+
command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n';
738744

739745
if (!buffer_args) { // Build up a string and send entire command in one write
740746
for (i = 0; i < args.length; i += 1) {
@@ -752,10 +758,6 @@ RedisClient.prototype.send_command = function (command, args, callback) {
752758

753759
for (i = 0; i < args.length; i += 1) {
754760
arg = args[i];
755-
if (!(Buffer.isBuffer(arg) || typeof arg === 'string')) {
756-
arg = String(arg);
757-
}
758-
759761
if (Buffer.isBuffer(arg)) {
760762
if (arg.length === 0) {
761763
debug('send_command: using empty string for 0 length buffer');
@@ -767,13 +769,16 @@ RedisClient.prototype.send_command = function (command, args, callback) {
767769
debug('send_command: buffer send ' + arg.length + ' bytes');
768770
}
769771
} else {
772+
if (typeof arg !== 'string') {
773+
arg = String(arg);
774+
}
770775
debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg);
771776
buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n');
772777
}
773778
}
774779
}
775-
debug('send_command buffered_writes: ' + buffered_writes, ' should_buffer: ' + this.should_buffer);
776-
if (buffered_writes || this.command_queue.length >= this.command_queue_high_water) {
780+
if (buffered_writes !== 0 || this.command_queue.length >= this.command_queue_high_water) {
781+
debug('send_command buffered_writes: ' + buffered_writes, ' should_buffer: ' + this.should_buffer);
777782
this.should_buffer = true;
778783
}
779784
return !this.should_buffer;
@@ -1077,7 +1082,7 @@ Multi.prototype.execute_callback = function (err, replies) {
10771082
}
10781083
replies[i].command = args[0].toUpperCase();
10791084
} else if (replies[i]) {
1080-
if (this._client.options.detect_buffers && this.wants_buffers[i + 1] === false) {
1085+
if (this.wants_buffers[i + 1] === false) {
10811086
replies[i] = utils.reply_to_strings(replies[i]);
10821087
}
10831088
if (args[0] === 'hgetall') {

lib/parsers/javascript.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
var util = require('util');
44

5-
function ReplyParser(return_buffers) {
5+
function ReplyParser() {
66
this.name = exports.name;
7-
this.return_buffers = return_buffers;
87

98
this._buffer = new Buffer(0);
109
this._offset = 0;
@@ -37,10 +36,8 @@ ReplyParser.prototype._parseResult = function (type) {
3736

3837
if (type === 45) {
3938
return new Error(this._buffer.toString(this._encoding, start, end));
40-
} else if (this.return_buffers) {
41-
return this._buffer.slice(start, end);
4239
}
43-
return this._buffer.toString(this._encoding, start, end);
40+
return this._buffer.slice(start, end);
4441
} else if (type === 58) { // :
4542
// up to the delimiter
4643
end = this._packetEndOffset() - 1;
@@ -77,15 +74,12 @@ ReplyParser.prototype._parseResult = function (type) {
7774
// set the offset to after the delimiter
7875
this._offset = end + 2;
7976

80-
if (this.return_buffers) {
81-
return this._buffer.slice(start, end);
82-
}
83-
return this._buffer.toString(this._encoding, start, end);
77+
return this._buffer.slice(start, end);
8478
} else if (type === 42) { // *
8579
offset = this._offset;
8680
packetHeader = this.parseHeader();
8781

88-
if (packetHeader < 0) {
82+
if (packetHeader === -1) {
8983
return null;
9084
}
9185

lib/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
// hgetall converts its replies to an Object. If the reply is empty, null is returned.
3+
// hgetall converts its replies to an Object. If the reply is empty, null is returned.
44
function replyToObject(reply) {
55
var obj = {}, j, jl, key, val;
66

test/parser/javascript.spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
var assert = require('assert');
44
var Parser = require("../../lib/parsers/javascript").Parser;
55
var config = require("../lib/config");
6+
var utils = require("../../lib/utils");
67
var redis = config.redis;
78

89
describe('javascript parser', function () {
910
it('handles multi-bulk reply', function (done) {
10-
var parser = new Parser(false);
11+
var parser = new Parser();
1112
var reply_count = 0;
1213
function check_reply(reply) {
14+
reply = utils.reply_to_strings(reply);
1315
assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]");
1416
reply_count++;
1517
}

0 commit comments

Comments
 (0)