connection_pool.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.ConnectionPool = exports.PoolState = void 0;
  4. const timers_1 = require("timers");
  5. const constants_1 = require("../constants");
  6. const error_1 = require("../error");
  7. const mongo_types_1 = require("../mongo_types");
  8. const utils_1 = require("../utils");
  9. const connect_1 = require("./connect");
  10. const connection_1 = require("./connection");
  11. const connection_pool_events_1 = require("./connection_pool_events");
  12. const errors_1 = require("./errors");
  13. const metrics_1 = require("./metrics");
  14. /** @internal */
  15. const kServer = Symbol('server');
  16. /** @internal */
  17. const kConnections = Symbol('connections');
  18. /** @internal */
  19. const kPending = Symbol('pending');
  20. /** @internal */
  21. const kCheckedOut = Symbol('checkedOut');
  22. /** @internal */
  23. const kMinPoolSizeTimer = Symbol('minPoolSizeTimer');
  24. /** @internal */
  25. const kGeneration = Symbol('generation');
  26. /** @internal */
  27. const kServiceGenerations = Symbol('serviceGenerations');
  28. /** @internal */
  29. const kConnectionCounter = Symbol('connectionCounter');
  30. /** @internal */
  31. const kCancellationToken = Symbol('cancellationToken');
  32. /** @internal */
  33. const kWaitQueue = Symbol('waitQueue');
  34. /** @internal */
  35. const kCancelled = Symbol('cancelled');
  36. /** @internal */
  37. const kMetrics = Symbol('metrics');
  38. /** @internal */
  39. const kProcessingWaitQueue = Symbol('processingWaitQueue');
  40. /** @internal */
  41. const kPoolState = Symbol('poolState');
  42. /** @internal */
  43. exports.PoolState = Object.freeze({
  44. paused: 'paused',
  45. ready: 'ready',
  46. closed: 'closed'
  47. });
  48. /**
  49. * A pool of connections which dynamically resizes, and emit events related to pool activity
  50. * @internal
  51. */
  52. class ConnectionPool extends mongo_types_1.TypedEventEmitter {
  53. constructor(server, options) {
  54. super();
  55. this.options = Object.freeze({
  56. ...options,
  57. connectionType: connection_1.Connection,
  58. maxPoolSize: options.maxPoolSize ?? 100,
  59. minPoolSize: options.minPoolSize ?? 0,
  60. maxConnecting: options.maxConnecting ?? 2,
  61. maxIdleTimeMS: options.maxIdleTimeMS ?? 0,
  62. waitQueueTimeoutMS: options.waitQueueTimeoutMS ?? 0,
  63. minPoolSizeCheckFrequencyMS: options.minPoolSizeCheckFrequencyMS ?? 100,
  64. autoEncrypter: options.autoEncrypter,
  65. metadata: options.metadata
  66. });
  67. if (this.options.minPoolSize > this.options.maxPoolSize) {
  68. throw new error_1.MongoInvalidArgumentError('Connection pool minimum size must not be greater than maximum pool size');
  69. }
  70. this[kPoolState] = exports.PoolState.paused;
  71. this[kServer] = server;
  72. this[kConnections] = new utils_1.List();
  73. this[kPending] = 0;
  74. this[kCheckedOut] = new Set();
  75. this[kMinPoolSizeTimer] = undefined;
  76. this[kGeneration] = 0;
  77. this[kServiceGenerations] = new Map();
  78. this[kConnectionCounter] = (0, utils_1.makeCounter)(1);
  79. this[kCancellationToken] = new mongo_types_1.CancellationToken();
  80. this[kCancellationToken].setMaxListeners(Infinity);
  81. this[kWaitQueue] = new utils_1.List();
  82. this[kMetrics] = new metrics_1.ConnectionPoolMetrics();
  83. this[kProcessingWaitQueue] = false;
  84. this.mongoLogger = this[kServer].topology.client.mongoLogger;
  85. this.component = 'connection';
  86. process.nextTick(() => {
  87. this.emitAndLog(ConnectionPool.CONNECTION_POOL_CREATED, new connection_pool_events_1.ConnectionPoolCreatedEvent(this));
  88. });
  89. }
  90. /** The address of the endpoint the pool is connected to */
  91. get address() {
  92. return this.options.hostAddress.toString();
  93. }
  94. /**
  95. * Check if the pool has been closed
  96. *
  97. * TODO(NODE-3263): We can remove this property once shell no longer needs it
  98. */
  99. get closed() {
  100. return this[kPoolState] === exports.PoolState.closed;
  101. }
  102. /** An integer representing the SDAM generation of the pool */
  103. get generation() {
  104. return this[kGeneration];
  105. }
  106. /** An integer expressing how many total connections (available + pending + in use) the pool currently has */
  107. get totalConnectionCount() {
  108. return (this.availableConnectionCount + this.pendingConnectionCount + this.currentCheckedOutCount);
  109. }
  110. /** An integer expressing how many connections are currently available in the pool. */
  111. get availableConnectionCount() {
  112. return this[kConnections].length;
  113. }
  114. get pendingConnectionCount() {
  115. return this[kPending];
  116. }
  117. get currentCheckedOutCount() {
  118. return this[kCheckedOut].size;
  119. }
  120. get waitQueueSize() {
  121. return this[kWaitQueue].length;
  122. }
  123. get loadBalanced() {
  124. return this.options.loadBalanced;
  125. }
  126. get serviceGenerations() {
  127. return this[kServiceGenerations];
  128. }
  129. get serverError() {
  130. return this[kServer].description.error;
  131. }
  132. /**
  133. * This is exposed ONLY for use in mongosh, to enable
  134. * killing all connections if a user quits the shell with
  135. * operations in progress.
  136. *
  137. * This property may be removed as a part of NODE-3263.
  138. */
  139. get checkedOutConnections() {
  140. return this[kCheckedOut];
  141. }
  142. /**
  143. * Get the metrics information for the pool when a wait queue timeout occurs.
  144. */
  145. waitQueueErrorMetrics() {
  146. return this[kMetrics].info(this.options.maxPoolSize);
  147. }
  148. /**
  149. * Set the pool state to "ready"
  150. */
  151. ready() {
  152. if (this[kPoolState] !== exports.PoolState.paused) {
  153. return;
  154. }
  155. this[kPoolState] = exports.PoolState.ready;
  156. this.emitAndLog(ConnectionPool.CONNECTION_POOL_READY, new connection_pool_events_1.ConnectionPoolReadyEvent(this));
  157. (0, timers_1.clearTimeout)(this[kMinPoolSizeTimer]);
  158. this.ensureMinPoolSize();
  159. }
  160. /**
  161. * Check a connection out of this pool. The connection will continue to be tracked, but no reference to it
  162. * will be held by the pool. This means that if a connection is checked out it MUST be checked back in or
  163. * explicitly destroyed by the new owner.
  164. */
  165. checkOut(callback) {
  166. this.emitAndLog(ConnectionPool.CONNECTION_CHECK_OUT_STARTED, new connection_pool_events_1.ConnectionCheckOutStartedEvent(this));
  167. const waitQueueMember = { callback };
  168. const waitQueueTimeoutMS = this.options.waitQueueTimeoutMS;
  169. if (waitQueueTimeoutMS) {
  170. waitQueueMember.timer = (0, timers_1.setTimeout)(() => {
  171. waitQueueMember[kCancelled] = true;
  172. waitQueueMember.timer = undefined;
  173. this.emitAndLog(ConnectionPool.CONNECTION_CHECK_OUT_FAILED, new connection_pool_events_1.ConnectionCheckOutFailedEvent(this, 'timeout'));
  174. waitQueueMember.callback(new errors_1.WaitQueueTimeoutError(this.loadBalanced
  175. ? this.waitQueueErrorMetrics()
  176. : 'Timed out while checking out a connection from connection pool', this.address));
  177. }, waitQueueTimeoutMS);
  178. }
  179. this[kWaitQueue].push(waitQueueMember);
  180. process.nextTick(() => this.processWaitQueue());
  181. }
  182. /**
  183. * Check a connection into the pool.
  184. *
  185. * @param connection - The connection to check in
  186. */
  187. checkIn(connection) {
  188. if (!this[kCheckedOut].has(connection)) {
  189. return;
  190. }
  191. const poolClosed = this.closed;
  192. const stale = this.connectionIsStale(connection);
  193. const willDestroy = !!(poolClosed || stale || connection.closed);
  194. if (!willDestroy) {
  195. connection.markAvailable();
  196. this[kConnections].unshift(connection);
  197. }
  198. this[kCheckedOut].delete(connection);
  199. this.emitAndLog(ConnectionPool.CONNECTION_CHECKED_IN, new connection_pool_events_1.ConnectionCheckedInEvent(this, connection));
  200. if (willDestroy) {
  201. const reason = connection.closed ? 'error' : poolClosed ? 'poolClosed' : 'stale';
  202. this.destroyConnection(connection, reason);
  203. }
  204. process.nextTick(() => this.processWaitQueue());
  205. }
  206. /**
  207. * Clear the pool
  208. *
  209. * Pool reset is handled by incrementing the pool's generation count. Any existing connection of a
  210. * previous generation will eventually be pruned during subsequent checkouts.
  211. */
  212. clear(options = {}) {
  213. if (this.closed) {
  214. return;
  215. }
  216. // handle load balanced case
  217. if (this.loadBalanced) {
  218. const { serviceId } = options;
  219. if (!serviceId) {
  220. throw new error_1.MongoRuntimeError('ConnectionPool.clear() called in load balanced mode with no serviceId.');
  221. }
  222. const sid = serviceId.toHexString();
  223. const generation = this.serviceGenerations.get(sid);
  224. // Only need to worry if the generation exists, since it should
  225. // always be there but typescript needs the check.
  226. if (generation == null) {
  227. throw new error_1.MongoRuntimeError('Service generations are required in load balancer mode.');
  228. }
  229. else {
  230. // Increment the generation for the service id.
  231. this.serviceGenerations.set(sid, generation + 1);
  232. }
  233. this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLEARED, new connection_pool_events_1.ConnectionPoolClearedEvent(this, { serviceId }));
  234. return;
  235. }
  236. // handle non load-balanced case
  237. const interruptInUseConnections = options.interruptInUseConnections ?? false;
  238. const oldGeneration = this[kGeneration];
  239. this[kGeneration] += 1;
  240. const alreadyPaused = this[kPoolState] === exports.PoolState.paused;
  241. this[kPoolState] = exports.PoolState.paused;
  242. this.clearMinPoolSizeTimer();
  243. if (!alreadyPaused) {
  244. this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLEARED, new connection_pool_events_1.ConnectionPoolClearedEvent(this, {
  245. interruptInUseConnections
  246. }));
  247. }
  248. if (interruptInUseConnections) {
  249. process.nextTick(() => this.interruptInUseConnections(oldGeneration));
  250. }
  251. this.processWaitQueue();
  252. }
  253. /**
  254. * Closes all stale in-use connections in the pool with a resumable PoolClearedOnNetworkError.
  255. *
  256. * Only connections where `connection.generation <= minGeneration` are killed.
  257. */
  258. interruptInUseConnections(minGeneration) {
  259. for (const connection of this[kCheckedOut]) {
  260. if (connection.generation <= minGeneration) {
  261. this.checkIn(connection);
  262. connection.onError(new errors_1.PoolClearedOnNetworkError(this));
  263. }
  264. }
  265. }
  266. close(_options, _cb) {
  267. let options = _options;
  268. const callback = (_cb ?? _options);
  269. if (typeof options === 'function') {
  270. options = {};
  271. }
  272. options = Object.assign({ force: false }, options);
  273. if (this.closed) {
  274. return callback();
  275. }
  276. // immediately cancel any in-flight connections
  277. this[kCancellationToken].emit('cancel');
  278. // end the connection counter
  279. if (typeof this[kConnectionCounter].return === 'function') {
  280. this[kConnectionCounter].return(undefined);
  281. }
  282. this[kPoolState] = exports.PoolState.closed;
  283. this.clearMinPoolSizeTimer();
  284. this.processWaitQueue();
  285. (0, utils_1.eachAsync)(this[kConnections].toArray(), (conn, cb) => {
  286. this.emitAndLog(ConnectionPool.CONNECTION_CLOSED, new connection_pool_events_1.ConnectionClosedEvent(this, conn, 'poolClosed'));
  287. conn.destroy({ force: !!options.force }, cb);
  288. }, err => {
  289. this[kConnections].clear();
  290. this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new connection_pool_events_1.ConnectionPoolClosedEvent(this));
  291. callback(err);
  292. });
  293. }
  294. /**
  295. * Runs a lambda with an implicitly checked out connection, checking that connection back in when the lambda
  296. * has completed by calling back.
  297. *
  298. * NOTE: please note the required signature of `fn`
  299. *
  300. * @remarks When in load balancer mode, connections can be pinned to cursors or transactions.
  301. * In these cases we pass the connection in to this method to ensure it is used and a new
  302. * connection is not checked out.
  303. *
  304. * @param conn - A pinned connection for use in load balancing mode.
  305. * @param fn - A function which operates on a managed connection
  306. * @param callback - The original callback
  307. */
  308. withConnection(conn, fn, callback) {
  309. if (conn) {
  310. // use the provided connection, and do _not_ check it in after execution
  311. fn(undefined, conn, (fnErr, result) => {
  312. if (fnErr) {
  313. return this.withReauthentication(fnErr, conn, fn, callback);
  314. }
  315. callback(undefined, result);
  316. });
  317. return;
  318. }
  319. this.checkOut((err, conn) => {
  320. // don't callback with `err` here, we might want to act upon it inside `fn`
  321. fn(err, conn, (fnErr, result) => {
  322. if (fnErr) {
  323. if (conn) {
  324. this.withReauthentication(fnErr, conn, fn, callback);
  325. }
  326. else {
  327. callback(fnErr);
  328. }
  329. }
  330. else {
  331. callback(undefined, result);
  332. }
  333. if (conn) {
  334. this.checkIn(conn);
  335. }
  336. });
  337. });
  338. }
  339. withReauthentication(fnErr, conn, fn, callback) {
  340. if (fnErr instanceof error_1.MongoError && fnErr.code === error_1.MONGODB_ERROR_CODES.Reauthenticate) {
  341. this.reauthenticate(conn, fn, (error, res) => {
  342. if (error) {
  343. return callback(error);
  344. }
  345. callback(undefined, res);
  346. });
  347. }
  348. else {
  349. callback(fnErr);
  350. }
  351. }
  352. /**
  353. * Reauthenticate on the same connection and then retry the operation.
  354. */
  355. reauthenticate(connection, fn, callback) {
  356. const authContext = connection.authContext;
  357. if (!authContext) {
  358. return callback(new error_1.MongoRuntimeError('No auth context found on connection.'));
  359. }
  360. const credentials = authContext.credentials;
  361. if (!credentials) {
  362. return callback(new error_1.MongoMissingCredentialsError('Connection is missing credentials when asked to reauthenticate'));
  363. }
  364. const resolvedCredentials = credentials.resolveAuthMechanism(connection.hello || undefined);
  365. const provider = connect_1.AUTH_PROVIDERS.get(resolvedCredentials.mechanism);
  366. if (!provider) {
  367. return callback(new error_1.MongoMissingCredentialsError(`Reauthenticate failed due to no auth provider for ${credentials.mechanism}`));
  368. }
  369. provider.reauth(authContext).then(() => {
  370. fn(undefined, connection, (fnErr, fnResult) => {
  371. if (fnErr) {
  372. return callback(fnErr);
  373. }
  374. callback(undefined, fnResult);
  375. });
  376. }, error => callback(error));
  377. }
  378. /** Clear the min pool size timer */
  379. clearMinPoolSizeTimer() {
  380. const minPoolSizeTimer = this[kMinPoolSizeTimer];
  381. if (minPoolSizeTimer) {
  382. (0, timers_1.clearTimeout)(minPoolSizeTimer);
  383. }
  384. }
  385. destroyConnection(connection, reason) {
  386. this.emitAndLog(ConnectionPool.CONNECTION_CLOSED, new connection_pool_events_1.ConnectionClosedEvent(this, connection, reason));
  387. // destroy the connection
  388. process.nextTick(() => connection.destroy({ force: false }));
  389. }
  390. connectionIsStale(connection) {
  391. const serviceId = connection.serviceId;
  392. if (this.loadBalanced && serviceId) {
  393. const sid = serviceId.toHexString();
  394. const generation = this.serviceGenerations.get(sid);
  395. return connection.generation !== generation;
  396. }
  397. return connection.generation !== this[kGeneration];
  398. }
  399. connectionIsIdle(connection) {
  400. return !!(this.options.maxIdleTimeMS && connection.idleTime > this.options.maxIdleTimeMS);
  401. }
  402. /**
  403. * Destroys a connection if the connection is perished.
  404. *
  405. * @returns `true` if the connection was destroyed, `false` otherwise.
  406. */
  407. destroyConnectionIfPerished(connection) {
  408. const isStale = this.connectionIsStale(connection);
  409. const isIdle = this.connectionIsIdle(connection);
  410. if (!isStale && !isIdle && !connection.closed) {
  411. return false;
  412. }
  413. const reason = connection.closed ? 'error' : isStale ? 'stale' : 'idle';
  414. this.destroyConnection(connection, reason);
  415. return true;
  416. }
  417. createConnection(callback) {
  418. const connectOptions = {
  419. ...this.options,
  420. id: this[kConnectionCounter].next().value,
  421. generation: this[kGeneration],
  422. cancellationToken: this[kCancellationToken]
  423. };
  424. this[kPending]++;
  425. // This is our version of a "virtual" no-I/O connection as the spec requires
  426. this.emitAndLog(ConnectionPool.CONNECTION_CREATED, new connection_pool_events_1.ConnectionCreatedEvent(this, { id: connectOptions.id }));
  427. (0, connect_1.connect)(connectOptions, (err, connection) => {
  428. if (err || !connection) {
  429. this[kPending]--;
  430. this.emitAndLog(ConnectionPool.CONNECTION_CLOSED, new connection_pool_events_1.ConnectionClosedEvent(this, { id: connectOptions.id, serviceId: undefined }, 'error',
  431. // TODO(NODE-5192): Remove this cast
  432. err));
  433. if (err instanceof error_1.MongoNetworkError || err instanceof error_1.MongoServerError) {
  434. err.connectionGeneration = connectOptions.generation;
  435. }
  436. callback(err ?? new error_1.MongoRuntimeError('Connection creation failed without error'));
  437. return;
  438. }
  439. // The pool might have closed since we started trying to create a connection
  440. if (this[kPoolState] !== exports.PoolState.ready) {
  441. this[kPending]--;
  442. connection.destroy({ force: true });
  443. callback(this.closed ? new errors_1.PoolClosedError(this) : new errors_1.PoolClearedError(this));
  444. return;
  445. }
  446. // forward all events from the connection to the pool
  447. for (const event of [...constants_1.APM_EVENTS, connection_1.Connection.CLUSTER_TIME_RECEIVED]) {
  448. connection.on(event, (e) => this.emit(event, e));
  449. }
  450. if (this.loadBalanced) {
  451. connection.on(connection_1.Connection.PINNED, pinType => this[kMetrics].markPinned(pinType));
  452. connection.on(connection_1.Connection.UNPINNED, pinType => this[kMetrics].markUnpinned(pinType));
  453. const serviceId = connection.serviceId;
  454. if (serviceId) {
  455. let generation;
  456. const sid = serviceId.toHexString();
  457. if ((generation = this.serviceGenerations.get(sid))) {
  458. connection.generation = generation;
  459. }
  460. else {
  461. this.serviceGenerations.set(sid, 0);
  462. connection.generation = 0;
  463. }
  464. }
  465. }
  466. connection.markAvailable();
  467. this.emitAndLog(ConnectionPool.CONNECTION_READY, new connection_pool_events_1.ConnectionReadyEvent(this, connection));
  468. this[kPending]--;
  469. callback(undefined, connection);
  470. return;
  471. });
  472. }
  473. ensureMinPoolSize() {
  474. const minPoolSize = this.options.minPoolSize;
  475. if (this[kPoolState] !== exports.PoolState.ready || minPoolSize === 0) {
  476. return;
  477. }
  478. this[kConnections].prune(connection => this.destroyConnectionIfPerished(connection));
  479. if (this.totalConnectionCount < minPoolSize &&
  480. this.pendingConnectionCount < this.options.maxConnecting) {
  481. // NOTE: ensureMinPoolSize should not try to get all the pending
  482. // connection permits because that potentially delays the availability of
  483. // the connection to a checkout request
  484. this.createConnection((err, connection) => {
  485. if (err) {
  486. this[kServer].handleError(err);
  487. }
  488. if (!err && connection) {
  489. this[kConnections].push(connection);
  490. process.nextTick(() => this.processWaitQueue());
  491. }
  492. if (this[kPoolState] === exports.PoolState.ready) {
  493. (0, timers_1.clearTimeout)(this[kMinPoolSizeTimer]);
  494. this[kMinPoolSizeTimer] = (0, timers_1.setTimeout)(() => this.ensureMinPoolSize(), this.options.minPoolSizeCheckFrequencyMS);
  495. }
  496. });
  497. }
  498. else {
  499. (0, timers_1.clearTimeout)(this[kMinPoolSizeTimer]);
  500. this[kMinPoolSizeTimer] = (0, timers_1.setTimeout)(() => this.ensureMinPoolSize(), this.options.minPoolSizeCheckFrequencyMS);
  501. }
  502. }
  503. processWaitQueue() {
  504. if (this[kProcessingWaitQueue]) {
  505. return;
  506. }
  507. this[kProcessingWaitQueue] = true;
  508. while (this.waitQueueSize) {
  509. const waitQueueMember = this[kWaitQueue].first();
  510. if (!waitQueueMember) {
  511. this[kWaitQueue].shift();
  512. continue;
  513. }
  514. if (waitQueueMember[kCancelled]) {
  515. this[kWaitQueue].shift();
  516. continue;
  517. }
  518. if (this[kPoolState] !== exports.PoolState.ready) {
  519. const reason = this.closed ? 'poolClosed' : 'connectionError';
  520. const error = this.closed ? new errors_1.PoolClosedError(this) : new errors_1.PoolClearedError(this);
  521. this.emitAndLog(ConnectionPool.CONNECTION_CHECK_OUT_FAILED, new connection_pool_events_1.ConnectionCheckOutFailedEvent(this, reason, error));
  522. if (waitQueueMember.timer) {
  523. (0, timers_1.clearTimeout)(waitQueueMember.timer);
  524. }
  525. this[kWaitQueue].shift();
  526. waitQueueMember.callback(error);
  527. continue;
  528. }
  529. if (!this.availableConnectionCount) {
  530. break;
  531. }
  532. const connection = this[kConnections].shift();
  533. if (!connection) {
  534. break;
  535. }
  536. if (!this.destroyConnectionIfPerished(connection)) {
  537. this[kCheckedOut].add(connection);
  538. this.emitAndLog(ConnectionPool.CONNECTION_CHECKED_OUT, new connection_pool_events_1.ConnectionCheckedOutEvent(this, connection));
  539. if (waitQueueMember.timer) {
  540. (0, timers_1.clearTimeout)(waitQueueMember.timer);
  541. }
  542. this[kWaitQueue].shift();
  543. waitQueueMember.callback(undefined, connection);
  544. }
  545. }
  546. const { maxPoolSize, maxConnecting } = this.options;
  547. while (this.waitQueueSize > 0 &&
  548. this.pendingConnectionCount < maxConnecting &&
  549. (maxPoolSize === 0 || this.totalConnectionCount < maxPoolSize)) {
  550. const waitQueueMember = this[kWaitQueue].shift();
  551. if (!waitQueueMember || waitQueueMember[kCancelled]) {
  552. continue;
  553. }
  554. this.createConnection((err, connection) => {
  555. if (waitQueueMember[kCancelled]) {
  556. if (!err && connection) {
  557. this[kConnections].push(connection);
  558. }
  559. }
  560. else {
  561. if (err) {
  562. this.emitAndLog(ConnectionPool.CONNECTION_CHECK_OUT_FAILED,
  563. // TODO(NODE-5192): Remove this cast
  564. new connection_pool_events_1.ConnectionCheckOutFailedEvent(this, 'connectionError', err));
  565. }
  566. else if (connection) {
  567. this[kCheckedOut].add(connection);
  568. this.emitAndLog(ConnectionPool.CONNECTION_CHECKED_OUT, new connection_pool_events_1.ConnectionCheckedOutEvent(this, connection));
  569. }
  570. if (waitQueueMember.timer) {
  571. (0, timers_1.clearTimeout)(waitQueueMember.timer);
  572. }
  573. waitQueueMember.callback(err, connection);
  574. }
  575. process.nextTick(() => this.processWaitQueue());
  576. });
  577. }
  578. this[kProcessingWaitQueue] = false;
  579. }
  580. }
  581. /**
  582. * Emitted when the connection pool is created.
  583. * @event
  584. */
  585. ConnectionPool.CONNECTION_POOL_CREATED = constants_1.CONNECTION_POOL_CREATED;
  586. /**
  587. * Emitted once when the connection pool is closed
  588. * @event
  589. */
  590. ConnectionPool.CONNECTION_POOL_CLOSED = constants_1.CONNECTION_POOL_CLOSED;
  591. /**
  592. * Emitted each time the connection pool is cleared and it's generation incremented
  593. * @event
  594. */
  595. ConnectionPool.CONNECTION_POOL_CLEARED = constants_1.CONNECTION_POOL_CLEARED;
  596. /**
  597. * Emitted each time the connection pool is marked ready
  598. * @event
  599. */
  600. ConnectionPool.CONNECTION_POOL_READY = constants_1.CONNECTION_POOL_READY;
  601. /**
  602. * Emitted when a connection is created.
  603. * @event
  604. */
  605. ConnectionPool.CONNECTION_CREATED = constants_1.CONNECTION_CREATED;
  606. /**
  607. * Emitted when a connection becomes established, and is ready to use
  608. * @event
  609. */
  610. ConnectionPool.CONNECTION_READY = constants_1.CONNECTION_READY;
  611. /**
  612. * Emitted when a connection is closed
  613. * @event
  614. */
  615. ConnectionPool.CONNECTION_CLOSED = constants_1.CONNECTION_CLOSED;
  616. /**
  617. * Emitted when an attempt to check out a connection begins
  618. * @event
  619. */
  620. ConnectionPool.CONNECTION_CHECK_OUT_STARTED = constants_1.CONNECTION_CHECK_OUT_STARTED;
  621. /**
  622. * Emitted when an attempt to check out a connection fails
  623. * @event
  624. */
  625. ConnectionPool.CONNECTION_CHECK_OUT_FAILED = constants_1.CONNECTION_CHECK_OUT_FAILED;
  626. /**
  627. * Emitted each time a connection is successfully checked out of the connection pool
  628. * @event
  629. */
  630. ConnectionPool.CONNECTION_CHECKED_OUT = constants_1.CONNECTION_CHECKED_OUT;
  631. /**
  632. * Emitted each time a connection is successfully checked into the connection pool
  633. * @event
  634. */
  635. ConnectionPool.CONNECTION_CHECKED_IN = constants_1.CONNECTION_CHECKED_IN;
  636. exports.ConnectionPool = ConnectionPool;
  637. //# sourceMappingURL=connection_pool.js.map