You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					212 lines
				
				5.9 KiB
			
		
		
			
		
	
	
					212 lines
				
				5.9 KiB
			| 
											3 years ago
										 | /*! | ||
|  |  * Module dependencies. | ||
|  |  */ | ||
|  | 
 | ||
|  | 'use strict'; | ||
|  | 
 | ||
|  | const MongooseConnection = require('../../connection'); | ||
|  | const STATES = require('../../connectionstate'); | ||
|  | const immediate = require('../../helpers/immediate'); | ||
|  | const setTimeout = require('../../helpers/timers').setTimeout; | ||
|  | 
 | ||
|  | /** | ||
|  |  * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
 | ||
|  |  * | ||
|  |  * @inherits Connection | ||
|  |  * @api private | ||
|  |  */ | ||
|  | 
 | ||
|  | function NativeConnection() { | ||
|  |   MongooseConnection.apply(this, arguments); | ||
|  |   this._listening = false; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Expose the possible connection states. | ||
|  |  * @api public | ||
|  |  */ | ||
|  | 
 | ||
|  | NativeConnection.STATES = STATES; | ||
|  | 
 | ||
|  | /*! | ||
|  |  * Inherits from Connection. | ||
|  |  */ | ||
|  | 
 | ||
|  | NativeConnection.prototype.__proto__ = MongooseConnection.prototype; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Switches to a different database using the same connection pool. | ||
|  |  * | ||
|  |  * Returns a new connection object, with the new db. If you set the `useCache` | ||
|  |  * option, `useDb()` will cache connections by `name`. | ||
|  |  * | ||
|  |  * **Note:** Calling `close()` on a `useDb()` connection will close the base connection as well. | ||
|  |  * | ||
|  |  * @param {String} name The database name | ||
|  |  * @param {Object} [options] | ||
|  |  * @param {Boolean} [options.useCache=false] If true, cache results so calling `useDb()` multiple times with the same name only creates 1 connection object. | ||
|  |  * @param {Boolean} [options.noListener=false] If true, the new connection object won't listen to any events on the base connection. This is better for memory usage in cases where you're calling `useDb()` for every request. | ||
|  |  * @return {Connection} New Connection Object | ||
|  |  * @api public | ||
|  |  */ | ||
|  | 
 | ||
|  | NativeConnection.prototype.useDb = function(name, options) { | ||
|  |   // Return immediately if cached
 | ||
|  |   options = options || {}; | ||
|  |   if (options.useCache && this.relatedDbs[name]) { | ||
|  |     return this.relatedDbs[name]; | ||
|  |   } | ||
|  | 
 | ||
|  |   // we have to manually copy all of the attributes...
 | ||
|  |   const newConn = new this.constructor(); | ||
|  |   newConn.name = name; | ||
|  |   newConn.base = this.base; | ||
|  |   newConn.collections = {}; | ||
|  |   newConn.models = {}; | ||
|  |   newConn.replica = this.replica; | ||
|  |   newConn.config = Object.assign({}, this.config, newConn.config); | ||
|  |   newConn.name = this.name; | ||
|  |   newConn.options = this.options; | ||
|  |   newConn._readyState = this._readyState; | ||
|  |   newConn._closeCalled = this._closeCalled; | ||
|  |   newConn._hasOpened = this._hasOpened; | ||
|  |   newConn._listening = false; | ||
|  | 
 | ||
|  |   newConn.host = this.host; | ||
|  |   newConn.port = this.port; | ||
|  |   newConn.user = this.user; | ||
|  |   newConn.pass = this.pass; | ||
|  | 
 | ||
|  |   // First, when we create another db object, we are not guaranteed to have a
 | ||
|  |   // db object to work with. So, in the case where we have a db object and it
 | ||
|  |   // is connected, we can just proceed with setting everything up. However, if
 | ||
|  |   // we do not have a db or the state is not connected, then we need to wait on
 | ||
|  |   // the 'open' event of the connection before doing the rest of the setup
 | ||
|  |   // the 'connected' event is the first time we'll have access to the db object
 | ||
|  | 
 | ||
|  |   const _this = this; | ||
|  | 
 | ||
|  |   newConn.client = _this.client; | ||
|  | 
 | ||
|  |   if (this.db && this._readyState === STATES.connected) { | ||
|  |     wireup(); | ||
|  |   } else { | ||
|  |     this.once('connected', wireup); | ||
|  |   } | ||
|  | 
 | ||
|  |   function wireup() { | ||
|  |     newConn.client = _this.client; | ||
|  |     const _opts = {}; | ||
|  |     if (options.hasOwnProperty('noListener')) { | ||
|  |       _opts.noListener = options.noListener; | ||
|  |     } | ||
|  |     newConn.db = _this.client.db(name, _opts); | ||
|  |     newConn.onOpen(); | ||
|  |     // setup the events appropriately
 | ||
|  |     if (options.noListener !== true) { | ||
|  |       listen(newConn); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   newConn.name = name; | ||
|  | 
 | ||
|  |   // push onto the otherDbs stack, this is used when state changes
 | ||
|  |   if (options.noListener !== true) { | ||
|  |     this.otherDbs.push(newConn); | ||
|  |   } | ||
|  |   newConn.otherDbs.push(this); | ||
|  | 
 | ||
|  |   // push onto the relatedDbs cache, this is used when state changes
 | ||
|  |   if (options && options.useCache) { | ||
|  |     this.relatedDbs[newConn.name] = newConn; | ||
|  |     newConn.relatedDbs = this.relatedDbs; | ||
|  |   } | ||
|  | 
 | ||
|  |   return newConn; | ||
|  | }; | ||
|  | 
 | ||
|  | /*! | ||
|  |  * Register listeners for important events and bubble appropriately. | ||
|  |  */ | ||
|  | 
 | ||
|  | function listen(conn) { | ||
|  |   if (conn._listening) { | ||
|  |     return; | ||
|  |   } | ||
|  |   conn._listening = true; | ||
|  | 
 | ||
|  |   conn.client.on('close', function(force) { | ||
|  |     if (conn._closeCalled) { | ||
|  |       return; | ||
|  |     } | ||
|  |     conn._closeCalled = conn.client._closeCalled; | ||
|  | 
 | ||
|  |     // the driver never emits an `open` event. auto_reconnect still
 | ||
|  |     // emits a `close` event but since we never get another
 | ||
|  |     // `open` we can't emit close
 | ||
|  |     if (conn.db.serverConfig.autoReconnect) { | ||
|  |       conn.readyState = STATES.disconnected; | ||
|  |       conn.emit('close'); | ||
|  |       return; | ||
|  |     } | ||
|  |     conn.onClose(force); | ||
|  |   }); | ||
|  |   conn.client.on('error', function(err) { | ||
|  |     conn.emit('error', err); | ||
|  |   }); | ||
|  | 
 | ||
|  |   if (!conn.client.s.options.useUnifiedTopology) { | ||
|  |     conn.db.on('reconnect', function() { | ||
|  |       conn.readyState = STATES.connected; | ||
|  |       conn.emit('reconnect'); | ||
|  |       conn.emit('reconnected'); | ||
|  |       conn.onOpen(); | ||
|  |     }); | ||
|  |     conn.db.on('open', function(err, db) { | ||
|  |       if (STATES.disconnected === conn.readyState && db && db.databaseName) { | ||
|  |         conn.readyState = STATES.connected; | ||
|  |         conn.emit('reconnect'); | ||
|  |         conn.emit('reconnected'); | ||
|  |       } | ||
|  |     }); | ||
|  |   } | ||
|  | 
 | ||
|  |   conn.client.on('timeout', function(err) { | ||
|  |     conn.emit('timeout', err); | ||
|  |   }); | ||
|  |   conn.client.on('parseError', function(err) { | ||
|  |     conn.emit('parseError', err); | ||
|  |   }); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Closes the connection | ||
|  |  * | ||
|  |  * @param {Boolean} [force] | ||
|  |  * @param {Function} [fn] | ||
|  |  * @return {Connection} this | ||
|  |  * @api private | ||
|  |  */ | ||
|  | 
 | ||
|  | NativeConnection.prototype.doClose = function(force, fn) { | ||
|  |   if (this.client == null) { | ||
|  |     immediate(() => fn()); | ||
|  |     return this; | ||
|  |   } | ||
|  | 
 | ||
|  |   this.client.close(force, (err, res) => { | ||
|  |     // Defer because the driver will wait at least 1ms before finishing closing
 | ||
|  |     // the pool, see https://github.com/mongodb-js/mongodb-core/blob/a8f8e4ce41936babc3b9112bf42d609779f03b39/lib/connection/pool.js#L1026-L1030.
 | ||
|  |     // If there's queued operations, you may still get some background work
 | ||
|  |     // after the callback is called.
 | ||
|  |     setTimeout(() => fn(err, res), 1); | ||
|  |   }); | ||
|  |   return this; | ||
|  | }; | ||
|  | 
 | ||
|  | /*! | ||
|  |  * Module exports. | ||
|  |  */ | ||
|  | 
 | ||
|  | module.exports = NativeConnection; |