Pool.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. var Mysql = require('../');
  2. var Connection = require('./Connection');
  3. module.exports = Pool;
  4. function Pool(options) {
  5. this.config = options.config;
  6. this.config.connectionConfig.pool = this;
  7. this._allConnections = [];
  8. this._freeConnections = [];
  9. this._connectionQueue = [];
  10. this._closed = false;
  11. }
  12. Pool.prototype.getConnection = function(cb) {
  13. if (this._closed) {
  14. cb(new Error('Pool is closed.'));
  15. return;
  16. }
  17. if (this._freeConnections.length > 0) {
  18. var connection = this._freeConnections[0];
  19. this._freeConnections.shift();
  20. cb(null, connection);
  21. } else if (this.config.connectionLimit == 0 || this._allConnections.length < this.config.connectionLimit) {
  22. var self = this;
  23. var connection = this._createConnection();
  24. this._allConnections.push(connection);
  25. connection.connect(function(err) {
  26. if (self._closed) {
  27. cb(new Error('Pool is closed.'));
  28. }
  29. else if (err) {
  30. cb(err);
  31. } else {
  32. cb(null, connection);
  33. }
  34. });
  35. } else if (this.config.waitForConnections) {
  36. this._connectionQueue.push(cb);
  37. } else {
  38. cb(new Error('No connections available.'));
  39. }
  40. };
  41. Pool.prototype.releaseConnection = function(connection) {
  42. if (connection._poolRemoved) {
  43. // The connection has been removed from the pool and is no longer good.
  44. if (this._connectionQueue.length) {
  45. var cb = this._connectionQueue[0];
  46. this._connectionQueue.shift();
  47. process.nextTick(this.getConnection.bind(this, cb));
  48. }
  49. } else if (this._connectionQueue.length) {
  50. var cb = this._connectionQueue[0];
  51. this._connectionQueue.shift();
  52. process.nextTick(cb.bind(null, null, connection));
  53. } else {
  54. this._freeConnections.push(connection);
  55. }
  56. };
  57. Pool.prototype.end = function(cb) {
  58. this._closed = true;
  59. cb = cb || function(err) { if( err ) throw err; };
  60. var self = this;
  61. var closedConnections = 0;
  62. var calledBack = false;
  63. var endCB = function(err) {
  64. if (calledBack) {
  65. return;
  66. } else if (err) {
  67. calledBack = true;
  68. delete endCB;
  69. cb(err);
  70. } else if (++closedConnections >= self._allConnections.length) {
  71. calledBack = true;
  72. delete endCB;
  73. cb();
  74. }
  75. };
  76. if (this._allConnections.length == 0) {
  77. endCB();
  78. return;
  79. }
  80. for (var i = 0; i < this._allConnections.length; ++i) {
  81. var connection = this._allConnections[i];
  82. connection.destroy = connection._realDestroy;
  83. connection.end = connection._realEnd;
  84. connection.end(endCB);
  85. }
  86. };
  87. Pool.prototype._createConnection = function() {
  88. var self = this;
  89. var connection = (this.config.createConnection)
  90. ? this.config.createConnection(this.config.connectionConfig)
  91. : Mysql.createConnection(this.config.connectionConfig);
  92. connection._realEnd = connection.end;
  93. connection.end = function(cb) {
  94. self.releaseConnection(connection);
  95. if (cb) cb();
  96. };
  97. connection._realDestroy = connection.destroy;
  98. connection.destroy = function() {
  99. self._removeConnection(connection);
  100. connection.destroy();
  101. };
  102. // When a fatal error occurs the connection's protocol ends, which will cause
  103. // the connection to end as well, thus we only need to watch for the end event
  104. // and we will be notified of disconnects.
  105. connection.on('end', this._handleConnectionEnd.bind(this, connection));
  106. return connection;
  107. };
  108. Pool.prototype._handleConnectionEnd = function(connection) {
  109. if (this._closed || connection._poolRemoved) {
  110. return;
  111. }
  112. this._removeConnection(connection);
  113. };
  114. Pool.prototype._removeConnection = function(connection) {
  115. connection._poolRemoved = true;
  116. for (var i = 0; i < this._allConnections.length; ++i) {
  117. if (this._allConnections[i] === connection) {
  118. this._allConnections.splice(i, 1);
  119. break;
  120. }
  121. }
  122. for (var i = 0; i < this._freeConnections.length; ++i) {
  123. if (this._freeConnections[i] === connection) {
  124. this._freeConnections.splice(i, 1);
  125. break;
  126. }
  127. }
  128. connection.end = connection._realEnd;
  129. connection.destroy = connection._realDestroy;
  130. this.releaseConnection(connection);
  131. };