Skip to content

Commit e0b1883

Browse files
committed
feat(transaction): transform replies of transactions
BREAKING CHANGE: 1. Reply transformers is supported inside transactions. 2. `Pipeline#execBuffer()` is deprecated. Use `Pipeline#exec()` instead. Closes #158.
1 parent 4a119bf commit e0b1883

File tree

3 files changed

+75
-25
lines changed

3 files changed

+75
-25
lines changed

lib/command.js

+19-11
Original file line numberDiff line numberDiff line change
@@ -200,25 +200,33 @@ Command.prototype.stringifyArguments = function () {
200200
Command.prototype._convertValue = function (resolve) {
201201
var _this = this;
202202
return function (value) {
203-
// Convert buffer/buffer[] to string/string[]
204-
var result = value;
205-
var transformer;
206203
try {
207-
if (_this.replyEncoding) {
208-
result = utils.convertBufferToString(value, _this.replyEncoding);
209-
}
210-
transformer = Command._transformer.reply[_this.name];
211-
if (transformer) {
212-
result = transformer(result);
213-
}
214-
resolve(result);
204+
resolve(_this.transformReply(value));
215205
} catch (err) {
216206
_this.reject(err);
217207
}
218208
return _this.promise;
219209
};
220210
};
221211

212+
/**
213+
* Convert buffer/buffer[] to string/string[],
214+
* and apply reply transformer.
215+
*
216+
* @public
217+
*/
218+
Command.prototype.transformReply = function (result) {
219+
if (this.replyEncoding) {
220+
result = utils.convertBufferToString(result, this.replyEncoding);
221+
}
222+
var transformer = Command._transformer.reply[this.name];
223+
if (transformer) {
224+
result = transformer(result);
225+
}
226+
227+
return result;
228+
};
229+
222230
Command.FLAGS = {
223231
// Commands that can be processed when Redis is loading data from disk
224232
VALID_WHEN_LOADING: ['info', 'auth', 'select', 'subscribe', 'unsubscribe', 'psubscribe',

lib/pipeline.js

+19-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var Command = require('./command');
55
var fbuffer = require('flexbuffer');
66
var Promise = require('bluebird');
77
var utils = require('./utils');
8+
var util = require('util');
89
var commands = require('redis-commands');
910

1011
function Pipeline(redis) {
@@ -35,13 +36,27 @@ function Pipeline(redis) {
3536
_.assign(Pipeline.prototype, Commander.prototype);
3637

3738
Pipeline.prototype.fillResult = function (value, position) {
39+
var i;
40+
if (this._queue[position].name === 'exec' && Array.isArray(value[1])) {
41+
var execLength = value[1].length;
42+
for (i = 0; i < execLength; i++) {
43+
if (value[1][i] instanceof Error) {
44+
continue;
45+
}
46+
var cmd = this._queue[position - (execLength - i)];
47+
try {
48+
value[1][i] = cmd.transformReply(value[1][i]);
49+
} catch (err) {
50+
value[1][i] = err;
51+
}
52+
}
53+
}
3854
this._result[position] = value;
3955

4056
if (--this.replyPending) {
4157
return;
4258
}
4359

44-
var i;
4560
if (this.isCluster) {
4661
var retriable = true;
4762
var commonError;
@@ -175,18 +190,17 @@ Pipeline.prototype.multi = function () {
175190
};
176191

177192
var execBuffer = Pipeline.prototype.execBuffer;
178-
Pipeline.prototype.execBuffer = function () {
193+
Pipeline.prototype.execBuffer = util.deprecate(function () {
179194
if (this._transactions > 0) {
180195
this._transactions -= 1;
181196
}
182197
return execBuffer.apply(this, arguments);
183-
};
198+
}, 'Pipeline#execBuffer: Use Pipeline#exec instead');
184199

185-
var exec = Pipeline.prototype.exec;
186200
Pipeline.prototype.exec = function (callback) {
187201
if (this._transactions > 0) {
188202
this._transactions -= 1;
189-
return exec.apply(this, arguments);
203+
return execBuffer.apply(this, arguments);
190204
}
191205
if (!this.nodeifiedPromise) {
192206
this.nodeifiedPromise = true;

test/functional/transaction.js

+37-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('transaction', function () {
2424

2525
it('should handle compile-time errors correctly', function (done) {
2626
var redis = new Redis();
27-
redis.multi().set('foo').get('foo').exec(function (err, result) {
27+
redis.multi().set('foo').get('foo').exec(function (err) {
2828
expect(err).to.be.instanceof(Error);
2929
expect(err.toString()).to.match(/Transaction discarded because of previous errors/);
3030
done();
@@ -69,14 +69,42 @@ describe('transaction', function () {
6969
});
7070
});
7171

72-
it('should support execBuffer', function (done) {
73-
var redis = new Redis();
74-
redis.multi().set('foo', 'bar').get('foo').execBuffer(function (err, res) {
75-
expect(res[0][1]).to.be.instanceof(Buffer);
76-
expect(res[0][1].toString()).to.eql('OK');
77-
expect(res[1][1]).to.be.instanceof(Buffer);
78-
expect(res[1][1].toString()).to.eql('bar');
79-
done();
72+
describe('transformer', function () {
73+
it('should trigger transformer', function (done) {
74+
var redis = new Redis();
75+
var pending = 2;
76+
var data = { name: 'Bob', age: '17' };
77+
redis.multi().hmset('foo', data).hgetall('foo', function (err, res) {
78+
expect(res).to.eql('QUEUED');
79+
if (!--pending) {
80+
done();
81+
}
82+
}).hgetallBuffer('foo').exec(function (err, res) {
83+
expect(res[0][1]).to.eql('OK');
84+
expect(res[1][1]).to.eql(data);
85+
expect(res[2][1]).to.eql({
86+
name: new Buffer('Bob'),
87+
age: new Buffer('17')
88+
});
89+
if (!--pending) {
90+
done();
91+
}
92+
});
93+
});
94+
95+
it('should trigger transformer inside pipeline', function (done) {
96+
var redis = new Redis();
97+
var data = { name: 'Bob', age: '17' };
98+
redis.pipeline().hmset('foo', data).multi().typeBuffer('foo')
99+
.hgetall('foo').exec().hgetall('foo').exec(function (err, res) {
100+
expect(res[0][1]).to.eql('OK');
101+
expect(res[1][1]).to.eql('OK');
102+
expect(res[2][1]).to.eql(new Buffer('QUEUED'));
103+
expect(res[3][1]).to.eql('QUEUED');
104+
expect(res[4][1]).to.eql([new Buffer('hash'), data]);
105+
expect(res[5][1]).to.eql(data);
106+
done();
107+
});
80108
});
81109
});
82110

0 commit comments

Comments
 (0)