github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/driver.go (about) 1 package ydb 2 3 import ( 4 "context" 5 "errors" 6 "os" 7 "sync" 8 9 "google.golang.org/grpc" 10 11 "github.com/ydb-platform/ydb-go-sdk/v3/config" 12 "github.com/ydb-platform/ydb-go-sdk/v3/coordination" 13 "github.com/ydb-platform/ydb-go-sdk/v3/discovery" 14 "github.com/ydb-platform/ydb-go-sdk/v3/internal/balancer" 15 "github.com/ydb-platform/ydb-go-sdk/v3/internal/conn" 16 internalCoordination "github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination" 17 coordinationConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination/config" 18 "github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials" 19 internalDiscovery "github.com/ydb-platform/ydb-go-sdk/v3/internal/discovery" 20 discoveryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/discovery/config" 21 "github.com/ydb-platform/ydb-go-sdk/v3/internal/dsn" 22 "github.com/ydb-platform/ydb-go-sdk/v3/internal/endpoint" 23 internalQuery "github.com/ydb-platform/ydb-go-sdk/v3/internal/query" 24 queryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/config" 25 internalRatelimiter "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter" 26 ratelimiterConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/config" 27 internalScheme "github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme" 28 schemeConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme/config" 29 internalScripting "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting" 30 scriptingConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting/config" 31 "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" 32 internalTable "github.com/ydb-platform/ydb-go-sdk/v3/internal/table" 33 tableConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config" 34 "github.com/ydb-platform/ydb-go-sdk/v3/internal/topic/topicclientinternal" 35 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" 36 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 37 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql" 38 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync" 39 "github.com/ydb-platform/ydb-go-sdk/v3/log" 40 "github.com/ydb-platform/ydb-go-sdk/v3/query" 41 "github.com/ydb-platform/ydb-go-sdk/v3/ratelimiter" 42 "github.com/ydb-platform/ydb-go-sdk/v3/scheme" 43 "github.com/ydb-platform/ydb-go-sdk/v3/scripting" 44 "github.com/ydb-platform/ydb-go-sdk/v3/table" 45 "github.com/ydb-platform/ydb-go-sdk/v3/topic" 46 "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions" 47 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 48 ) 49 50 var _ Connection = (*Driver)(nil) 51 52 // Driver type provide access to YDB service clients 53 type Driver struct { 54 ctx context.Context // cancel while Driver.Close called. 55 ctxCancel context.CancelFunc 56 57 userInfo *dsn.UserInfo 58 59 logger log.Logger 60 loggerOpts []log.Option 61 loggerDetails trace.Detailer 62 63 opts []Option 64 65 config *config.Config 66 options []config.Option 67 68 discovery *internalDiscovery.Client 69 discoveryOptions []discoveryConfig.Option 70 71 table *internalTable.Client 72 tableOptions []tableConfig.Option 73 74 query *internalQuery.Client 75 queryOptions []queryConfig.Option 76 77 scripting *internalScripting.Client 78 scriptingOptions []scriptingConfig.Option 79 80 scheme *internalScheme.Client 81 schemeOptions []schemeConfig.Option 82 83 coordination *internalCoordination.Client 84 coordinationOptions []coordinationConfig.Option 85 86 ratelimiter *internalRatelimiter.Client 87 ratelimiterOptions []ratelimiterConfig.Option 88 89 topic *topicclientinternal.Client 90 topicOptions []topicoptions.TopicOption 91 92 databaseSQLOptions []xsql.ConnectorOption 93 94 pool *conn.Pool 95 96 mtx sync.Mutex 97 balancer *balancer.Balancer 98 99 children map[uint64]*Driver 100 childrenMtx xsync.Mutex 101 onClose []func(c *Driver) 102 103 panicCallback func(e interface{}) 104 } 105 106 func (d *Driver) trace() *trace.Driver { 107 if d.config != nil { 108 return d.config.Trace() 109 } 110 111 return &trace.Driver{} 112 } 113 114 // Close closes Driver and clear resources 115 // 116 //nolint:nonamedreturns 117 func (d *Driver) Close(ctx context.Context) (finalErr error) { 118 onDone := trace.DriverOnClose(d.trace(), &ctx, stack.FunctionID("")) 119 defer func() { 120 onDone(finalErr) 121 }() 122 d.ctxCancel() 123 124 d.mtx.Lock() 125 defer d.mtx.Unlock() 126 127 d.ctxCancel() 128 129 defer func() { 130 for _, f := range d.onClose { 131 f(d) 132 } 133 }() 134 135 closes := make([]func(context.Context) error, 0) 136 d.childrenMtx.WithLock(func() { 137 for _, child := range d.children { 138 closes = append(closes, child.Close) 139 } 140 d.children = nil 141 }) 142 143 closes = append( 144 closes, 145 d.ratelimiter.Close, 146 d.coordination.Close, 147 d.scheme.Close, 148 d.scripting.Close, 149 d.table.Close, 150 d.query.Close, 151 d.topic.Close, 152 d.balancer.Close, 153 d.pool.Release, 154 ) 155 156 var issues []error 157 for _, f := range closes { 158 if err := f(ctx); err != nil { 159 issues = append(issues, err) 160 } 161 } 162 163 if len(issues) > 0 { 164 return xerrors.WithStackTrace(xerrors.NewWithIssues("close failed", issues...)) 165 } 166 167 return nil 168 } 169 170 // Endpoint returns initial endpoint 171 func (d *Driver) Endpoint() string { 172 return d.config.Endpoint() 173 } 174 175 // Name returns database name 176 func (d *Driver) Name() string { 177 return d.config.Database() 178 } 179 180 // Secure returns true if database Driver is secure 181 func (d *Driver) Secure() bool { 182 return d.config.Secure() 183 } 184 185 // Table returns table client 186 func (d *Driver) Table() table.Client { 187 return d.table 188 } 189 190 // Query returns query client 191 // 192 // # Experimental 193 // 194 // Notice: This API is EXPERIMENTAL and may be changed or removed in a later release. 195 func (d *Driver) Query() query.Client { 196 return d.query 197 } 198 199 // Scheme returns scheme client 200 func (d *Driver) Scheme() scheme.Client { 201 return d.scheme 202 } 203 204 // Coordination returns coordination client 205 func (d *Driver) Coordination() coordination.Client { 206 return d.coordination 207 } 208 209 // Ratelimiter returns ratelimiter client 210 func (d *Driver) Ratelimiter() ratelimiter.Client { 211 return d.ratelimiter 212 } 213 214 // Discovery returns discovery client 215 func (d *Driver) Discovery() discovery.Client { 216 return d.discovery 217 } 218 219 // Scripting returns scripting client 220 func (d *Driver) Scripting() scripting.Client { 221 return d.scripting 222 } 223 224 // Topic returns topic client 225 func (d *Driver) Topic() topic.Client { 226 return d.topic 227 } 228 229 // Open connects to database by DSN and return driver runtime holder 230 // 231 // DSN accept Driver string like 232 // 233 // "grpc[s]://{endpoint}/{database}[?param=value]" 234 // 235 // See sugar.DSN helper for make dsn from endpoint and database 236 // 237 //nolint:nonamedreturns 238 func Open(ctx context.Context, dsn string, opts ...Option) (_ *Driver, err error) { 239 d, err := newConnectionFromOptions(ctx, append( 240 []Option{ 241 WithConnectionString(dsn), 242 }, 243 opts..., 244 )...) 245 if err != nil { 246 return nil, xerrors.WithStackTrace(err) 247 } 248 249 onDone := trace.DriverOnInit( 250 d.trace(), &ctx, 251 stack.FunctionID(""), 252 d.config.Endpoint(), d.config.Database(), d.config.Secure(), 253 ) 254 defer func() { 255 onDone(err) 256 }() 257 258 if err = d.connect(ctx); err != nil { 259 return nil, xerrors.WithStackTrace(err) 260 } 261 262 return d, nil 263 } 264 265 func MustOpen(ctx context.Context, dsn string, opts ...Option) *Driver { 266 db, err := Open(ctx, dsn, opts...) 267 if err != nil { 268 panic(err) 269 } 270 271 return db 272 } 273 274 // New connects to database and return driver runtime holder 275 // 276 // Deprecated: use Open with required param connectionString instead 277 // 278 //nolint:nonamedreturns 279 func New(ctx context.Context, opts ...Option) (_ *Driver, err error) { 280 d, err := newConnectionFromOptions(ctx, opts...) 281 if err != nil { 282 return nil, xerrors.WithStackTrace(err) 283 } 284 285 onDone := trace.DriverOnInit( 286 d.trace(), &ctx, 287 stack.FunctionID(""), 288 d.config.Endpoint(), d.config.Database(), d.config.Secure(), 289 ) 290 defer func() { 291 onDone(err) 292 }() 293 294 if err = d.connect(ctx); err != nil { 295 return nil, xerrors.WithStackTrace(err) 296 } 297 298 return d, nil 299 } 300 301 //nolint:cyclop, nonamedreturns 302 func newConnectionFromOptions(ctx context.Context, opts ...Option) (_ *Driver, err error) { 303 ctx, driverCtxCancel := xcontext.WithCancel(xcontext.WithoutDeadline(ctx)) 304 defer func() { 305 if err != nil { 306 driverCtxCancel() 307 } 308 }() 309 310 d := &Driver{ 311 children: make(map[uint64]*Driver), 312 ctx: ctx, 313 ctxCancel: driverCtxCancel, 314 } 315 316 if caFile, has := os.LookupEnv("YDB_SSL_ROOT_CERTIFICATES_FILE"); has { 317 d.opts = append(d.opts, 318 WithCertificatesFromFile(caFile), 319 ) 320 } 321 if logLevel, has := os.LookupEnv("YDB_LOG_SEVERITY_LEVEL"); has { 322 if l := log.FromString(logLevel); l < log.QUIET { 323 d.opts = append(d.opts, 324 WithLogger( 325 log.Default(os.Stderr, 326 log.WithMinLevel(log.FromString(logLevel)), 327 log.WithColoring(), 328 ), 329 trace.MatchDetails( 330 os.Getenv("YDB_LOG_DETAILS"), 331 trace.WithDefaultDetails(trace.DetailsAll), 332 ), 333 log.WithLogQuery(), 334 ), 335 ) 336 } 337 } 338 d.opts = append(d.opts, opts...) 339 for _, opt := range d.opts { 340 if opt != nil { 341 err = opt(ctx, d) 342 if err != nil { 343 return nil, xerrors.WithStackTrace(err) 344 } 345 } 346 } 347 if d.logger != nil { 348 for _, opt := range []Option{ 349 WithTraceDriver(log.Driver(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 350 WithTraceTable(log.Table(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 351 WithTraceScripting(log.Scripting(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 352 WithTraceScheme(log.Scheme(d.logger, d.loggerDetails, d.loggerOpts...)), 353 WithTraceCoordination(log.Coordination(d.logger, d.loggerDetails, d.loggerOpts...)), 354 WithTraceRatelimiter(log.Ratelimiter(d.logger, d.loggerDetails, d.loggerOpts...)), 355 WithTraceDiscovery(log.Discovery(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 356 WithTraceTopic(log.Topic(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 357 WithTraceDatabaseSQL(log.DatabaseSQL(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 358 WithTraceRetry(log.Retry(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck 359 } { 360 if opt != nil { 361 err = opt(ctx, d) 362 if err != nil { 363 return nil, xerrors.WithStackTrace(err) 364 } 365 } 366 } 367 } 368 d.config = config.New(d.options...) 369 370 return d, nil 371 } 372 373 //nolint:cyclop, nonamedreturns, funlen 374 func (d *Driver) connect(ctx context.Context) (err error) { 375 if d.config.Endpoint() == "" { 376 return xerrors.WithStackTrace(errors.New("configuration: empty dial address")) //nolint:goerr113 377 } 378 379 if d.config.Database() == "" { 380 return xerrors.WithStackTrace(errors.New("configuration: empty database")) //nolint:goerr113 381 } 382 383 if d.userInfo != nil { 384 d.config = d.config.With(config.WithCredentials( 385 credentials.NewStaticCredentials( 386 d.userInfo.User, d.userInfo.Password, 387 d.config.Endpoint(), 388 credentials.WithGrpcDialOptions(d.config.GrpcDialOptions()...), 389 ), 390 )) 391 } 392 393 if d.pool == nil { 394 d.pool = conn.NewPool(ctx, d.config) 395 } 396 397 d.balancer, err = balancer.New(ctx, d.config, d.pool, d.discoveryOptions...) 398 if err != nil { 399 return xerrors.WithStackTrace(err) 400 } 401 402 d.table, err = internalTable.New(ctx, 403 d.balancer, 404 tableConfig.New( 405 append( 406 // prepend common params from root config 407 []tableConfig.Option{ 408 tableConfig.With(d.config.Common), 409 }, 410 d.tableOptions..., 411 )..., 412 ), 413 ) 414 if err != nil { 415 return xerrors.WithStackTrace(err) 416 } 417 418 d.query, err = internalQuery.New(ctx, 419 d.balancer, 420 queryConfig.New( 421 append( 422 // prepend common params from root config 423 []queryConfig.Option{ 424 queryConfig.With(d.config.Common), 425 }, 426 d.queryOptions..., 427 )..., 428 ), 429 ) 430 if err != nil { 431 return xerrors.WithStackTrace(err) 432 } 433 434 d.scheme, err = internalScheme.New(ctx, 435 d.balancer, 436 schemeConfig.New( 437 append( 438 // prepend common params from root config 439 []schemeConfig.Option{ 440 schemeConfig.WithDatabaseName(d.Name()), 441 schemeConfig.With(d.config.Common), 442 }, 443 d.schemeOptions..., 444 )..., 445 ), 446 ) 447 if err != nil { 448 return xerrors.WithStackTrace(err) 449 } 450 451 d.coordination, err = internalCoordination.New(ctx, 452 d.balancer, 453 coordinationConfig.New( 454 append( 455 // prepend common params from root config 456 []coordinationConfig.Option{ 457 coordinationConfig.With(d.config.Common), 458 }, 459 d.coordinationOptions..., 460 )..., 461 ), 462 ) 463 if err != nil { 464 return xerrors.WithStackTrace(err) 465 } 466 467 d.ratelimiter, err = internalRatelimiter.New(ctx, 468 d.balancer, 469 ratelimiterConfig.New( 470 append( 471 // prepend common params from root config 472 []ratelimiterConfig.Option{ 473 ratelimiterConfig.With(d.config.Common), 474 }, 475 d.ratelimiterOptions..., 476 )..., 477 ), 478 ) 479 if err != nil { 480 return xerrors.WithStackTrace(err) 481 } 482 483 d.discovery, err = internalDiscovery.New(ctx, 484 d.pool.Get(endpoint.New(d.config.Endpoint())), 485 discoveryConfig.New( 486 append( 487 // prepend common params from root config 488 []discoveryConfig.Option{ 489 discoveryConfig.With(d.config.Common), 490 discoveryConfig.WithEndpoint(d.Endpoint()), 491 discoveryConfig.WithDatabase(d.Name()), 492 discoveryConfig.WithSecure(d.Secure()), 493 discoveryConfig.WithMeta(d.config.Meta()), 494 }, 495 d.discoveryOptions..., 496 )..., 497 ), 498 ) 499 if err != nil { 500 return xerrors.WithStackTrace(err) 501 } 502 503 d.scripting, err = internalScripting.New(ctx, 504 d.balancer, 505 scriptingConfig.New( 506 append( 507 // prepend common params from root config 508 []scriptingConfig.Option{ 509 scriptingConfig.With(d.config.Common), 510 }, 511 d.scriptingOptions..., 512 )..., 513 ), 514 ) 515 if err != nil { 516 return xerrors.WithStackTrace(err) 517 } 518 519 d.topic, err = topicclientinternal.New(ctx, 520 d.balancer, 521 d.config.Credentials(), 522 append( 523 // prepend common params from root config 524 []topicoptions.TopicOption{ 525 topicoptions.WithOperationTimeout(d.config.OperationTimeout()), 526 topicoptions.WithOperationCancelAfter(d.config.OperationCancelAfter()), 527 }, 528 d.topicOptions..., 529 )..., 530 ) 531 if err != nil { 532 return xerrors.WithStackTrace(err) 533 } 534 535 return nil 536 } 537 538 // GRPCConn casts *ydb.Driver to grpc.ClientConnInterface for executing 539 // unary and streaming RPC over internal driver balancer. 540 // 541 // Warning: for connect to driver-unsupported YDB services 542 func GRPCConn(cc *Driver) grpc.ClientConnInterface { 543 return conn.WithContextModifier(cc.balancer, conn.WithoutWrapping) 544 }