github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/table/session.go (about)

     1  package table
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"strconv"
     8  	"sync"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/ydb-platform/ydb-go-genproto/Ydb_Table_V1"
    13  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    14  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Table"
    15  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/metadata"
    18  
    19  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
    20  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/conn"
    21  	balancerContext "github.com/ydb-platform/ydb-go-sdk/v3/internal/endpoint"
    22  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/feature"
    23  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/meta"
    24  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
    25  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
    26  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
    27  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
    28  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner"
    29  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/tx"
    30  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
    31  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
    32  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
    33  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    34  	"github.com/ydb-platform/ydb-go-sdk/v3/retry"
    35  	"github.com/ydb-platform/ydb-go-sdk/v3/table"
    36  	"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
    37  	"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
    38  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    39  )
    40  
    41  // session represents a single table API session.
    42  //
    43  // session methods are not goroutine safe. Simultaneous execution of requests
    44  // are forbidden within a single session.
    45  //
    46  // Note that after session is no longer needed it should be destroyed by
    47  // Close() call.
    48  type session struct {
    49  	onClose      []func(s *session)
    50  	id           string
    51  	tableService Ydb_Table_V1.TableServiceClient
    52  	status       table.SessionStatus
    53  	config       *config.Config
    54  	lastUsage    atomic.Int64
    55  	statusMtx    sync.RWMutex
    56  	closeOnce    sync.Once
    57  	nodeID       atomic.Uint32
    58  }
    59  
    60  func (s *session) IsAlive() bool {
    61  	return s.Status() == table.SessionReady
    62  }
    63  
    64  func (s *session) LastUsage() time.Time {
    65  	return time.Unix(s.lastUsage.Load(), 0)
    66  }
    67  
    68  func nodeID(sessionID string) (uint32, error) {
    69  	u, err := url.Parse(sessionID)
    70  	if err != nil {
    71  		return 0, err
    72  	}
    73  	id, err := strconv.ParseUint(u.Query().Get("node_id"), 10, 32)
    74  	if err != nil {
    75  		return 0, err
    76  	}
    77  
    78  	return uint32(id), err
    79  }
    80  
    81  func (s *session) NodeID() uint32 {
    82  	if s == nil {
    83  		return 0
    84  	}
    85  	if id := s.nodeID.Load(); id != 0 {
    86  		return id
    87  	}
    88  	id, err := nodeID(s.id)
    89  	if err != nil {
    90  		return 0
    91  	}
    92  	s.nodeID.Store(id)
    93  
    94  	return id
    95  }
    96  
    97  func (s *session) Status() table.SessionStatus {
    98  	if s == nil {
    99  		return table.SessionStatusUnknown
   100  	}
   101  	s.statusMtx.RLock()
   102  	defer s.statusMtx.RUnlock()
   103  
   104  	return s.status
   105  }
   106  
   107  func (s *session) SetStatus(status table.SessionStatus) {
   108  	s.statusMtx.Lock()
   109  	defer s.statusMtx.Unlock()
   110  	s.status = status
   111  }
   112  
   113  func (s *session) isClosed() bool {
   114  	return s.Status() == table.SessionClosed
   115  }
   116  
   117  func newSession(ctx context.Context, cc grpc.ClientConnInterface, config *config.Config) (
   118  	s *session, err error,
   119  ) {
   120  	onDone := trace.TableOnSessionNew(config.Trace(), &ctx,
   121  		stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.newSession"),
   122  	)
   123  	defer func() {
   124  		onDone(s, err)
   125  	}()
   126  	var (
   127  		response *Ydb_Table.CreateSessionResponse
   128  		result   Ydb_Table.CreateSessionResult
   129  		c        = Ydb_Table_V1.NewTableServiceClient(cc)
   130  	)
   131  	response, err = c.CreateSession(ctx,
   132  		&Ydb_Table.CreateSessionRequest{
   133  			OperationParams: operation.Params(
   134  				ctx,
   135  				config.OperationTimeout(),
   136  				config.OperationCancelAfter(),
   137  				operation.ModeSync,
   138  			),
   139  		},
   140  	)
   141  	if err != nil {
   142  		return nil, xerrors.WithStackTrace(err)
   143  	}
   144  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   145  	if err != nil {
   146  		return nil, xerrors.WithStackTrace(err)
   147  	}
   148  
   149  	s = &session{
   150  		id:     result.GetSessionId(),
   151  		config: config,
   152  		status: table.SessionReady,
   153  	}
   154  	s.lastUsage.Store(time.Now().Unix())
   155  
   156  	s.tableService = Ydb_Table_V1.NewTableServiceClient(
   157  		conn.WithBeforeFunc(
   158  			conn.WithContextModifier(cc, func(ctx context.Context) context.Context {
   159  				return meta.WithTrailerCallback(balancerContext.WithNodeID(ctx, s.NodeID()), s.checkCloseHint)
   160  			}),
   161  			func() {
   162  				s.lastUsage.Store(time.Now().Unix())
   163  			},
   164  		),
   165  	)
   166  
   167  	return s, nil
   168  }
   169  
   170  func (s *session) ID() string {
   171  	if s == nil {
   172  		return ""
   173  	}
   174  
   175  	return s.id
   176  }
   177  
   178  func (s *session) Close(ctx context.Context) (err error) {
   179  	if s.isClosed() {
   180  		return xerrors.WithStackTrace(errSessionClosed)
   181  	}
   182  
   183  	s.closeOnce.Do(func() {
   184  		onDone := trace.TableOnSessionDelete(s.config.Trace(), &ctx,
   185  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).Close"),
   186  			s,
   187  		)
   188  		defer func() {
   189  			s.SetStatus(table.SessionClosed)
   190  			onDone(err)
   191  		}()
   192  
   193  		if time.Since(s.LastUsage()) < s.config.IdleThreshold() {
   194  			_, err = s.tableService.DeleteSession(ctx,
   195  				&Ydb_Table.DeleteSessionRequest{
   196  					SessionId: s.id,
   197  					OperationParams: operation.Params(ctx,
   198  						s.config.OperationTimeout(),
   199  						s.config.OperationCancelAfter(),
   200  						operation.ModeSync,
   201  					),
   202  				},
   203  			)
   204  		}
   205  
   206  		for _, onClose := range s.onClose {
   207  			onClose(s)
   208  		}
   209  	})
   210  
   211  	if err != nil {
   212  		return xerrors.WithStackTrace(err)
   213  	}
   214  
   215  	return nil
   216  }
   217  
   218  func (s *session) checkCloseHint(md metadata.MD) {
   219  	for header, values := range md {
   220  		if header != meta.HeaderServerHints {
   221  			continue
   222  		}
   223  		for _, hint := range values {
   224  			if hint == meta.HintSessionClose {
   225  				s.SetStatus(table.SessionClosing)
   226  			}
   227  		}
   228  	}
   229  }
   230  
   231  // KeepAlive keeps idle session alive.
   232  func (s *session) KeepAlive(ctx context.Context) (err error) {
   233  	var (
   234  		result Ydb_Table.KeepAliveResult
   235  		onDone = trace.TableOnSessionKeepAlive(
   236  			s.config.Trace(), &ctx,
   237  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).KeepAlive"),
   238  			s,
   239  		)
   240  	)
   241  	defer func() {
   242  		onDone(err)
   243  	}()
   244  
   245  	resp, err := s.tableService.KeepAlive(ctx,
   246  		&Ydb_Table.KeepAliveRequest{
   247  			SessionId: s.id,
   248  			OperationParams: operation.Params(
   249  				ctx,
   250  				s.config.OperationTimeout(),
   251  				s.config.OperationCancelAfter(),
   252  				operation.ModeSync,
   253  			),
   254  		},
   255  	)
   256  	if err != nil {
   257  		return xerrors.WithStackTrace(err)
   258  	}
   259  
   260  	err = resp.GetOperation().GetResult().UnmarshalTo(&result)
   261  	if err != nil {
   262  		return xerrors.WithStackTrace(err)
   263  	}
   264  
   265  	switch result.GetSessionStatus() {
   266  	case Ydb_Table.KeepAliveResult_SESSION_STATUS_READY:
   267  		s.SetStatus(table.SessionReady)
   268  	case Ydb_Table.KeepAliveResult_SESSION_STATUS_BUSY:
   269  		s.SetStatus(table.SessionBusy)
   270  	}
   271  
   272  	return nil
   273  }
   274  
   275  // CreateTable creates table at given path with given options.
   276  func (s *session) CreateTable(
   277  	ctx context.Context,
   278  	path string,
   279  	opts ...options.CreateTableOption,
   280  ) (err error) {
   281  	var (
   282  		request = Ydb_Table.CreateTableRequest{
   283  			SessionId: s.id,
   284  			Path:      path,
   285  			OperationParams: operation.Params(
   286  				ctx,
   287  				s.config.OperationTimeout(),
   288  				s.config.OperationCancelAfter(),
   289  				operation.ModeSync,
   290  			),
   291  		}
   292  		a = allocator.New()
   293  	)
   294  	defer a.Free()
   295  	for _, opt := range opts {
   296  		if opt != nil {
   297  			opt.ApplyCreateTableOption((*options.CreateTableDesc)(&request), a)
   298  		}
   299  	}
   300  	_, err = s.tableService.CreateTable(ctx, &request)
   301  	if err != nil {
   302  		return xerrors.WithStackTrace(err)
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  // DescribeTable describes table at given path.
   309  func (s *session) DescribeTable(
   310  	ctx context.Context,
   311  	path string,
   312  	opts ...options.DescribeTableOption,
   313  ) (desc options.Description, err error) {
   314  	request := initializeRequest(ctx, s, path, opts)
   315  	response, err := s.tableService.DescribeTable(ctx, request)
   316  	if err != nil {
   317  		return desc, xerrors.WithStackTrace(err)
   318  	}
   319  
   320  	var result Ydb_Table.DescribeTableResult
   321  	if err = response.GetOperation().GetResult().UnmarshalTo(&result); err != nil {
   322  		return desc, xerrors.WithStackTrace(err)
   323  	}
   324  
   325  	desc = options.Description{
   326  		Name:                 result.GetSelf().GetName(),
   327  		PrimaryKey:           result.GetPrimaryKey(),
   328  		Columns:              processColumns(result.GetColumns()),
   329  		KeyRanges:            processKeyRanges(result.GetShardKeyBounds()),
   330  		Stats:                processTableStats(result.GetTableStats()),
   331  		ColumnFamilies:       processColumnFamilies(result.GetColumnFamilies()),
   332  		Attributes:           processAttributes(result.GetAttributes()),
   333  		ReadReplicaSettings:  options.NewReadReplicasSettings(result.GetReadReplicasSettings()),
   334  		StorageSettings:      options.NewStorageSettings(result.GetStorageSettings()),
   335  		KeyBloomFilter:       feature.FromYDB(result.GetKeyBloomFilter()),
   336  		PartitioningSettings: options.NewPartitioningSettings(result.GetPartitioningSettings()),
   337  		Indexes:              processIndexes(result.GetIndexes()),
   338  		TimeToLiveSettings:   NewTimeToLiveSettings(result.GetTtlSettings()),
   339  		Changefeeds:          processChangefeeds(result.GetChangefeeds()),
   340  		Tiering:              result.GetTiering(),
   341  	}
   342  
   343  	return desc, nil
   344  }
   345  
   346  func initializeRequest(
   347  	ctx context.Context,
   348  	s *session,
   349  	path string,
   350  	opts []options.DescribeTableOption,
   351  ) *Ydb_Table.DescribeTableRequest {
   352  	request := Ydb_Table.DescribeTableRequest{
   353  		SessionId: s.id,
   354  		Path:      path,
   355  		OperationParams: operation.Params(
   356  			ctx,
   357  			s.config.OperationTimeout(),
   358  			s.config.OperationCancelAfter(),
   359  			operation.ModeSync,
   360  		),
   361  	}
   362  	for _, opt := range opts {
   363  		if opt != nil {
   364  			opt((*options.DescribeTableDesc)(&request))
   365  		}
   366  	}
   367  
   368  	return &request
   369  }
   370  
   371  func processColumns(columns []*Ydb_Table.ColumnMeta) []options.Column {
   372  	cs := make([]options.Column, len(columns))
   373  	for i, c := range columns {
   374  		cs[i] = options.Column{
   375  			Name:   c.GetName(),
   376  			Type:   types.TypeFromYDB(c.GetType()),
   377  			Family: c.GetFamily(),
   378  		}
   379  	}
   380  
   381  	return cs
   382  }
   383  
   384  func processKeyRanges(bounds []*Ydb.TypedValue) []options.KeyRange {
   385  	rs := make([]options.KeyRange, len(bounds)+1)
   386  	var last value.Value
   387  	for i, b := range bounds {
   388  		if last != nil {
   389  			rs[i].From = last
   390  		}
   391  
   392  		bound := value.FromYDB(b.GetType(), b.GetValue())
   393  		rs[i].To = bound
   394  
   395  		last = bound
   396  	}
   397  	if last != nil {
   398  		i := len(rs) - 1
   399  		rs[i].From = last
   400  	}
   401  
   402  	return rs
   403  }
   404  
   405  func processTableStats(resStats *Ydb_Table.TableStats) *options.TableStats {
   406  	if resStats == nil {
   407  		return nil
   408  	}
   409  
   410  	partStats := make([]options.PartitionStats, len(resStats.GetPartitionStats()))
   411  	for i, v := range resStats.GetPartitionStats() {
   412  		partStats[i].RowsEstimate = v.GetRowsEstimate()
   413  		partStats[i].StoreSize = v.GetStoreSize()
   414  	}
   415  
   416  	var creationTime, modificationTime time.Time
   417  	if resStats.GetCreationTime().GetSeconds() != 0 {
   418  		creationTime = time.Unix(resStats.GetCreationTime().GetSeconds(), int64(resStats.GetCreationTime().GetNanos()))
   419  	}
   420  	if resStats.GetModificationTime().GetSeconds() != 0 {
   421  		modificationTime = time.Unix(
   422  			resStats.GetModificationTime().GetSeconds(),
   423  			int64(resStats.GetModificationTime().GetNanos()),
   424  		)
   425  	}
   426  
   427  	return &options.TableStats{
   428  		PartitionStats:   partStats,
   429  		RowsEstimate:     resStats.GetRowsEstimate(),
   430  		StoreSize:        resStats.GetStoreSize(),
   431  		Partitions:       resStats.GetPartitions(),
   432  		CreationTime:     creationTime,
   433  		ModificationTime: modificationTime,
   434  	}
   435  }
   436  
   437  func processColumnFamilies(families []*Ydb_Table.ColumnFamily) []options.ColumnFamily {
   438  	cf := make([]options.ColumnFamily, len(families))
   439  	for i, c := range families {
   440  		cf[i] = options.NewColumnFamily(c)
   441  	}
   442  
   443  	return cf
   444  }
   445  
   446  func processAttributes(attrs map[string]string) map[string]string {
   447  	attributes := make(map[string]string, len(attrs))
   448  	for k, v := range attrs {
   449  		attributes[k] = v
   450  	}
   451  
   452  	return attributes
   453  }
   454  
   455  func processIndexes(indexes []*Ydb_Table.TableIndexDescription) []options.IndexDescription {
   456  	idxs := make([]options.IndexDescription, len(indexes))
   457  	for i, idx := range indexes {
   458  		var typ options.IndexType
   459  		switch idx.GetType().(type) {
   460  		case *Ydb_Table.TableIndexDescription_GlobalAsyncIndex:
   461  			typ = options.IndexTypeGlobalAsync
   462  		case *Ydb_Table.TableIndexDescription_GlobalIndex:
   463  			typ = options.IndexTypeGlobal
   464  		}
   465  		idxs[i] = options.IndexDescription{
   466  			Name:         idx.GetName(),
   467  			IndexColumns: idx.GetIndexColumns(),
   468  			DataColumns:  idx.GetDataColumns(),
   469  			Status:       idx.GetStatus(),
   470  			Type:         typ,
   471  		}
   472  	}
   473  
   474  	return idxs
   475  }
   476  
   477  func processChangefeeds(changefeeds []*Ydb_Table.ChangefeedDescription) []options.ChangefeedDescription {
   478  	feeds := make([]options.ChangefeedDescription, len(changefeeds))
   479  	for i, proto := range changefeeds {
   480  		feeds[i] = options.NewChangefeedDescription(proto)
   481  	}
   482  
   483  	return feeds
   484  }
   485  
   486  // DropTable drops table at given path with given options.
   487  func (s *session) DropTable(
   488  	ctx context.Context,
   489  	path string,
   490  	opts ...options.DropTableOption,
   491  ) (err error) {
   492  	request := Ydb_Table.DropTableRequest{
   493  		SessionId: s.id,
   494  		Path:      path,
   495  		OperationParams: operation.Params(
   496  			ctx,
   497  			s.config.OperationTimeout(),
   498  			s.config.OperationCancelAfter(),
   499  			operation.ModeSync,
   500  		),
   501  	}
   502  	for _, opt := range opts {
   503  		if opt != nil {
   504  			opt.ApplyDropTableOption((*options.DropTableDesc)(&request))
   505  		}
   506  	}
   507  	_, err = s.tableService.DropTable(ctx, &request)
   508  
   509  	return xerrors.WithStackTrace(err)
   510  }
   511  
   512  func (s *session) checkError(err error) {
   513  	if err == nil {
   514  		return
   515  	}
   516  	m := retry.Check(err)
   517  	if m.MustDeleteSession() {
   518  		s.SetStatus(table.SessionClosing)
   519  	}
   520  }
   521  
   522  // AlterTable modifies schema of table at given path with given options.
   523  func (s *session) AlterTable(
   524  	ctx context.Context,
   525  	path string,
   526  	opts ...options.AlterTableOption,
   527  ) (err error) {
   528  	var (
   529  		request = Ydb_Table.AlterTableRequest{
   530  			SessionId: s.id,
   531  			Path:      path,
   532  			OperationParams: operation.Params(
   533  				ctx,
   534  				s.config.OperationTimeout(),
   535  				s.config.OperationCancelAfter(),
   536  				operation.ModeSync,
   537  			),
   538  		}
   539  		a = allocator.New()
   540  	)
   541  	defer a.Free()
   542  	for _, opt := range opts {
   543  		if opt != nil {
   544  			opt.ApplyAlterTableOption((*options.AlterTableDesc)(&request), a)
   545  		}
   546  	}
   547  	_, err = s.tableService.AlterTable(ctx, &request)
   548  
   549  	return xerrors.WithStackTrace(err)
   550  }
   551  
   552  // CopyTable creates copy of table at given path.
   553  func (s *session) CopyTable(
   554  	ctx context.Context,
   555  	dst, src string,
   556  	opts ...options.CopyTableOption,
   557  ) (err error) {
   558  	request := Ydb_Table.CopyTableRequest{
   559  		SessionId:       s.id,
   560  		SourcePath:      src,
   561  		DestinationPath: dst,
   562  		OperationParams: operation.Params(
   563  			ctx,
   564  			s.config.OperationTimeout(),
   565  			s.config.OperationCancelAfter(),
   566  			operation.ModeSync,
   567  		),
   568  	}
   569  	for _, opt := range opts {
   570  		if opt != nil {
   571  			opt((*options.CopyTableDesc)(&request))
   572  		}
   573  	}
   574  	_, err = s.tableService.CopyTable(ctx, &request)
   575  	if err != nil {
   576  		return xerrors.WithStackTrace(err)
   577  	}
   578  
   579  	return nil
   580  }
   581  
   582  func copyTables(
   583  	ctx context.Context,
   584  	sessionID string,
   585  	operationTimeout time.Duration,
   586  	operationCancelAfter time.Duration,
   587  	service interface {
   588  		CopyTables(
   589  			ctx context.Context, in *Ydb_Table.CopyTablesRequest, opts ...grpc.CallOption,
   590  		) (*Ydb_Table.CopyTablesResponse, error)
   591  	},
   592  	opts ...options.CopyTablesOption,
   593  ) (err error) {
   594  	request := Ydb_Table.CopyTablesRequest{
   595  		SessionId: sessionID,
   596  		OperationParams: operation.Params(
   597  			ctx,
   598  			operationTimeout,
   599  			operationCancelAfter,
   600  			operation.ModeSync,
   601  		),
   602  	}
   603  	for _, opt := range opts {
   604  		if opt != nil {
   605  			opt((*options.CopyTablesDesc)(&request))
   606  		}
   607  	}
   608  	if len(request.GetTables()) == 0 {
   609  		return xerrors.WithStackTrace(fmt.Errorf("no CopyTablesItem: %w", errParamsRequired))
   610  	}
   611  	_, err = service.CopyTables(ctx, &request)
   612  	if err != nil {
   613  		return xerrors.WithStackTrace(err)
   614  	}
   615  
   616  	return nil
   617  }
   618  
   619  // CopyTables creates copy of table at given path.
   620  func (s *session) CopyTables(
   621  	ctx context.Context,
   622  	opts ...options.CopyTablesOption,
   623  ) (err error) {
   624  	err = copyTables(ctx, s.id, s.config.OperationTimeout(), s.config.OperationCancelAfter(), s.tableService, opts...)
   625  	if err != nil {
   626  		return xerrors.WithStackTrace(err)
   627  	}
   628  
   629  	return nil
   630  }
   631  
   632  func renameTables(
   633  	ctx context.Context,
   634  	sessionID string,
   635  	operationTimeout time.Duration,
   636  	operationCancelAfter time.Duration,
   637  	service interface {
   638  		RenameTables(
   639  			ctx context.Context, in *Ydb_Table.RenameTablesRequest, opts ...grpc.CallOption,
   640  		) (*Ydb_Table.RenameTablesResponse, error)
   641  	},
   642  	opts ...options.RenameTablesOption,
   643  ) (err error) {
   644  	request := Ydb_Table.RenameTablesRequest{
   645  		SessionId: sessionID,
   646  		OperationParams: operation.Params(
   647  			ctx,
   648  			operationTimeout,
   649  			operationCancelAfter,
   650  			operation.ModeSync,
   651  		),
   652  	}
   653  	for _, opt := range opts {
   654  		if opt != nil {
   655  			opt((*options.RenameTablesDesc)(&request))
   656  		}
   657  	}
   658  	if len(request.GetTables()) == 0 {
   659  		return xerrors.WithStackTrace(fmt.Errorf("no RenameTablesItem: %w", errParamsRequired))
   660  	}
   661  	_, err = service.RenameTables(ctx, &request)
   662  	if err != nil {
   663  		return xerrors.WithStackTrace(err)
   664  	}
   665  
   666  	return nil
   667  }
   668  
   669  // RenameTables renames tables.
   670  func (s *session) RenameTables(
   671  	ctx context.Context,
   672  	opts ...options.RenameTablesOption,
   673  ) (err error) {
   674  	err = renameTables(ctx, s.id, s.config.OperationTimeout(), s.config.OperationCancelAfter(), s.tableService, opts...)
   675  	if err != nil {
   676  		return xerrors.WithStackTrace(err)
   677  	}
   678  
   679  	return nil
   680  }
   681  
   682  // Explain explains data query represented by text.
   683  func (s *session) Explain(
   684  	ctx context.Context,
   685  	query string,
   686  ) (
   687  	exp table.DataQueryExplanation,
   688  	err error,
   689  ) {
   690  	var (
   691  		result   Ydb_Table.ExplainQueryResult
   692  		response *Ydb_Table.ExplainDataQueryResponse
   693  		onDone   = trace.TableOnSessionQueryExplain(
   694  			s.config.Trace(), &ctx,
   695  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).Explain"),
   696  			s, query,
   697  		)
   698  	)
   699  	defer func() {
   700  		if err != nil {
   701  			onDone("", "", err)
   702  		} else {
   703  			onDone(exp.AST, exp.AST, nil)
   704  		}
   705  	}()
   706  
   707  	response, err = s.tableService.ExplainDataQuery(ctx,
   708  		&Ydb_Table.ExplainDataQueryRequest{
   709  			SessionId: s.id,
   710  			YqlText:   query,
   711  			OperationParams: operation.Params(
   712  				ctx,
   713  				s.config.OperationTimeout(),
   714  				s.config.OperationCancelAfter(),
   715  				operation.ModeSync,
   716  			),
   717  		},
   718  	)
   719  	if err != nil {
   720  		return exp, xerrors.WithStackTrace(err)
   721  	}
   722  
   723  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   724  	if err != nil {
   725  		return exp, xerrors.WithStackTrace(err)
   726  	}
   727  
   728  	return table.DataQueryExplanation{
   729  		Explanation: table.Explanation{
   730  			Plan: result.GetQueryPlan(),
   731  		},
   732  		AST: result.GetQueryAst(),
   733  	}, nil
   734  }
   735  
   736  // Prepare prepares data query within session s.
   737  func (s *session) Prepare(ctx context.Context, queryText string) (_ table.Statement, err error) {
   738  	var (
   739  		stmt     *statement
   740  		response *Ydb_Table.PrepareDataQueryResponse
   741  		result   Ydb_Table.PrepareQueryResult
   742  		onDone   = trace.TableOnSessionQueryPrepare(
   743  			s.config.Trace(), &ctx,
   744  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).Prepare"),
   745  			s, queryText,
   746  		)
   747  	)
   748  	defer func() {
   749  		if err != nil {
   750  			onDone(nil, err)
   751  		} else {
   752  			onDone(stmt.query, nil)
   753  		}
   754  	}()
   755  
   756  	response, err = s.tableService.PrepareDataQuery(ctx,
   757  		&Ydb_Table.PrepareDataQueryRequest{
   758  			SessionId: s.id,
   759  			YqlText:   queryText,
   760  			OperationParams: operation.Params(
   761  				ctx,
   762  				s.config.OperationTimeout(),
   763  				s.config.OperationCancelAfter(),
   764  				operation.ModeSync,
   765  			),
   766  		},
   767  	)
   768  	if err != nil {
   769  		return nil, xerrors.WithStackTrace(err)
   770  	}
   771  
   772  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   773  	if err != nil {
   774  		return nil, xerrors.WithStackTrace(err)
   775  	}
   776  
   777  	stmt = &statement{
   778  		session: s,
   779  		query:   queryPrepared(result.GetQueryId(), queryText),
   780  		params:  result.GetParametersTypes(),
   781  	}
   782  
   783  	return stmt, nil
   784  }
   785  
   786  // Execute executes given data query represented by text.
   787  func (s *session) Execute(
   788  	ctx context.Context,
   789  	txControl *table.TransactionControl,
   790  	query string,
   791  	parameters *params.Parameters,
   792  	opts ...options.ExecuteDataQueryOption,
   793  ) (
   794  	txr table.Transaction, r result.Result, err error,
   795  ) {
   796  	var (
   797  		a       = allocator.New()
   798  		q       = queryFromText(query)
   799  		request = options.ExecuteDataQueryDesc{
   800  			ExecuteDataQueryRequest: a.TableExecuteDataQueryRequest(),
   801  			IgnoreTruncated:         s.config.IgnoreTruncated(),
   802  		}
   803  		callOptions []grpc.CallOption
   804  	)
   805  	defer a.Free()
   806  
   807  	request.SessionId = s.id
   808  	request.TxControl = txControl.Desc()
   809  	request.Parameters = parameters.ToYDB(a)
   810  	request.Query = q.toYDB(a)
   811  	request.QueryCachePolicy = a.TableQueryCachePolicy()
   812  	request.QueryCachePolicy.KeepInCache = len(request.Parameters) > 0
   813  	request.OperationParams = operation.Params(ctx,
   814  		s.config.OperationTimeout(),
   815  		s.config.OperationCancelAfter(),
   816  		operation.ModeSync,
   817  	)
   818  
   819  	for _, opt := range opts {
   820  		if opt != nil {
   821  			callOptions = append(callOptions, opt.ApplyExecuteDataQueryOption(&request, a)...)
   822  		}
   823  	}
   824  
   825  	onDone := trace.TableOnSessionQueryExecute(
   826  		s.config.Trace(), &ctx,
   827  		stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).Execute"),
   828  		s, q, parameters,
   829  		request.QueryCachePolicy.GetKeepInCache(),
   830  	)
   831  	defer func() {
   832  		onDone(txr, false, r, err)
   833  	}()
   834  
   835  	result, err := s.executeDataQuery(ctx, a, request.ExecuteDataQueryRequest, callOptions...)
   836  	if err != nil {
   837  		return nil, nil, xerrors.WithStackTrace(err)
   838  	}
   839  
   840  	return s.executeQueryResult(result, request.TxControl, request.IgnoreTruncated)
   841  }
   842  
   843  // executeQueryResult returns Transaction and result built from received
   844  // result.
   845  func (s *session) executeQueryResult(
   846  	res *Ydb_Table.ExecuteQueryResult,
   847  	txControl *Ydb_Table.TransactionControl,
   848  	ignoreTruncated bool,
   849  ) (
   850  	table.Transaction, result.Result, error,
   851  ) {
   852  	tx := &transaction{
   853  		Identifier: tx.ID(res.GetTxMeta().GetId()),
   854  		s:          s,
   855  	}
   856  	if txControl.GetCommitTx() {
   857  		tx.state.Store(txStateCommitted)
   858  	} else {
   859  		tx.state.Store(txStateInitialized)
   860  		tx.control = table.TxControl(table.WithTxID(tx.ID()))
   861  	}
   862  
   863  	return tx, scanner.NewUnary(
   864  		res.GetResultSets(),
   865  		res.GetQueryStats(),
   866  		scanner.WithIgnoreTruncated(ignoreTruncated),
   867  	), nil
   868  }
   869  
   870  // executeDataQuery executes data query.
   871  func (s *session) executeDataQuery(
   872  	ctx context.Context, a *allocator.Allocator, request *Ydb_Table.ExecuteDataQueryRequest,
   873  	callOptions ...grpc.CallOption,
   874  ) (
   875  	_ *Ydb_Table.ExecuteQueryResult,
   876  	err error,
   877  ) {
   878  	var (
   879  		result   = a.TableExecuteQueryResult()
   880  		response *Ydb_Table.ExecuteDataQueryResponse
   881  	)
   882  
   883  	response, err = s.tableService.ExecuteDataQuery(ctx, request, callOptions...)
   884  	if err != nil {
   885  		return nil, xerrors.WithStackTrace(err)
   886  	}
   887  
   888  	err = response.GetOperation().GetResult().UnmarshalTo(result)
   889  	if err != nil {
   890  		return nil, xerrors.WithStackTrace(err)
   891  	}
   892  
   893  	return result, nil
   894  }
   895  
   896  // ExecuteSchemeQuery executes scheme query.
   897  func (s *session) ExecuteSchemeQuery(
   898  	ctx context.Context,
   899  	query string,
   900  	opts ...options.ExecuteSchemeQueryOption,
   901  ) (err error) {
   902  	request := Ydb_Table.ExecuteSchemeQueryRequest{
   903  		SessionId: s.id,
   904  		YqlText:   query,
   905  		OperationParams: operation.Params(
   906  			ctx,
   907  			s.config.OperationTimeout(),
   908  			s.config.OperationCancelAfter(),
   909  			operation.ModeSync,
   910  		),
   911  	}
   912  	for _, opt := range opts {
   913  		if opt != nil {
   914  			opt((*options.ExecuteSchemeQueryDesc)(&request))
   915  		}
   916  	}
   917  	_, err = s.tableService.ExecuteSchemeQuery(ctx, &request)
   918  
   919  	return xerrors.WithStackTrace(err)
   920  }
   921  
   922  // DescribeTableOptions describes supported table options.
   923  //
   924  //nolint:funlen
   925  func (s *session) DescribeTableOptions(ctx context.Context) (
   926  	desc options.TableOptionsDescription,
   927  	err error,
   928  ) {
   929  	var (
   930  		response *Ydb_Table.DescribeTableOptionsResponse
   931  		result   Ydb_Table.DescribeTableOptionsResult
   932  	)
   933  	request := Ydb_Table.DescribeTableOptionsRequest{
   934  		OperationParams: operation.Params(
   935  			ctx,
   936  			s.config.OperationTimeout(),
   937  			s.config.OperationCancelAfter(),
   938  			operation.ModeSync,
   939  		),
   940  	}
   941  	response, err = s.tableService.DescribeTableOptions(ctx, &request)
   942  	if err != nil {
   943  		return desc, xerrors.WithStackTrace(err)
   944  	}
   945  
   946  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   947  	if err != nil {
   948  		return desc, xerrors.WithStackTrace(err)
   949  	}
   950  
   951  	{
   952  		xs := make([]options.TableProfileDescription, len(result.GetTableProfilePresets()))
   953  		for i, p := range result.GetTableProfilePresets() {
   954  			xs[i] = options.TableProfileDescription{
   955  				Name:   p.GetName(),
   956  				Labels: p.GetLabels(),
   957  
   958  				DefaultStoragePolicy:      p.GetDefaultStoragePolicy(),
   959  				DefaultCompactionPolicy:   p.GetDefaultCompactionPolicy(),
   960  				DefaultPartitioningPolicy: p.GetDefaultPartitioningPolicy(),
   961  				DefaultExecutionPolicy:    p.GetDefaultExecutionPolicy(),
   962  				DefaultReplicationPolicy:  p.GetDefaultReplicationPolicy(),
   963  				DefaultCachingPolicy:      p.GetDefaultCachingPolicy(),
   964  
   965  				AllowedStoragePolicies:      p.GetAllowedStoragePolicies(),
   966  				AllowedCompactionPolicies:   p.GetAllowedCompactionPolicies(),
   967  				AllowedPartitioningPolicies: p.GetAllowedPartitioningPolicies(),
   968  				AllowedExecutionPolicies:    p.GetAllowedExecutionPolicies(),
   969  				AllowedReplicationPolicies:  p.GetAllowedReplicationPolicies(),
   970  				AllowedCachingPolicies:      p.GetAllowedCachingPolicies(),
   971  			}
   972  		}
   973  		desc.TableProfilePresets = xs
   974  	}
   975  	{
   976  		xs := make(
   977  			[]options.StoragePolicyDescription,
   978  			len(result.GetStoragePolicyPresets()),
   979  		)
   980  		for i, p := range result.GetStoragePolicyPresets() {
   981  			xs[i] = options.StoragePolicyDescription{
   982  				Name:   p.GetName(),
   983  				Labels: p.GetLabels(),
   984  			}
   985  		}
   986  		desc.StoragePolicyPresets = xs
   987  	}
   988  	{
   989  		xs := make(
   990  			[]options.CompactionPolicyDescription,
   991  			len(result.GetCompactionPolicyPresets()),
   992  		)
   993  		for i, p := range result.GetCompactionPolicyPresets() {
   994  			xs[i] = options.CompactionPolicyDescription{
   995  				Name:   p.GetName(),
   996  				Labels: p.GetLabels(),
   997  			}
   998  		}
   999  		desc.CompactionPolicyPresets = xs
  1000  	}
  1001  	{
  1002  		xs := make(
  1003  			[]options.PartitioningPolicyDescription,
  1004  			len(result.GetPartitioningPolicyPresets()),
  1005  		)
  1006  		for i, p := range result.GetPartitioningPolicyPresets() {
  1007  			xs[i] = options.PartitioningPolicyDescription{
  1008  				Name:   p.GetName(),
  1009  				Labels: p.GetLabels(),
  1010  			}
  1011  		}
  1012  		desc.PartitioningPolicyPresets = xs
  1013  	}
  1014  	{
  1015  		xs := make(
  1016  			[]options.ExecutionPolicyDescription,
  1017  			len(result.GetExecutionPolicyPresets()),
  1018  		)
  1019  		for i, p := range result.GetExecutionPolicyPresets() {
  1020  			xs[i] = options.ExecutionPolicyDescription{
  1021  				Name:   p.GetName(),
  1022  				Labels: p.GetLabels(),
  1023  			}
  1024  		}
  1025  		desc.ExecutionPolicyPresets = xs
  1026  	}
  1027  	{
  1028  		xs := make(
  1029  			[]options.ReplicationPolicyDescription,
  1030  			len(result.GetReplicationPolicyPresets()),
  1031  		)
  1032  		for i, p := range result.GetReplicationPolicyPresets() {
  1033  			xs[i] = options.ReplicationPolicyDescription{
  1034  				Name:   p.GetName(),
  1035  				Labels: p.GetLabels(),
  1036  			}
  1037  		}
  1038  		desc.ReplicationPolicyPresets = xs
  1039  	}
  1040  	{
  1041  		xs := make(
  1042  			[]options.CachingPolicyDescription,
  1043  			len(result.GetCachingPolicyPresets()),
  1044  		)
  1045  		for i, p := range result.GetCachingPolicyPresets() {
  1046  			xs[i] = options.CachingPolicyDescription{
  1047  				Name:   p.GetName(),
  1048  				Labels: p.GetLabels(),
  1049  			}
  1050  		}
  1051  		desc.CachingPolicyPresets = xs
  1052  	}
  1053  
  1054  	return desc, nil
  1055  }
  1056  
  1057  // StreamReadTable reads table at given path with given options.
  1058  //
  1059  // Note that given ctx controls the lifetime of the whole read, not only this
  1060  // StreamReadTable() call; that is, the time until returned result is closed
  1061  // via Close() call or fully drained by sequential NextResultSet() calls.
  1062  //
  1063  //nolint:funlen
  1064  func (s *session) StreamReadTable(
  1065  	ctx context.Context,
  1066  	path string,
  1067  	opts ...options.ReadTableOption,
  1068  ) (_ result.StreamResult, err error) {
  1069  	var (
  1070  		onDone = trace.TableOnSessionQueryStreamRead(s.config.Trace(), &ctx,
  1071  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).StreamReadTable"),
  1072  			s,
  1073  		)
  1074  		request = Ydb_Table.ReadTableRequest{
  1075  			SessionId: s.id,
  1076  			Path:      path,
  1077  		}
  1078  		stream Ydb_Table_V1.TableService_StreamReadTableClient
  1079  		a      = allocator.New()
  1080  	)
  1081  	defer func() {
  1082  		a.Free()
  1083  		onDone(xerrors.HideEOF(err))
  1084  	}()
  1085  
  1086  	for _, opt := range opts {
  1087  		if opt != nil {
  1088  			opt.ApplyReadTableOption((*options.ReadTableDesc)(&request), a)
  1089  		}
  1090  	}
  1091  
  1092  	ctx, cancel := xcontext.WithCancel(ctx)
  1093  
  1094  	stream, err = s.tableService.StreamReadTable(ctx, &request)
  1095  	if err != nil {
  1096  		cancel()
  1097  
  1098  		return nil, xerrors.WithStackTrace(err)
  1099  	}
  1100  
  1101  	return scanner.NewStream(ctx,
  1102  		func(ctx context.Context) (
  1103  			set *Ydb.ResultSet,
  1104  			stats *Ydb_TableStats.QueryStats,
  1105  			err error,
  1106  		) {
  1107  			select {
  1108  			case <-ctx.Done():
  1109  				return nil, nil, xerrors.WithStackTrace(ctx.Err())
  1110  			default:
  1111  				var response *Ydb_Table.ReadTableResponse
  1112  				response, err = stream.Recv()
  1113  				result := response.GetResult()
  1114  				if result == nil || err != nil {
  1115  					return nil, nil, xerrors.WithStackTrace(err)
  1116  				}
  1117  
  1118  				return result.GetResultSet(), nil, nil
  1119  			}
  1120  		},
  1121  		func(err error) error {
  1122  			cancel()
  1123  			onDone(xerrors.HideEOF(err))
  1124  
  1125  			return err
  1126  		},
  1127  		scanner.WithIgnoreTruncated(true), // stream read table always returns truncated flag on last result set
  1128  	)
  1129  }
  1130  
  1131  func (s *session) ReadRows(
  1132  	ctx context.Context,
  1133  	path string,
  1134  	keys value.Value,
  1135  	opts ...options.ReadRowsOption,
  1136  ) (_ result.Result, err error) {
  1137  	var (
  1138  		a       = allocator.New()
  1139  		request = Ydb_Table.ReadRowsRequest{
  1140  			SessionId: s.id,
  1141  			Path:      path,
  1142  			Keys:      value.ToYDB(keys, a),
  1143  		}
  1144  		response *Ydb_Table.ReadRowsResponse
  1145  	)
  1146  	defer func() {
  1147  		a.Free()
  1148  	}()
  1149  
  1150  	for _, opt := range opts {
  1151  		if opt != nil {
  1152  			opt.ApplyReadRowsOption((*options.ReadRowsDesc)(&request), a)
  1153  		}
  1154  	}
  1155  
  1156  	response, err = s.tableService.ReadRows(ctx, &request)
  1157  	if err != nil {
  1158  		return nil, xerrors.WithStackTrace(err)
  1159  	}
  1160  
  1161  	if response.GetStatus() != Ydb.StatusIds_SUCCESS {
  1162  		return nil, xerrors.WithStackTrace(
  1163  			xerrors.FromOperation(response),
  1164  		)
  1165  	}
  1166  
  1167  	return scanner.NewUnary(
  1168  		[]*Ydb.ResultSet{response.GetResultSet()},
  1169  		nil,
  1170  		scanner.WithIgnoreTruncated(s.config.IgnoreTruncated()),
  1171  	), nil
  1172  }
  1173  
  1174  // StreamExecuteScanQuery scan-reads table at given path with given options.
  1175  //
  1176  // Note that given ctx controls the lifetime of the whole read, not only this
  1177  // StreamExecuteScanQuery() call; that is, the time until returned result is closed
  1178  // via Close() call or fully drained by sequential NextResultSet() calls.
  1179  //
  1180  //nolint:funlen
  1181  func (s *session) StreamExecuteScanQuery(
  1182  	ctx context.Context,
  1183  	query string,
  1184  	parameters *params.Parameters,
  1185  	opts ...options.ExecuteScanQueryOption,
  1186  ) (_ result.StreamResult, err error) {
  1187  	var (
  1188  		a      = allocator.New()
  1189  		q      = queryFromText(query)
  1190  		onDone = trace.TableOnSessionQueryStreamExecute(
  1191  			s.config.Trace(), &ctx,
  1192  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).StreamExecuteScanQuery"),
  1193  			s, q, parameters,
  1194  		)
  1195  		request = Ydb_Table.ExecuteScanQueryRequest{
  1196  			Query:      q.toYDB(a),
  1197  			Parameters: parameters.ToYDB(a),
  1198  			Mode:       Ydb_Table.ExecuteScanQueryRequest_MODE_EXEC, // set default
  1199  		}
  1200  		stream      Ydb_Table_V1.TableService_StreamExecuteScanQueryClient
  1201  		callOptions []grpc.CallOption
  1202  	)
  1203  	defer func() {
  1204  		a.Free()
  1205  		onDone(xerrors.HideEOF(err))
  1206  	}()
  1207  
  1208  	for _, opt := range opts {
  1209  		if opt != nil {
  1210  			callOptions = append(callOptions, opt.ApplyExecuteScanQueryOption((*options.ExecuteScanQueryDesc)(&request))...)
  1211  		}
  1212  	}
  1213  
  1214  	ctx, cancel := xcontext.WithCancel(ctx)
  1215  
  1216  	stream, err = s.tableService.StreamExecuteScanQuery(ctx, &request, callOptions...)
  1217  	if err != nil {
  1218  		cancel()
  1219  
  1220  		return nil, xerrors.WithStackTrace(err)
  1221  	}
  1222  
  1223  	return scanner.NewStream(ctx,
  1224  		func(ctx context.Context) (
  1225  			set *Ydb.ResultSet,
  1226  			stats *Ydb_TableStats.QueryStats,
  1227  			err error,
  1228  		) {
  1229  			select {
  1230  			case <-ctx.Done():
  1231  				return nil, nil, xerrors.WithStackTrace(ctx.Err())
  1232  			default:
  1233  				var response *Ydb_Table.ExecuteScanQueryPartialResponse
  1234  				response, err = stream.Recv()
  1235  				result := response.GetResult()
  1236  				if result == nil || err != nil {
  1237  					return nil, nil, xerrors.WithStackTrace(err)
  1238  				}
  1239  
  1240  				return result.GetResultSet(), result.GetQueryStats(), nil
  1241  			}
  1242  		},
  1243  		func(err error) error {
  1244  			cancel()
  1245  			onDone(xerrors.HideEOF(err))
  1246  
  1247  			return err
  1248  		},
  1249  		scanner.WithIgnoreTruncated(s.config.IgnoreTruncated()),
  1250  		scanner.WithMarkTruncatedAsRetryable(),
  1251  	)
  1252  }
  1253  
  1254  // BulkUpsert uploads given list of ydb struct values to the table.
  1255  func (s *session) BulkUpsert(ctx context.Context, table string, rows value.Value,
  1256  	opts ...options.BulkUpsertOption,
  1257  ) (err error) {
  1258  	var (
  1259  		a           = allocator.New()
  1260  		callOptions []grpc.CallOption
  1261  		onDone      = trace.TableOnSessionBulkUpsert(s.config.Trace(), &ctx,
  1262  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).BulkUpsert"), s,
  1263  		)
  1264  	)
  1265  	defer func() {
  1266  		defer a.Free()
  1267  		onDone(err)
  1268  	}()
  1269  
  1270  	for _, opt := range opts {
  1271  		if opt != nil {
  1272  			callOptions = append(callOptions, opt.ApplyBulkUpsertOption()...)
  1273  		}
  1274  	}
  1275  
  1276  	_, err = s.tableService.BulkUpsert(ctx,
  1277  		&Ydb_Table.BulkUpsertRequest{
  1278  			Table: table,
  1279  			Rows:  value.ToYDB(rows, a),
  1280  			OperationParams: operation.Params(
  1281  				ctx,
  1282  				s.config.OperationTimeout(),
  1283  				s.config.OperationCancelAfter(),
  1284  				operation.ModeSync,
  1285  			),
  1286  		},
  1287  		callOptions...,
  1288  	)
  1289  	if err != nil {
  1290  		return xerrors.WithStackTrace(err)
  1291  	}
  1292  
  1293  	return nil
  1294  }
  1295  
  1296  // BeginTransaction begins new transaction within given session with given settings.
  1297  func (s *session) BeginTransaction(
  1298  	ctx context.Context,
  1299  	txSettings *table.TransactionSettings,
  1300  ) (x table.Transaction, err error) {
  1301  	var (
  1302  		result   Ydb_Table.BeginTransactionResult
  1303  		response *Ydb_Table.BeginTransactionResponse
  1304  		onDone   = trace.TableOnTxBegin(
  1305  			s.config.Trace(), &ctx,
  1306  			stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*session).BeginTransaction"),
  1307  			s,
  1308  		)
  1309  	)
  1310  	defer func() {
  1311  		onDone(x, err)
  1312  	}()
  1313  
  1314  	response, err = s.tableService.BeginTransaction(ctx,
  1315  		&Ydb_Table.BeginTransactionRequest{
  1316  			SessionId:  s.id,
  1317  			TxSettings: txSettings.Settings(),
  1318  			OperationParams: operation.Params(
  1319  				ctx,
  1320  				s.config.OperationTimeout(),
  1321  				s.config.OperationCancelAfter(),
  1322  				operation.ModeSync,
  1323  			),
  1324  		},
  1325  	)
  1326  	if err != nil {
  1327  		return nil, xerrors.WithStackTrace(err)
  1328  	}
  1329  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
  1330  	if err != nil {
  1331  		return nil, xerrors.WithStackTrace(err)
  1332  	}
  1333  	tx := &transaction{
  1334  		Identifier: tx.ID(result.GetTxMeta().GetId()),
  1335  		s:          s,
  1336  		control:    table.TxControl(table.WithTxID(result.GetTxMeta().GetId())),
  1337  	}
  1338  	tx.state.Store(txStateInitialized)
  1339  
  1340  	return tx, nil
  1341  }