@@ -36,6 +36,7 @@ const thinUtil = require('./util.js');
3636const { getConnectionInfo} = require ( './sqlnet/networkSession.js' ) ;
3737const crypto = require ( 'crypto' ) ;
3838const EventEmitter = require ( 'events' ) ;
39+ const Timers = require ( 'timers' ) ;
3940
4041class ThinPoolImpl extends PoolImpl {
4142
@@ -69,6 +70,9 @@ class ThinPoolImpl extends PoolImpl {
6970 this . _privateKey = params . privateKey ;
7071 this . _obfuscatedPrivateKey = [ ] ;
7172 this . _schedulerJob = null ;
73+ this . _poolCloseWaiter = null ;
74+ this . _pendingRequests = [ ] ;
75+
7276 // password obfuscation
7377 if ( this . _password !== undefined ) {
7478 const obj = protocolUtil . setObfuscatedValue ( this . _password ) ;
@@ -121,8 +125,9 @@ class ThinPoolImpl extends PoolImpl {
121125 this . _generateConnectionClass ( ) ;
122126 }
123127
124- // create minimum connections
125- await this . _growPool ( this . _poolMin ) ;
128+ // create a background task. It will create minimum connections in the pool
129+ // and expand the pool as required.
130+ this . bgThreadFunc ( ) ;
126131 }
127132
128133 //---------------------------------------------------------------------------
@@ -151,15 +156,21 @@ class ThinPoolImpl extends PoolImpl {
151156 async _getConnAttrs ( ) {
152157 let accessToken ;
153158 const clonedAttrs = Object . assign ( { } , this . _userConfig ) ;
159+ // deobfuscate password
154160 if ( clonedAttrs . password === null ) {
155161 clonedAttrs . password = protocolUtil . getDeobfuscatedValue ( this . _password ,
156162 this . _obfuscatedPassword ) ;
157163 }
164+
165+ // deobfuscate wallet password
158166 if ( clonedAttrs . walletPassword === null ) {
159167 clonedAttrs . walletPassword =
160168 protocolUtil . getDeobfuscatedValue ( this . _walletPassword ,
161169 this . _obfuscatedWalletPassword ) ;
162170 }
171+
172+ // deobfuscate token and private key
173+ // check for token expiry
163174 if ( clonedAttrs . token === null ) {
164175 clonedAttrs . token =
165176 protocolUtil . getDeobfuscatedValue ( this . _token , this . _obfuscatedToken ) ;
@@ -230,6 +241,15 @@ class ThinPoolImpl extends PoolImpl {
230241 //---------------------------------------------------------------------------
231242 async close ( ) {
232243
244+ // wait till background task for pool expansion is finished; if it is not
245+ // currently running, wake it up!
246+ await new Promise ( ( resolve ) => {
247+ this . _poolCloseWaiter = resolve ;
248+ if ( this . bgWaiter ) {
249+ this . bgWaiter ( ) ;
250+ }
251+ } ) ;
252+
233253 // clear scheduled job
234254 if ( this . _schedulerJob ) {
235255 clearTimeout ( this . _schedulerJob ) ;
@@ -319,25 +339,6 @@ class ThinPoolImpl extends PoolImpl {
319339 return this . _stmtCacheSize ;
320340 }
321341
322- //---------------------------------------------------------------------------
323- // _growPool()
324- //
325- // Grows the pool to include the specified number of connections.
326- //---------------------------------------------------------------------------
327- async _growPool ( numConns ) {
328- const clonedAttrs = await this . _getConnAttrs ( ) ;
329- while ( numConns > 0 ) {
330- const conn = new ThinConnectionImpl ( ) ;
331- conn . _pool = this ;
332- await conn . connect ( clonedAttrs ) ;
333- conn . _newSession = true ;
334- conn . _dropSess = false ;
335- conn . _lastTimeUsed = Date . now ( ) ;
336- this . _freeConnectionList . push ( conn ) ;
337- numConns -- ;
338- }
339- }
340-
341342 //---------------------------------------------------------------------------
342343 // _setScheduler()
343344 //
@@ -375,6 +376,94 @@ class ThinPoolImpl extends PoolImpl {
375376 this . _setScheduler ( ) ;
376377 }
377378
379+ //---------------------------------------------------------------------------
380+ // _getNumConns()
381+ //
382+ // get number of connections need to be created
383+ //---------------------------------------------------------------------------
384+ _getNumConns ( ) {
385+ const usedConns = this . _freeConnectionList . length + this . _usedConnectionList . size ;
386+ // less connections in the pool than poolMin? restore to poolMin
387+ if ( usedConns < this . _poolMin ) {
388+ return this . _poolMin - usedConns ;
389+ // connections need to be created? create up to poolIncrement without exceeding poolMax
390+ } else if ( this . _pendingRequests . length > 0 ) {
391+ return Math . min ( this . _poolIncrement , this . _poolMax - usedConns ) ;
392+ // no pending requests and we are already at poolMin so nothing to do!
393+ } else {
394+ return 0 ;
395+ }
396+ }
397+
398+ //---------------------------------------------------------------------------
399+ // bgThreadFunc()
400+ //
401+ // method which runs in a background thread and is used to create connections.
402+ // When first started, it creates poolMin connections. After that, it creates
403+ // poolIncrement connections up to the value of poolMax when needed.
404+ // The thread terminates automatically when the pool is closed.
405+ //---------------------------------------------------------------------------
406+ async bgThreadFunc ( ) {
407+
408+ // continue until a close request is received
409+ while ( ! this . _poolCloseWaiter ) {
410+ // get count for connections to be created
411+ const numConns = this . _getNumConns ( ) ;
412+
413+ // connection creation is going on serially and not concurrently
414+ for ( let i = 0 ; i < numConns ; i ++ ) {
415+ try {
416+ // get deobfuscated value
417+ const config = await this . _getConnAttrs ( ) ;
418+ const conn = new ThinConnectionImpl ( ) ;
419+ conn . _pool = this ;
420+ await conn . connect ( config ) ;
421+ conn . _newSession = true ;
422+ conn . _dropSess = false ;
423+ conn . _lastTimeUsed = Date . now ( ) ;
424+ this . _freeConnectionList . push ( conn ) ;
425+ } catch ( err ) {
426+ this . _bgErr = err ;
427+ }
428+
429+ if ( this . _poolIncrement > 1 && ( this . _poolMax - this . _usedConnectionList . size
430+ - this . _freeConnectionList . length ) > 1 ) {
431+ this . _setScheduler ( ) ;
432+ }
433+
434+ // resolve pending request
435+ if ( this . _pendingRequests . length > 0 ) {
436+ const payload = this . _pendingRequests . shift ( ) ;
437+ payload . resolve ( ) ;
438+ }
439+
440+ // give an opportunity for other "threads" to do their work.
441+ await new Promise ( ( resolve ) => Timers . setImmediate ( resolve ) ) ;
442+
443+ // break loop when pool is closing
444+ if ( this . _poolCloseWaiter ) {
445+ break ;
446+ }
447+ }
448+
449+ // when pool is closing, break from while loop
450+ if ( this . _poolCloseWaiter ) {
451+ break ;
452+ }
453+
454+ // if no pending requests, wait for pending requests to appear!
455+ if ( this . _pendingRequests . length == 0 || this . _bgErr ) {
456+ await new Promise ( ( resolve ) => {
457+ this . bgWaiter = resolve ;
458+ } ) ;
459+ this . bgWaiter = null ;
460+ }
461+ }
462+
463+ // notify the closer that the close can actually take place
464+ this . _poolCloseWaiter ( ) ;
465+ }
466+
378467 //---------------------------------------------------------------------------
379468 // acquire()
380469 //
@@ -422,17 +511,28 @@ class ThinPoolImpl extends PoolImpl {
422511
423512 // no free connections exist at this point; if less than poolMin
424513 // connections exist, grow the pool to poolMin again; otherwise, increase
425- // the pool by poolIncrement up to poolMax
426- if ( this . _usedConnectionList . size < this . _poolMin ) {
427- await this . _growPool ( this . _poolMin - this . _usedConnectionList . size ) ;
428- } else {
429- const sizeAvailable = this . _poolMax - this . _usedConnectionList . size ;
430- await this . _growPool ( Math . min ( this . _poolIncrement , sizeAvailable ) ) ;
431- if ( this . _poolIncrement > 1 && sizeAvailable > 1 ) {
432- this . _setScheduler ( ) ;
514+ // the pool by poolIncrement up to poolMax. We are deferring this
515+ // to the background thread function!
516+ await new Promise ( ( resolve ) => {
517+ this . _pendingRequests . push ( { resolve : resolve } ) ;
518+ if ( this . bgWaiter ) {
519+ // this wakes up the function to do some more work
520+ this . bgWaiter ( ) ;
433521 }
434- }
522+ } ) ;
435523
524+ if ( this . _bgErr ) {
525+ const err = this . _bgErr ;
526+ this . _bgErr = null ;
527+
528+ // if an error has occurred in the background thread we clear it and then,
529+ // if there are more pending requests we request the background thread
530+ // function to try again.
531+ if ( this . _pendingRequests . length > 0 && this . bgWaiter ) {
532+ this . bgWaiter ( ) ;
533+ }
534+ throw err ;
535+ }
436536 // return a connection from the ones that were just built
437537 const conn = this . _freeConnectionList . pop ( ) ;
438538 this . _usedConnectionList . add ( conn ) ;
0 commit comments