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

Commit a7e31040 by Daniel Sim Committed by Sushant

read-only transactions for use on read-replicas (#7324)

1 parent 21671949
# 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] Show a reasonable message when using renameColumn with a missing column [#6606](https://github.com/sequelize/sequelize/issues/6606)
- [PERFORMANCE] more efficient array handing for certain large queries [#7175](https://github.com/sequelize/sequelize/pull/7175)
- [FIXED] Add `unique` indexes defined via options to `rawAttributes` [#7196]
......
......@@ -15,6 +15,8 @@ const Utils = require('./utils');
* @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.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
*
* @see {@link Sequelize.transaction}
*/
......@@ -30,7 +32,8 @@ class Transaction {
this.options = Utils._.extend({
autocommit: transactionOptions.autocommit || null,
type: sequelize.options.transactionType,
isolationLevel: sequelize.options.isolationLevel
isolationLevel: sequelize.options.isolationLevel,
readOnly: false,
}, options || {});
this.parent = this.options.transaction;
......@@ -99,8 +102,19 @@ class Transaction {
}
prepareEnvironment() {
let connectionPromise;
return Utils.Promise.resolve(this.parent ? this.parent.connection : this.sequelize.connectionManager.getConnection({ uuid: this.id }))
if (this.parent) {
connectionPromise = Utils.Promise.resolve(this.parent.connection);
} else {
let acquireOptions = { uuid: this.id };
if (this.options.readOnly) {
acquireOptions.type = 'SELECT';
}
connectionPromise = this.sequelize.connectionManager.getConnection(acquireOptions);
}
return connectionPromise
.then(connection => {
this.connection = connection;
this.connection.uuid = this.id;
......
......@@ -7,11 +7,17 @@ const expect = chai.expect;
const Support = require(__dirname + '/support');
const DataTypes = require(__dirname + '/../../lib/data-types');
const dialect = Support.getTestDialect();
const sinon = require('sinon');
describe(Support.getTestDialectTeaser('Replication'), function() {
if (dialect === 'sqlite') return;
let sandbox;
let readSpy, writeSpy;
beforeEach(() => {
sandbox = sinon.sandbox.create();
this.sequelize = Support.getSequelizeInstance(null, null, null, {
replication: {
write: Support.getConnectionOptions(),
......@@ -29,16 +35,50 @@ describe(Support.getTestDialectTeaser('Replication'), function() {
}
});
return this.User.sync({force: true});
return this.User.sync({force: true})
.then(() => {
readSpy = sandbox.spy(this.sequelize.connectionManager.pool.read, 'acquire');
writeSpy = sandbox.spy(this.sequelize.connectionManager.pool.write, 'acquire');
});
});
afterEach(() => {
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', () => {
return this.User.create({
firstName: Math.random().toString()
});
})
.then(expectWriteCalls);
});
it('should be able to make a read', () => {
return this.User.findAll();
return this.User.findAll()
.then(expectReadCalls);
});
it('should run read-only transactions on the replica', () => {
return this.sequelize.transaction({readOnly: true}, (transaction) => {
return this.User.findAll({transaction});
})
.then(expectReadCalls);
});
it('should run non-read-only transactions on the primary', () => {
return this.sequelize.transaction((transaction) => {
return this.User.findAll({transaction});
})
.then(expectWriteCalls);
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!