不要怂,就是干,撸起袖子干!

Commit 437b8499 by Daniel Sim Committed by Sushant

read-only transactions for read replicas (#7329)

1 parent 5da749c8
# Future # Future
- [ADDED] Ability to run transactions on a read-replica by marking transactions as read only [#7323](https://github.com/sequelize/sequelize/issues/7323)
- [FIXED] Add quotes around column names for unique constraints in sqlite [#4407](https://github.com/sequelize/sequelize/issues/4407) - [FIXED] Add quotes around column names for unique constraints in sqlite [#4407](https://github.com/sequelize/sequelize/issues/4407)
# 3.30.2 # 3.30.2
......
...@@ -15,6 +15,8 @@ var Utils = require('./utils'); ...@@ -15,6 +15,8 @@ var Utils = require('./utils');
* @param {String} options.type=true Sets the type of the transaction. * @param {String} options.type=true Sets the type of the transaction.
* @param {String} options.isolationLevel=true Sets the isolation level of the transaction. * @param {String} options.isolationLevel=true Sets the isolation level of the transaction.
* @param {String} options.deferrable Sets the constraints to be deferred or immediately checked. * @param {String} options.deferrable Sets the constraints to be deferred or immediately checked.
* @param {String} options.readOnly=false Sets the read-only property of the transaction. Such transactions
* will use read replicas when available
*/ */
function Transaction(sequelize, options) { function Transaction(sequelize, options) {
this.sequelize = sequelize; this.sequelize = sequelize;
...@@ -24,7 +26,8 @@ function Transaction(sequelize, options) { ...@@ -24,7 +26,8 @@ function Transaction(sequelize, options) {
this.options = Utils._.extend({ this.options = Utils._.extend({
autocommit: true, autocommit: true,
type: sequelize.options.transactionType, type: sequelize.options.transactionType,
isolationLevel: sequelize.options.isolationLevel isolationLevel: sequelize.options.isolationLevel,
readOnly: false
}, options || {}); }, options || {});
this.parent = this.options.transaction; this.parent = this.options.transaction;
...@@ -221,10 +224,22 @@ Transaction.prototype.rollback = function() { ...@@ -221,10 +224,22 @@ Transaction.prototype.rollback = function() {
Transaction.prototype.prepareEnvironment = function() { Transaction.prototype.prepareEnvironment = function() {
var self = this; var self = this;
var connectionPromise;
return Utils.Promise.resolve( if (this.parent) {
self.parent ? self.parent.connection : self.sequelize.connectionManager.getConnection({ uuid: self.id }) connectionPromise = Utils.Promise.resolve(this.parent.connection);
).then(function (connection) { } else {
var acquireOptions = {uuid: this.id};
if (this.options.readOnly) {
acquireOptions.type = 'SELECT';
}
connectionPromise = this.sequelize.connectionManager.getConnection(acquireOptions);
}
return connectionPromise
.then(function (connection) {
self.connection = connection; self.connection = connection;
self.connection.uuid = self.id; self.connection.uuid = self.id;
}).then(function () { }).then(function () {
......
...@@ -6,12 +6,20 @@ var chai = require('chai') ...@@ -6,12 +6,20 @@ var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, Support = require(__dirname + '/support') , Support = require(__dirname + '/support')
, DataTypes = require(__dirname + '/../../lib/data-types') , DataTypes = require(__dirname + '/../../lib/data-types')
, dialect = Support.getTestDialect(); , dialect = Support.getTestDialect()
, sinon = require('sinon');
describe(Support.getTestDialectTeaser('Replication'), function() { describe(Support.getTestDialectTeaser('Replication'), function() {
if (dialect === 'sqlite') return; if (dialect === 'sqlite') return;
var sandbox;
var readSpy, writeSpy;
beforeEach(function () { beforeEach(function () {
var self = this;
sandbox = sinon.sandbox.create();
this.sequelize = Support.getSequelizeInstance(null, null, null, { this.sequelize = Support.getSequelizeInstance(null, null, null, {
replication: { replication: {
write: Support.getConnectionOptions(), write: Support.getConnectionOptions(),
...@@ -29,16 +37,52 @@ describe(Support.getTestDialectTeaser('Replication'), function() { ...@@ -29,16 +37,52 @@ describe(Support.getTestDialectTeaser('Replication'), function() {
} }
}); });
return this.User.sync({force: true}); return this.User.sync({force: true})
.then(function () {
readSpy = sandbox.spy(self.sequelize.connectionManager.pool.read, 'acquire');
writeSpy = sandbox.spy(self.sequelize.connectionManager.pool.write, 'acquire');
});
});
afterEach(function () {
sandbox.restore();
}); });
function expectReadCalls() {
chai.expect(readSpy.callCount).least(1);
chai.expect(writeSpy.notCalled).eql(true);
}
function expectWriteCalls() {
chai.expect(writeSpy.callCount).least(1);
chai.expect(readSpy.notCalled).eql(true);
}
it('should be able to make a write', function () { it('should be able to make a write', function () {
return this.User.create({ return this.User.create({
firstName: Math.random().toString() firstName: Math.random().toString()
}); })
.then(expectWriteCalls);
}); });
it('should be able to make a read', function () { it('should be able to make a read', function () {
return this.User.findAll(); return this.User.findAll()
.then(expectReadCalls);
});
it('should run read-only transactions on the replica', function () {
var self = this;
return this.sequelize.transaction({readOnly: true}, function (transaction) {
return self.User.findAll({transaction: transaction});
})
.then(expectReadCalls);
});
it('should run non-read-only transactions on the primary', function () {
var self = this;
return self.sequelize.transaction(function (transaction) {
return self.User.findAll({transaction: transaction});
})
.then(expectWriteCalls);
}); });
}); });
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!