github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/xsql/connector.go (about)

     1  package xsql
     2  
     3  import (
     4  	"context"
     5  	"database/sql/driver"
     6  	"io"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/jonboulle/clockwork"
    11  
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/bind"
    13  	metaHeaders "github.com/ydb-platform/ydb-go-sdk/v3/internal/meta"
    14  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
    15  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
    16  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    17  	"github.com/ydb-platform/ydb-go-sdk/v3/meta"
    18  	"github.com/ydb-platform/ydb-go-sdk/v3/scheme"
    19  	"github.com/ydb-platform/ydb-go-sdk/v3/scripting"
    20  	"github.com/ydb-platform/ydb-go-sdk/v3/table"
    21  	"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
    22  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    23  )
    24  
    25  type ConnectorOption interface {
    26  	Apply(c *Connector) error
    27  }
    28  
    29  type defaultQueryModeConnectorOption QueryMode
    30  
    31  func (mode defaultQueryModeConnectorOption) Apply(c *Connector) error {
    32  	c.defaultQueryMode = QueryMode(mode)
    33  
    34  	return nil
    35  }
    36  
    37  type QueryBindConnectorOption interface {
    38  	ConnectorOption
    39  	bind.Bind
    40  }
    41  
    42  type queryBindConnectorOption struct {
    43  	bind.Bind
    44  }
    45  
    46  func (o queryBindConnectorOption) Apply(c *Connector) error {
    47  	c.Bindings = bind.Sort(append(c.Bindings, o.Bind))
    48  
    49  	return nil
    50  }
    51  
    52  type tablePathPrefixConnectorOption struct {
    53  	bind.TablePathPrefix
    54  }
    55  
    56  func (o tablePathPrefixConnectorOption) Apply(c *Connector) error {
    57  	c.Bindings = bind.Sort(append(c.Bindings, o.TablePathPrefix))
    58  	c.pathNormalizer = o.TablePathPrefix
    59  
    60  	return nil
    61  }
    62  
    63  func WithQueryBind(bind bind.Bind) QueryBindConnectorOption {
    64  	return queryBindConnectorOption{Bind: bind}
    65  }
    66  
    67  func WithTablePathPrefix(tablePathPrefix string) QueryBindConnectorOption {
    68  	return tablePathPrefixConnectorOption{TablePathPrefix: bind.TablePathPrefix(tablePathPrefix)}
    69  }
    70  
    71  func WithDefaultQueryMode(mode QueryMode) ConnectorOption {
    72  	return defaultQueryModeConnectorOption(mode)
    73  }
    74  
    75  type defaultTxControlOption struct {
    76  	txControl *table.TransactionControl
    77  }
    78  
    79  func (opt defaultTxControlOption) Apply(c *Connector) error {
    80  	c.defaultTxControl = opt.txControl
    81  
    82  	return nil
    83  }
    84  
    85  func WithDefaultTxControl(txControl *table.TransactionControl) ConnectorOption {
    86  	return defaultTxControlOption{txControl}
    87  }
    88  
    89  type defaultDataQueryOptionsConnectorOption []options.ExecuteDataQueryOption
    90  
    91  func (opts defaultDataQueryOptionsConnectorOption) Apply(c *Connector) error {
    92  	c.defaultDataQueryOpts = append(c.defaultDataQueryOpts, opts...)
    93  
    94  	return nil
    95  }
    96  
    97  func WithDefaultDataQueryOptions(opts ...options.ExecuteDataQueryOption) ConnectorOption {
    98  	return defaultDataQueryOptionsConnectorOption(opts)
    99  }
   100  
   101  type defaultScanQueryOptionsConnectorOption []options.ExecuteScanQueryOption
   102  
   103  func (opts defaultScanQueryOptionsConnectorOption) Apply(c *Connector) error {
   104  	c.defaultScanQueryOpts = append(c.defaultScanQueryOpts, opts...)
   105  
   106  	return nil
   107  }
   108  
   109  func WithDefaultScanQueryOptions(opts ...options.ExecuteScanQueryOption) ConnectorOption {
   110  	return defaultScanQueryOptionsConnectorOption(opts)
   111  }
   112  
   113  type traceConnectorOption struct {
   114  	t    *trace.DatabaseSQL
   115  	opts []trace.DatabaseSQLComposeOption
   116  }
   117  
   118  func (option traceConnectorOption) Apply(c *Connector) error {
   119  	c.trace = c.trace.Compose(option.t, option.opts...)
   120  
   121  	return nil
   122  }
   123  
   124  func WithTrace(t *trace.DatabaseSQL, opts ...trace.DatabaseSQLComposeOption) ConnectorOption {
   125  	return traceConnectorOption{t, opts}
   126  }
   127  
   128  type disableServerBalancerConnectorOption struct{}
   129  
   130  func (d disableServerBalancerConnectorOption) Apply(c *Connector) error {
   131  	c.disableServerBalancer = true
   132  
   133  	return nil
   134  }
   135  
   136  func WithDisableServerBalancer() ConnectorOption {
   137  	return disableServerBalancerConnectorOption{}
   138  }
   139  
   140  type idleThresholdConnectorOption time.Duration
   141  
   142  func (idleThreshold idleThresholdConnectorOption) Apply(c *Connector) error {
   143  	c.idleThreshold = time.Duration(idleThreshold)
   144  
   145  	return nil
   146  }
   147  
   148  func WithIdleThreshold(idleThreshold time.Duration) ConnectorOption {
   149  	return idleThresholdConnectorOption(idleThreshold)
   150  }
   151  
   152  type onCloseConnectorOption func(connector *Connector)
   153  
   154  func (f onCloseConnectorOption) Apply(c *Connector) error {
   155  	c.onClose = append(c.onClose, f)
   156  
   157  	return nil
   158  }
   159  
   160  func WithOnClose(f func(connector *Connector)) ConnectorOption {
   161  	return onCloseConnectorOption(f)
   162  }
   163  
   164  type traceRetryConnectorOption struct {
   165  	t *trace.Retry
   166  }
   167  
   168  func (t traceRetryConnectorOption) Apply(c *Connector) error {
   169  	c.traceRetry = t.t
   170  
   171  	return nil
   172  }
   173  
   174  func WithTraceRetry(t *trace.Retry) ConnectorOption {
   175  	return traceRetryConnectorOption{t: t}
   176  }
   177  
   178  type fakeTxConnectorOption QueryMode
   179  
   180  func (m fakeTxConnectorOption) Apply(c *Connector) error {
   181  	c.fakeTxModes = append(c.fakeTxModes, QueryMode(m))
   182  
   183  	return nil
   184  }
   185  
   186  // WithFakeTx returns a copy of context with given QueryMode
   187  func WithFakeTx(m QueryMode) ConnectorOption {
   188  	return fakeTxConnectorOption(m)
   189  }
   190  
   191  type ydbDriver interface {
   192  	Name() string
   193  	Table() table.Client
   194  	Scripting() scripting.Client
   195  	Scheme() scheme.Client
   196  }
   197  
   198  func Open(parent ydbDriver, opts ...ConnectorOption) (_ *Connector, err error) {
   199  	c := &Connector{
   200  		parent:           parent,
   201  		clock:            clockwork.NewRealClock(),
   202  		conns:            make(map[*conn]struct{}),
   203  		defaultTxControl: table.DefaultTxControl(),
   204  		defaultQueryMode: DefaultQueryMode,
   205  		pathNormalizer:   bind.TablePathPrefix(parent.Name()),
   206  		trace:            &trace.DatabaseSQL{},
   207  	}
   208  	for _, opt := range opts {
   209  		if opt != nil {
   210  			if err = opt.Apply(c); err != nil {
   211  				return nil, err
   212  			}
   213  		}
   214  	}
   215  	if c.idleThreshold > 0 {
   216  		c.idleStopper = c.idleCloser()
   217  	}
   218  
   219  	return c, nil
   220  }
   221  
   222  type pathNormalizer interface {
   223  	NormalizePath(folderOrTable string) string
   224  }
   225  
   226  // Connector is a producer of database/sql connections
   227  type Connector struct {
   228  	parent ydbDriver
   229  
   230  	clock clockwork.Clock
   231  
   232  	Bindings       bind.Bindings
   233  	pathNormalizer pathNormalizer
   234  
   235  	fakeTxModes []QueryMode
   236  
   237  	onClose []func(connector *Connector)
   238  
   239  	conns    map[*conn]struct{}
   240  	connsMtx sync.RWMutex
   241  
   242  	idleStopper func()
   243  
   244  	defaultTxControl      *table.TransactionControl
   245  	defaultQueryMode      QueryMode
   246  	defaultDataQueryOpts  []options.ExecuteDataQueryOption
   247  	defaultScanQueryOpts  []options.ExecuteScanQueryOption
   248  	disableServerBalancer bool
   249  	idleThreshold         time.Duration
   250  
   251  	trace      *trace.DatabaseSQL
   252  	traceRetry *trace.Retry
   253  }
   254  
   255  var (
   256  	_ driver.Connector = &Connector{}
   257  	_ io.Closer        = &Connector{}
   258  )
   259  
   260  func (c *Connector) idleCloser() (idleStopper func()) {
   261  	var ctx context.Context
   262  	ctx, idleStopper = xcontext.WithCancel(context.Background())
   263  	go func() {
   264  		for {
   265  			select {
   266  			case <-ctx.Done():
   267  				return
   268  			case <-c.clock.After(c.idleThreshold):
   269  				c.connsMtx.RLock()
   270  				conns := make([]*conn, 0, len(c.conns))
   271  				for cc := range c.conns {
   272  					conns = append(conns, cc)
   273  				}
   274  				c.connsMtx.RUnlock()
   275  				for _, cc := range conns {
   276  					if cc.sinceLastUsage() > c.idleThreshold {
   277  						cc.session.Close(context.Background())
   278  					}
   279  				}
   280  			}
   281  		}
   282  	}()
   283  
   284  	return idleStopper
   285  }
   286  
   287  func (c *Connector) Close() (err error) {
   288  	defer func() {
   289  		for _, onClose := range c.onClose {
   290  			onClose(c)
   291  		}
   292  	}()
   293  	if c.idleStopper != nil {
   294  		c.idleStopper()
   295  	}
   296  
   297  	return nil
   298  }
   299  
   300  func (c *Connector) attach(cc *conn) {
   301  	c.connsMtx.Lock()
   302  	defer c.connsMtx.Unlock()
   303  	c.conns[cc] = struct{}{}
   304  }
   305  
   306  func (c *Connector) detach(cc *conn) {
   307  	c.connsMtx.Lock()
   308  	defer c.connsMtx.Unlock()
   309  	delete(c.conns, cc)
   310  }
   311  
   312  func (c *Connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
   313  	var (
   314  		onDone = trace.DatabaseSQLOnConnectorConnect(
   315  			c.trace, &ctx,
   316  			stack.FunctionID(""),
   317  		)
   318  		session table.ClosableSession
   319  	)
   320  	defer func() {
   321  		onDone(err, session)
   322  	}()
   323  	if !c.disableServerBalancer {
   324  		ctx = meta.WithAllowFeatures(ctx, metaHeaders.HintSessionBalancer)
   325  	}
   326  	session, err = c.parent.Table().CreateSession(ctx) //nolint
   327  	if err != nil {
   328  		return nil, xerrors.WithStackTrace(err)
   329  	}
   330  
   331  	return newConn(ctx, c, session, withDefaultTxControl(c.defaultTxControl),
   332  		withDefaultQueryMode(c.defaultQueryMode),
   333  		withDataOpts(c.defaultDataQueryOpts...),
   334  		withScanOpts(c.defaultScanQueryOpts...),
   335  		withTrace(c.trace),
   336  		withFakeTxModes(c.fakeTxModes...),
   337  	), nil
   338  }
   339  
   340  func (c *Connector) Driver() driver.Driver {
   341  	return &driverWrapper{c: c}
   342  }
   343  
   344  type driverWrapper struct {
   345  	c *Connector
   346  }
   347  
   348  func (d *driverWrapper) TraceRetry() *trace.Retry {
   349  	return d.c.traceRetry
   350  }
   351  
   352  func (d *driverWrapper) Open(_ string) (driver.Conn, error) {
   353  	return nil, ErrUnsupported
   354  }