@@ -80,7 +80,7 @@ type Client struct {
80
80
clock clockwork.Clock
81
81
82
82
// read-write fields
83
- mu xsync.Mutex
83
+ mu xsync.RWMutex
84
84
index map [* session ]sessionInfo
85
85
createInProgress int // KIKIMR-9163: in-create-process counter
86
86
limit int // Upper bound for Client size.
@@ -352,45 +352,53 @@ func (c *Client) internalPoolCreateSession(ctx context.Context) (s *session, err
352
352
return s , nil
353
353
}
354
354
355
- type getOptions struct {
356
- t * trace.Table
355
+ type getSettings struct {
356
+ t * trace.Table
357
+ maxAttempts int
357
358
}
358
359
359
- type getOption func (o * getOptions )
360
+ type getOption func (o * getSettings )
360
361
361
362
func withTrace (t * trace.Table ) getOption {
362
- return func (o * getOptions ) {
363
+ return func (o * getSettings ) {
363
364
o .t = o .t .Compose (t )
364
365
}
365
366
}
366
367
367
- func (c * Client ) internalPoolGet (ctx context.Context , opts ... getOption ) (s * session , err error ) {
368
+ const maxAttempts = 100
369
+
370
+ func (c * Client ) internalPoolGet (ctx context.Context , opts ... getOption ) (s * session , finalErr error ) { //nolint:funlen
368
371
if c .isClosed () {
369
372
return nil , xerrors .WithStackTrace (errClosedClient )
370
373
}
371
374
372
- const maxAttempts = 100
373
-
374
- var (
375
- start = time .Now ()
376
- i = 0
377
- o = getOptions {t : c .config .Trace ()}
378
- )
375
+ settings := getSettings {t : c .config .Trace (), maxAttempts : maxAttempts }
379
376
for _ , opt := range opts {
380
377
if opt != nil {
381
- opt (& o )
378
+ opt (& settings )
382
379
}
383
380
}
384
381
385
- onDone := trace .TableOnPoolGet (o .t , & ctx ,
382
+ var (
383
+ start = time .Now ()
384
+ i int
385
+ lastErr error
386
+ createSessionErr error
387
+ waitFromChErr error
388
+ )
389
+
390
+ onDone := trace .TableOnPoolGet (settings .t , & ctx ,
386
391
stack .FunctionID ("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).internalPoolGet" ),
387
392
)
388
393
defer func () {
389
- onDone (s , i , err )
394
+ onDone (s , i , finalErr )
390
395
}()
391
396
392
- for s == nil && err == nil && i < maxAttempts && ! c .isClosed () {
393
- i ++
397
+ for ; i < settings .maxAttempts ; i ++ {
398
+ if c .isClosed () {
399
+ return nil , xerrors .WithStackTrace (errClosedClient )
400
+ }
401
+
394
402
s = tryGetIdleSession (c )
395
403
if s != nil {
396
404
if ! s .isReady () {
@@ -403,18 +411,36 @@ func (c *Client) internalPoolGet(ctx context.Context, opts ...getOption) (s *ses
403
411
return s , nil
404
412
}
405
413
406
- s , err = tryCreateNewSession (ctx , c )
407
- if s != nil || ! isCreateSessionErrorRetriable ( err ) {
408
- return s , xerrors . WithStackTrace ( err )
414
+ s , createSessionErr = tryCreateNewSession (ctx , c )
415
+ if s != nil {
416
+ return s , nil
409
417
}
410
418
411
- s , err = c .internalPoolWaitFromCh (ctx , o .t )
412
- if err != nil {
413
- err = xerrors .WithStackTrace (err )
419
+ if ! isCreateSessionErrorRetriable (createSessionErr ) {
420
+ return nil , xerrors .WithStackTrace (createSessionErr )
414
421
}
422
+
423
+ s , waitFromChErr = c .internalPoolWaitFromCh (ctx , settings .t )
424
+ if s != nil {
425
+ return s , nil
426
+ }
427
+
428
+ if waitFromChErr != nil && ! isCreateSessionErrorRetriable (waitFromChErr ) {
429
+ return nil , xerrors .WithStackTrace (waitFromChErr )
430
+ }
431
+
432
+ lastErr = xerrors .WithStackTrace (xerrors .Join (createSessionErr , waitFromChErr ))
415
433
}
416
434
417
- return handleNoProgress (s , err , start , c , i )
435
+ c .mu .RLock ()
436
+ defer c .mu .RUnlock ()
437
+
438
+ return nil , xerrors .WithStackTrace (
439
+ fmt .Errorf ("failed to get session from pool (" +
440
+ "attempts: %d, latency: %v, pool has %d sessions (%d busy, %d idle, %d create_in_progress): %w" ,
441
+ i , time .Since (start ), len (c .index ), len (c .index )- c .idle .Len (), c .idle .Len (), c .createInProgress , lastErr ,
442
+ ),
443
+ )
418
444
}
419
445
420
446
func tryGetIdleSession (c * Client ) * session {
@@ -442,38 +468,6 @@ func tryCreateNewSession(ctx context.Context, c *Client) (*session, error) {
442
468
return s , err
443
469
}
444
470
445
- func handleNoProgress (s * session , err error , start time.Time , c * Client , attempts int ) (* session , error ) {
446
- if s == nil && err == nil {
447
- if c .isClosed () {
448
- err = xerrors .WithStackTrace (errClosedClient )
449
- } else {
450
- err = xerrors .WithStackTrace (errNoProgress )
451
- }
452
- }
453
-
454
- if err != nil {
455
- var (
456
- index int
457
- idle int
458
- createInProgress int
459
- )
460
- c .mu .WithLock (func () {
461
- index = len (c .index )
462
- idle = c .idle .Len ()
463
- createInProgress = c .createInProgress
464
- })
465
-
466
- err = xerrors .WithStackTrace (
467
- fmt .Errorf ("failed to get session from pool (" +
468
- "attempts: %d, latency: %v, pool has %d sessions (%d busy, %d idle, %d create_in_progress): %w" ,
469
- attempts , time .Since (start ), index , index - idle , idle , createInProgress , err ,
470
- ),
471
- )
472
- }
473
-
474
- return s , err
475
- }
476
-
477
471
// Get returns first idle session from the Client and removes it from
478
472
// there. If no items stored in Client it creates new one returns it.
479
473
func (c * Client ) Get (ctx context.Context ) (s * session , err error ) {
@@ -708,7 +702,9 @@ func (c *Client) DoTx(ctx context.Context, op table.TxOperation, opts ...table.O
708
702
}
709
703
710
704
defer func () {
711
- err = handleTransactionError (ctx , tx , err )
705
+ if err != nil {
706
+ _ = tx .Rollback (ctx )
707
+ }
712
708
}()
713
709
714
710
if err = executeTxOperation (ctx , c , op , tx ); err != nil {
@@ -724,22 +720,6 @@ func (c *Client) DoTx(ctx context.Context, op table.TxOperation, opts ...table.O
724
720
}, config .RetryOptions ... )
725
721
}
726
722
727
- func handleTransactionError (ctx context.Context , tx table.Transaction , err error ) error {
728
- if err != nil {
729
- errRollback := tx .Rollback (ctx )
730
- if errRollback != nil {
731
- return xerrors .NewWithIssues ("" ,
732
- xerrors .WithStackTrace (err ),
733
- xerrors .WithStackTrace (errRollback ),
734
- )
735
- }
736
-
737
- return xerrors .WithStackTrace (err )
738
- }
739
-
740
- return nil
741
- }
742
-
743
723
func executeTxOperation (ctx context.Context , c * Client , op table.TxOperation , tx table.Transaction ) (err error ) {
744
724
if panicCallback := c .config .PanicCallback (); panicCallback != nil {
745
725
defer func () {
0 commit comments