@@ -36,6 +36,7 @@ const thinUtil = require('./util.js');
36
36
const { getConnectionInfo} = require ( './sqlnet/networkSession.js' ) ;
37
37
const crypto = require ( 'crypto' ) ;
38
38
const EventEmitter = require ( 'events' ) ;
39
+ const Timers = require ( 'timers' ) ;
39
40
40
41
class ThinPoolImpl extends PoolImpl {
41
42
@@ -69,6 +70,9 @@ class ThinPoolImpl extends PoolImpl {
69
70
this . _privateKey = params . privateKey ;
70
71
this . _obfuscatedPrivateKey = [ ] ;
71
72
this . _schedulerJob = null ;
73
+ this . _poolCloseWaiter = null ;
74
+ this . _pendingRequests = [ ] ;
75
+
72
76
// password obfuscation
73
77
if ( this . _password !== undefined ) {
74
78
const obj = protocolUtil . setObfuscatedValue ( this . _password ) ;
@@ -121,8 +125,9 @@ class ThinPoolImpl extends PoolImpl {
121
125
this . _generateConnectionClass ( ) ;
122
126
}
123
127
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 ( ) ;
126
131
}
127
132
128
133
//---------------------------------------------------------------------------
@@ -151,15 +156,21 @@ class ThinPoolImpl extends PoolImpl {
151
156
async _getConnAttrs ( ) {
152
157
let accessToken ;
153
158
const clonedAttrs = Object . assign ( { } , this . _userConfig ) ;
159
+ // deobfuscate password
154
160
if ( clonedAttrs . password === null ) {
155
161
clonedAttrs . password = protocolUtil . getDeobfuscatedValue ( this . _password ,
156
162
this . _obfuscatedPassword ) ;
157
163
}
164
+
165
+ // deobfuscate wallet password
158
166
if ( clonedAttrs . walletPassword === null ) {
159
167
clonedAttrs . walletPassword =
160
168
protocolUtil . getDeobfuscatedValue ( this . _walletPassword ,
161
169
this . _obfuscatedWalletPassword ) ;
162
170
}
171
+
172
+ // deobfuscate token and private key
173
+ // check for token expiry
163
174
if ( clonedAttrs . token === null ) {
164
175
clonedAttrs . token =
165
176
protocolUtil . getDeobfuscatedValue ( this . _token , this . _obfuscatedToken ) ;
@@ -230,6 +241,15 @@ class ThinPoolImpl extends PoolImpl {
230
241
//---------------------------------------------------------------------------
231
242
async close ( ) {
232
243
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
+
233
253
// clear scheduled job
234
254
if ( this . _schedulerJob ) {
235
255
clearTimeout ( this . _schedulerJob ) ;
@@ -319,25 +339,6 @@ class ThinPoolImpl extends PoolImpl {
319
339
return this . _stmtCacheSize ;
320
340
}
321
341
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
-
341
342
//---------------------------------------------------------------------------
342
343
// _setScheduler()
343
344
//
@@ -375,6 +376,94 @@ class ThinPoolImpl extends PoolImpl {
375
376
this . _setScheduler ( ) ;
376
377
}
377
378
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
+
378
467
//---------------------------------------------------------------------------
379
468
// acquire()
380
469
//
@@ -422,17 +511,28 @@ class ThinPoolImpl extends PoolImpl {
422
511
423
512
// no free connections exist at this point; if less than poolMin
424
513
// 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 ( ) ;
433
521
}
434
- }
522
+ } ) ;
435
523
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
+ }
436
536
// return a connection from the ones that were just built
437
537
const conn = this . _freeConnectionList . pop ( ) ;
438
538
this . _usedConnectionList . add ( conn ) ;
0 commit comments