var mysql = require("mysql") , Pooling = require('generic-pool') , Query = require("./query") , Utils = require("../../utils") , without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) } module.exports = (function() { var ConnectorManager = function(sequelize, config) { this.sequelize = sequelize this.client = null this.config = config || {} this.disconnectTimeoutId = null this.queue = [] this.activeQueue = [] this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50) this.poolCfg = this.config.pool var self = this if (this.poolCfg) { //the user has requested pooling, so create our connection pool if (!this.poolCfg.maxConnections) { this.poolCfg.maxConnections = 10 } this.pool = Pooling.Pool({ name: 'sequelize-mysql', create: function (done) { connect.call(self, done) }, destroy: function(client) { disconnect.call(self, client) }, max: self.poolCfg.maxConnections, idleTimeoutMillis: self.poolCfg.maxIdleTime }) } process.on('exit', function () { //be nice & close our connections on exit if (self.pool) { self.pool.drain() } else if (self.client) { disconnect(self.client) } return }) } Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype) var isConnecting = false ConnectorManager.prototype.query = function(sql, callee, options) { if(!this.isConnected && !this.pool) this.connect() var queueItem = { query: new Query(this.client, this.sequelize, callee, options || {}), sql: sql } enqueue.call(this, queueItem) return queueItem.query } ConnectorManager.prototype.connect = function() { var self = this // in case database is slow to connect, prevent orphaning the client if (this.isConnecting || this.pool) { return } connect.call(self, function(err, client) { self.client = client return }) return } ConnectorManager.prototype.disconnect = function() { if (this.client) disconnect.call(this, this.client) return } // private var disconnect = function(client) { var self = this client.end(function() { var intervalObj = null var cleanup = function () { var retryCt = 0 // make sure to let client finish before calling destroy if (self && self.hasQueuedItems) { return } // needed to prevent mysql connection leak client.destroy() if (self && self.client) { self.client = null } clearInterval(intervalObj) } intervalObj = setInterval(cleanup, 10) cleanup() return }) } var connect = function(done) { var connection = mysql.createConnection({ host: this.config.host, port: this.config.port, user: this.config.username, password: this.config.password, database: this.config.database }) // client.setMaxListeners(self.maxConcurrentQueries) this.isConnecting = false done(null, connection) } var enqueue = function(queueItem) { if(this.activeQueue.length < this.maxConcurrentQueries) { this.activeQueue.push(queueItem) if (this.pool) { var self = this this.pool.acquire(function(err, client) { if (err) { queueItem.query.emit('error', err) return } //we set the client here, asynchronously, when getting a pooled connection //allowing the ConnectorManager.query method to remain synchronous queueItem.query.client = client queueItem.client = client execQueueItem.call(self, queueItem) return }) } else { execQueueItem.call(this, queueItem) } } else { this.queue.push(queueItem) } } var dequeue = function(queueItem) { //return the item's connection to the pool if (this.pool) { this.pool.release(queueItem.client) } this.activeQueue = without(this.activeQueue, queueItem) } var transferQueuedItems = function(count) { for(var i = 0; i < count; i++) { var queueItem = this.queue[0] if(queueItem) { enqueue.call(this, queueItem) this.queue = without(this.queue, queueItem) } } } var afterQuery = function(queueItem) { var self = this dequeue.call(this, queueItem) transferQueuedItems.call(this, this.maxConcurrentQueries - this.activeQueue.length) disconnectIfNoConnections.call(this) } var execQueueItem = function(queueItem) { var self = this queueItem.query .success(function(){ afterQuery.call(self, queueItem) }) .error(function(){ afterQuery.call(self, queueItem) }) queueItem.query.run(queueItem.sql, queueItem.client) } ConnectorManager.prototype.__defineGetter__('hasQueuedItems', function() { return (this.queue.length > 0) || (this.activeQueue.length > 0) || (this.client && this.client._queue && (this.client._queue.length > 0)) }) // legacy ConnectorManager.prototype.__defineGetter__('hasNoConnections', function() { return !this.hasQueuedItems }) ConnectorManager.prototype.__defineGetter__('isConnected', function() { return this.client != null }) var disconnectIfNoConnections = function() { var self = this this.disconnectTimeoutId && clearTimeout(this.disconnectTimeoutId) this.disconnectTimeoutId = setTimeout(function() { self.isConnected && !self.hasQueuedItems && self.disconnect() }, 100) } return ConnectorManager })()
mysql
mysql