@@ -80,7 +80,7 @@ type Client struct {
8080 clock clockwork.Clock
8181
8282 // read-write fields
83- mu xsync.Mutex
83+ mu xsync.RWMutex
8484 index map [* session ]sessionInfo
8585 createInProgress int // KIKIMR-9163: in-create-process counter
8686 limit int // Upper bound for Client size.
@@ -352,45 +352,53 @@ func (c *Client) internalPoolCreateSession(ctx context.Context) (s *session, err
352352 return s , nil
353353}
354354
355- type getOptions struct {
356- t * trace.Table
355+ type getSettings struct {
356+ t * trace.Table
357+ maxAttempts int
357358}
358359
359- type getOption func (o * getOptions )
360+ type getOption func (o * getSettings )
360361
361362func withTrace (t * trace.Table ) getOption {
362- return func (o * getOptions ) {
363+ return func (o * getSettings ) {
363364 o .t = o .t .Compose (t )
364365 }
365366}
366367
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
368371 if c .isClosed () {
369372 return nil , xerrors .WithStackTrace (errClosedClient )
370373 }
371374
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 }
379376 for _ , opt := range opts {
380377 if opt != nil {
381- opt (& o )
378+ opt (& settings )
382379 }
383380 }
384381
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 ,
386391 stack .FunctionID ("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).internalPoolGet" ),
387392 )
388393 defer func () {
389- onDone (s , i , err )
394+ onDone (s , i , finalErr )
390395 }()
391396
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+
394402 s = tryGetIdleSession (c )
395403 if s != nil {
396404 if ! s .isReady () {
@@ -403,18 +411,36 @@ func (c *Client) internalPoolGet(ctx context.Context, opts ...getOption) (s *ses
403411 return s , nil
404412 }
405413
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
409417 }
410418
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 )
414421 }
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 ))
415433 }
416434
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+ )
418444}
419445
420446func tryGetIdleSession (c * Client ) * session {
@@ -442,38 +468,6 @@ func tryCreateNewSession(ctx context.Context, c *Client) (*session, error) {
442468 return s , err
443469}
444470
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-
477471// Get returns first idle session from the Client and removes it from
478472// there. If no items stored in Client it creates new one returns it.
479473func (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
708702 }
709703
710704 defer func () {
711- err = handleTransactionError (ctx , tx , err )
705+ if err != nil {
706+ _ = tx .Rollback (ctx )
707+ }
712708 }()
713709
714710 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
724720 }, config .RetryOptions ... )
725721}
726722
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-
743723func executeTxOperation (ctx context.Context , c * Client , op table.TxOperation , tx table.Transaction ) (err error ) {
744724 if panicCallback := c .config .PanicCallback (); panicCallback != nil {
745725 defer func () {
0 commit comments