github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/SQL.md (about) 1 # `database/sql` driver for `YDB` 2 3 In addition to native YDB Go driver APIs, package `ydb-go-sdk` provides standard APIs for `database/sql`. 4 It allows to use "regular" Go facilities to access YDB databases. 5 Behind the scene, `database/sql` APIs are implemented using the native interfaces. 6 7 8 ## Table of contents 9 1. [Initialization of `database/sql` driver](#init) 10 * [Configure driver with `ydb.Connector` (recommended way)](#init-connector) 11 * [Configure driver with data source name or connection string](#init-dsn) 12 2. [Client balancing](#balancing) 13 3. [Session pooling](#session-pool) 14 4. [Query execution](#queries) 15 * [Queries on database object](#queries-db) 16 * [Queries on transaction object](#queries-tx) 17 5. [Query modes (DDL, DML, DQL, etc.)](#query-modes) 18 6. [Retry helpers for `YDB` `database/sql` driver](#retry) 19 * [Over `sql.Conn` object](#retry-conn) 20 * [Over `sql.Tx`](#retry-tx) 21 7. [Query args types](#arg-types) 22 8. [Query bindings](#bindings) 23 9. [Accessing the native driver from `*sql.DB`](#unwrap) 24 * [Driver with go's 1.18 supports also `*sql.Conn` for unwrapping](#unwrap-cc) 25 10. [Troubleshooting](#troubleshooting) 26 * [Logging driver events](#logging) 27 * [Add metrics about SDK's events](#metrics) 28 * [Add `Jaeger` traces about driver events](#jaeger) 29 11. [Example of usage](#example) 30 31 ## Initialization of `database/sql` driver <a name="init"></a> 32 33 ### Configure driver with `ydb.Connector` (recommended way) <a name="init-connector"></a> 34 ```go 35 import ( 36 "database/sql" 37 38 "github.com/ydb-platform/ydb-go-sdk/v3" 39 ) 40 41 func main() { 42 // init native ydb-go-sdk driver 43 nativeDriver, err := ydb.Open(context.TODO(), "grpc://localhost:2136/local", 44 // See many ydb.Option's for configure driver https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#Option 45 ) 46 if err != nil { 47 // fallback on error 48 } 49 defer nativeDriver.Close(context.TODO()) 50 connector, err := ydb.Connector(nativeDriver, 51 // See ydb.ConnectorOption's for configure connector https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#ConnectorOption 52 ) 53 if err != nil { 54 // fallback on error 55 } 56 db := sql.OpenDB(connector) 57 defer db.Close() 58 db.SetMaxOpenConns(100) 59 db.SetMaxIdleConns(100) 60 db.SetConnMaxIdleTime(time.Second) // workaround for background keep-aliving of YDB sessions 61 } 62 ``` 63 64 ### Configure driver with data source name or connection string <a name="init-dsn"></a> 65 ```go 66 import ( 67 "database/sql" 68 69 _ "github.com/ydb-platform/ydb-go-sdk/v3" 70 ) 71 72 func main() { 73 db, err := sql.Open("ydb", "grpc://localhost:2136/local") 74 defer db.Close() 75 } 76 ``` 77 Data source name parameters: 78 * `token` – access token to be used during requests (required) 79 * static credentials with authority part of URI, like `grpcs://root:password@localhost:2135/local` 80 * `query_mode=scripting` - you can redefine default [DML](https://en.wikipedia.org/wiki/Data_manipulation_language) query mode 81 82 ## Client balancing <a name="balancing"></a> 83 84 `database/sql` driver for `YDB` like as native driver for `YDB` use client balancing, which happens on `CreateSession` request. 85 At this time, native driver choose known node for execute request by according balancer algorithm. 86 Default balancer algorithm is a `random choice`. 87 Client balancer may be re-configured with option `ydb.WithBalancer`: 88 ```go 89 import ( 90 "github.com/ydb-platform/ydb-go-sdk/v3/balancers" 91 ) 92 func main() { 93 nativeDriver, err := ydb.Open(context.TODO(), "grpc://localhost:2136/local", 94 ydb.WithBalancer( 95 balancers.PreferLocationsWithFallback( 96 balancers.RandomChoice(), "a", "b", 97 ), 98 ), 99 ) 100 if err != nil { 101 // fallback on error 102 } 103 connector, err := ydb.Connector(nativeDriver) 104 if err != nil { 105 // fallback on error 106 } 107 db := sql.OpenDB(connector) 108 db.SetMaxOpenConns(100) 109 db.SetMaxIdleConns(100) 110 db.SetConnMaxIdleTime(time.Second) // workaround for background keep-aliving of YDB sessions 111 } 112 ``` 113 114 ## Session pooling <a name="session-pool"></a> 115 116 Native driver `ydb-go-sdk/v3` implements the internal session pool, which uses with `db.Table().Do()` or `db.Table().DoTx()` methods. 117 Internal session pool are configured with options like `ydb.WithSessionPoolSizeLimit()` and other. 118 Unlike the session pool in the native driver, `database/sql` contains a different implementation of the session pool, which is configured with `*sql.DB.SetMaxOpenConns` and `*sql.DB.SetMaxIdleConns`. 119 Lifetime of a `YDB` session depends on driver configuration and error occurance, such as `sql.driver.ErrBadConn`. 120 `YDB` driver for `database/sql` includes the logic to transform the internal `YDB` error codes into `sql.driver.ErrBadConn` and other retryable and non-retryable error types. 121 122 In most cases the implementation of `database/sql` driver for YDB allows to complete queries without user-visible errors. 123 But some errors need to be handled on the client side, by re-running not a single operation, but a complete transaction. 124 Therefore the transaction logic needs to be wrapped with retry helpers, such as `retry.Do` or `retry.DoTx` (see more about retry helpers in the [retry section](#retry)). 125 126 ## Query execution <a name="queries"></a> 127 128 ### Queries on database object <a name="queries-db"></a> 129 ```go 130 rows, err := db.QueryContext(ctx, 131 "SELECT series_id, title, release_date FROM `/full/path/of/table/series`;" 132 ) 133 if err != nil { 134 log.Fatal(err) 135 } 136 defer rows.Close() // always close rows 137 var ( 138 id *string 139 title *string 140 releaseDate *time.Time 141 ) 142 for rows.Next() { // iterate over rows 143 // apply database values to go's type destinations 144 if err = rows.Scan(&id, &title, &releaseDate); err != nil { 145 log.Fatal(err) 146 } 147 log.Printf("> [%s] %s (%s)", *id, *title, releaseDate.Format("2006-01-02")) 148 } 149 if err = rows.Err(); err != nil { // always check final rows err 150 log.Fatal(err) 151 } 152 ``` 153 154 ### Queries on transaction object <a name="queries-tx"></a> 155 156 `database/sql` driver over `ydb-go-sdk/v3` supports next isolation leveles: 157 - read-write (mapped to `SerializableReadWrite` transaction control) 158 ```go 159 rw := sql.TxOption{ 160 ReadOnly: false, 161 Isolation: sql.LevelDefault, 162 } 163 ``` 164 - read-only (mapped to `OnlineReadOnly` transaction settings on each request, will be mapped to true `SnapshotReadOnly` soon) 165 ```go 166 ro := sql.TxOption{ 167 ReadOnly: true, 168 Isolation: sql.LevelSnapshot, 169 } 170 ``` 171 172 Example of works with transactions: 173 ```go 174 tx, err := db.BeginTx(ctx, sql.TxOption{ 175 ReadOnly: true, 176 Isolation: sql.LevelSnapshot, 177 }) 178 if err != nil { 179 log.Fatal(err) 180 } 181 defer tx.Rollback() 182 rows, err := tx.QueryContext(ctx, 183 "SELECT series_id, title, release_date FROM `/full/path/of/table/series`;" 184 ) 185 if err != nil { 186 log.Fatal(err) 187 } 188 defer rows.Close() // always close rows 189 var ( 190 id *string 191 title *string 192 releaseDate *time.Time 193 ) 194 for rows.Next() { // iterate over rows 195 // apply database values to go's type destinations 196 if err = rows.Scan(&id, &title, &releaseDate); err != nil { 197 log.Fatal(err) 198 } 199 log.Printf("> [%s] %s (%s)", *id, *title, releaseDate.Format("2006-01-02")) 200 } 201 if err = rows.Err(); err != nil { // always check final rows err 202 log.Fatal(err) 203 } 204 if err = tx.Commit(); err != nil { 205 log.Fatal(err) 206 } 207 ``` 208 209 ## Query modes (DDL, DML, DQL, etc.) <a name="query-modes"></a> 210 Currently the `YDB` server APIs require the use of a proper GRPC service method depending on the specific request type. 211 In particular, [DDL](https://en.wikipedia.org/wiki/Data_definition_language) must be called through `table.session.ExecuteSchemeQuery`, 212 [DML](https://en.wikipedia.org/wiki/Data_manipulation_language) needs `table.session.Execute`, and 213 [DQL](https://en.wikipedia.org/wiki/Data_query_language) should be passed via `table.session.Execute` or `table.session.StreamExecuteScanQuery`. 214 `YDB` also has a so-called "scripting" service, which supports different query types within a single method, 215 but without support for transactions. 216 217 Unfortunately, this leads to the need to choose the proper query mode on the application side. 218 219 `YDB` team has a roadmap goal to implement a single universal service method for executing 220 different query types and without the limitations of the "scripting" service method. 221 222 `database/sql` driver implementation for `YDB` supports the following query modes: 223 * `ydb.DataQueryMode` - default query mode, for lookup [DQL](https://en.wikipedia.org/wiki/Data_query_language) queries and [DML](https://en.wikipedia.org/wiki/Data_manipulation_language) queries. 224 * `ydb.ExplainQueryMode` - for gettting plan and [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of the query 225 * `ydb.ScanQueryMode` - for heavy [OLAP](https://en.wikipedia.org/wiki/Online_analytical_processing) style scenarious, with [DQL-only](https://en.wikipedia.org/wiki/Data_query_language) queries. Read more about scan queries in [ydb.tech](https://ydb.tech/en/docs/concepts/scan_query) 226 * `ydb.SchemeQueryMode` - for [DDL](https://en.wikipedia.org/wiki/Data_definition_language) queries 227 * `ydb.ScriptingQueryMode` - for [DDL](https://en.wikipedia.org/wiki/Data_definition_language), [DML](https://en.wikipedia.org/wiki/Data_manipulation_language), [DQL](https://en.wikipedia.org/wiki/Data_query_language) queries (not a [TCL](https://en.wikipedia.org/wiki/SQL#Transaction_controls)). Be careful: queries execute longer than with other query modes, and consume more server-side resources 228 229 Example for changing the default query mode: 230 ```go 231 res, err = db.ExecContext(ydb.WithQueryMode(ctx, ydb.SchemeQueryMode), 232 "DROP TABLE `/full/path/to/table/series`", 233 ) 234 ``` 235 236 ## Changing the transaction control mode <a name="tx-control"></a> 237 238 Default `YDB`'s transaction control mode is a `SerializableReadWrite`. 239 Default transaction control mode can be changed outside of interactive transactions by updating the context object: 240 ```go 241 rows, err := db.QueryContext(ydb.WithTxControl(ctx, table.OnlineReadOnlyTxControl()), 242 "SELECT series_id, title, release_date FROM `/full/path/of/table/series`;" 243 ) 244 ``` 245 246 ## Retry helpers for `YDB` `database/sql` driver <a name="retry"></a> 247 248 `YDB` is a distributed `RDBMS` with non-stop 24/7 releases flow. 249 It means some nodes may be unavailable for queries at some point in time. 250 Network errors may also occur. 251 That's why some queries may complete with errors. 252 Most of those errors are transient. 253 `ydb-go-sdk`'s "knows" what to do on specific error: retry or not, with or without backoff, with or without the need to re-establish the session, etc. 254 `ydb-go-sdk` provides retry helpers which can work either with the database connection object, or with the transaction object. 255 256 ### Retries over `sql.Conn` object <a name="retry-conn"></a> 257 258 `retry.Do` helper accepts custom lambda, which must return error if it happens during the processing, 259 or nil if the operation succeeds. 260 ```go 261 import ( 262 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 263 ) 264 ... 265 err := retry.Do(context.TODO(), db, func(ctx context.Context, cc *sql.Conn) error { 266 // work with cc 267 rows, err := cc.QueryContext(ctx, "SELECT 1;") 268 if err != nil { 269 return err // return err to retryer 270 } 271 ... 272 return nil // good final of retry operation 273 }, retry.WithIdempotent(true)) 274 275 ``` 276 277 ### Retries over `sql.Tx` <a name="retry-tx"></a> 278 279 `retry.DoTx` helper accepts custom lambda, which must return error if it happens during processing, 280 or nil if the operation succeeds. 281 282 `tx` object is a prepared transaction object. 283 284 The logic within the custom lambda does not need the explicit commit or rollback at the end - `retry.DoTx` does it automatically. 285 286 ```go 287 import ( 288 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 289 ) 290 ... 291 err := retry.DoTx(context.TODO(), db, func(ctx context.Context, tx *sql.Tx) error { 292 // work with tx 293 rows, err := tx.QueryContext(ctx, "SELECT 1;") 294 if err != nil { 295 return err // return err to retryer 296 } 297 ... 298 return nil // good final of retry tx operation 299 }, retry.WithIdempotent(true), retry.WithTxOptions(&sql.TxOptions{ 300 Isolation: sql.LevelSnapshot, 301 ReadOnly: true, 302 })) 303 ``` 304 305 ## Specifying query parameters <a name="arg-types"></a> 306 307 `database/sql` driver for `YDB` supports the following types of query parameters: 308 * `sql.NamedArg` types with native Go's types and ydb types: 309 ```go 310 rows, err := db.QueryContext(ctx, 311 ` 312 DECLARE $title AS Text; 313 DECLARE $views AS Uint64; 314 DECLARE $ts AS Datetime; 315 SELECT season_id FROM seasons WHERE title LIKE $title AND views > $views AND first_aired > $ts; 316 `, 317 sql.Named("$title", "%Season 1%"), // argument name with prefix `$` 318 sql.Named("views", uint64(1000)), // argument name without prefix `$` (driver will prepend `$` if necessary) 319 sql.Named("$ts", types.DatetimeValueFromTime( // native ydb type 320 time.Now().Add(-time.Hour*24*365), 321 )), 322 ) 323 ``` 324 * `table.ParameterOption` arguments: 325 ```go 326 rows, err := db.QueryContext(ctx, 327 ` 328 DECLARE $title AS Text; 329 DECLARE $views AS Uint64; 330 SELECT season_id FROM seasons WHERE title LIKE $title AND views > $views; 331 `, 332 table.ValueParam("$seasonTitle", types.TextValue("%Season 1%")), 333 table.ValueParam("$views", types.Uint64Value((1000)), 334 ) 335 ``` 336 * single `*table.QueryParameters` argument: 337 ```go 338 rows, err := db.QueryContext(ctx, 339 ` 340 DECLARE $title AS Text; 341 DECLARE $views AS Uint64; 342 SELECT season_id FROM seasons WHERE title LIKE $title AND views > $views; 343 `, 344 table.NewQueryParameters( 345 table.ValueParam("$seasonTitle", types.TextValue("%Season 1%")), 346 table.ValueParam("$views", types.Uint64Value((1000)), 347 ), 348 ) 349 ``` 350 351 ## Query bindings <a name="bindings"></a> 352 353 YQL is a SQL dialect with YDB specific strict types. This is great for performance and correctness, but sometimes can be a bit dauting to express in a query, especially when then need to be parametrized externally from the application side. For instance, when a YDB query needs to be parametrized, each parameter has name and type provided via `DECLARE` statement. 354 355 Also, because YDB tables reside in virtual filesystem-like structure their names can be quite lengthy. There's a `PRAGMA TablePathPrefix` that can scope the rest of the query inside a given prefix, simplifying table names. For example, a query to the table `/local/path/to/tables/seasons` might look like this: 356 357 ``` 358 DECLARE $title AS Text; 359 DECLARE $views AS Uint64; 360 SELECT season_id 361 FROM `/local/path/to/tables/seasons` 362 WHERE title LIKE $title AND views > $views; 363 ``` 364 365 Using the `PRAGMA` statement, you can simplify the prefix part in the name of all tables involved in the `YQL`-query: 366 367 ``` 368 PRAGMA TablePathPrefix("/local/path/to/tables/"); 369 DECLARE $title AS Text; 370 DECLARE $views AS Uint64; 371 SELECT season_id FROM seasons WHERE title LIKE $title AND views > $views; 372 ``` 373 374 `database/sql` driver for `YDB` (part of [YDB Go SDK](https://github.com/ydb-platform/ydb-go-sdk)) supports query enrichment for: 375 376 * specifying **_TablePathPrefix_** 377 * **_declaring_** types of parameters 378 * **_numeric_** or **_positional_** parameters 379 380 These query enrichments can be enabled explicitly on the initializing step using connector options or connection string parameter `go_auto_bind`. By default `database/sql` driver for `YDB` doesn’t modify queries. 381 382 The following example without bindings demonstrates explicit working with `YDB` types: 383 384 ```go 385 import ( 386 "context" 387 "database/sql" 388 389 "github.com/ydb-platform/ydb-go-sdk/v3" 390 "github.com/ydb-platform/ydb-go-sdk/v3/table" 391 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 392 ) 393 394 func main() { 395 db := sql.Open("ydb", "grpc://localhost:2136/local") 396 defer db.Close() 397 398 // positional args 399 row := db.QueryRowContext(context.TODO(), ` 400 PRAGMA TablePathPrefix("/local/path/to/my/folder"); 401 DECLARE $p0 AS Int32; 402 DECLARE $p1 AS Utf8; 403 SELECT $p0, $p1`, 404 sql.Named("$p0", 42), 405 table.ValueParam("$p1", types.TextValue("my string")), 406 ) 407 // process row ... 408 } 409 ``` 410 411 As you can see, this example also required importing of `ydb-go-sdk` packages and working with them directly. 412 413 With enabled bindings enrichment, the same result can be achieved much easier: 414 415 - with _**connection string_** params: 416 ```go 417 import ( 418 "context" 419 "database/sql" 420 421 _ "github.com/ydb-platform/ydb-go-sdk/v3" // anonymous import for registering driver 422 ) 423 424 func main() { 425 var ( 426 ctx = context.TODO() 427 db = sql.Open("ydb", 428 "grpc://localhost:2136/local?"+ 429 "go_auto_bind="+ 430 "table_path_prefix(/local/path/to/my/folder),"+ 431 "declare,"+ 432 "positional", 433 ) 434 ) 435 defer db.Close() // cleanup resources 436 437 // positional args 438 row := db.QueryRowContext(ctx, `SELECT ?, ?`, 42, "my string") 439 ``` 440 441 - with _**connector options**_: 442 ```go 443 import ( 444 "context" 445 "database/sql" 446 447 "github.com/ydb-platform/ydb-go-sdk/v3" 448 ) 449 450 func main() { 451 var ( 452 ctx = context.TODO() 453 nativeDriver = ydb.MustOpen(ctx, "grpc://localhost:2136/local") 454 db = sql.OpenDB(ydb.MustConnector(nativeDriver, 455 query.TablePathPrefix("/local/path/to/my/folder"), // bind pragma TablePathPrefix 456 query.Declare(), // bind parameters declare 457 query.Positional(), // bind positional args 458 )) 459 ) 460 defer nativeDriver.Close(ctx) // cleanup resources 461 defer db.Close() 462 463 // positional args 464 row := db.QueryRowContext(ctx, `SELECT ?, ?`, 42, "my string") 465 ``` 466 467 The original simple query `SELECT ?, ?` will be expanded on the driver side to the following: 468 469 ```sql 470 -- bind TablePathPrefix 471 PRAGMA TablePathPrefix("/local/path/to/my/folder"); 472 473 -- bind declares 474 DECLARE $p0 AS Int32; 475 DECLARE $p1 AS Utf8; 476 477 -- origin query with positional args replacement 478 SELECT $p0, $p1 479 ``` 480 481 This expanded query will be sent to `ydbd` server instead of the original one. 482 483 For additional examples of query enrichment, see `ydb-go-sdk` documentation: 484 485 * specifying `TablePathPrefix`: 486 * using [connection string parameter](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindTablePathPrefix) 487 * using [connector option](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindTablePathPrefixOverConnector) 488 * declaring bindings: 489 * using [connection string parameter](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindDeclare) 490 * using [connector option](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindDeclareOverConnector) 491 * positional arguments binding: 492 * using [connection string parameter](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindPositionalArgs) 493 * using [connector option](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindPositionalArgsOverConnector) 494 * numeric arguments binding: 495 * using [connection string parameter](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindNumericlArgs) 496 * using [connector option](https://pkg.go.dev/github.com/ydb-platform/ydb-go-sdk/v3#example-package-DatabaseSQLBindNumericArgsOverConnector) 497 498 For a deep understanding of query enrichment see also [unit-tests](https://github.com/ydb-platform/ydb-go-sdk/blob/master/query/bind_test.go). 499 500 You can write your own unit tests to check correct binding of your queries like this: 501 502 ```go 503 func TestBinding(t *testing.T) { 504 bindings := query.NewBind( 505 query.TablePathPrefix("/local/path/to/my/folder"), // bind pragma TablePathPrefix 506 query.Declare(), // bind parameters declare 507 query.Positional(), // auto-replace positional args 508 ) 509 query, params, err := bindings.ToYQL("SELECT ?, ?, ?", 1, uint64(2), "3") 510 require.NoError(t, err) 511 require.Equal(t, `-- bind TablePathPrefix 512 PRAGMA TablePathPrefix("/local/path/to/my/folder"); 513 514 -- bind declares 515 DECLARE $p0 AS Int32; 516 DECLARE $p1 AS Uint64; 517 DECLARE $p2 AS Utf8; 518 519 -- origin query with positional args replacement 520 SELECT $p0, $p1, $p2`, query) 521 require.Equal(t, table.NewQueryParameters( 522 table.ValueParam("$p0", types.Int32Value(1)), 523 table.ValueParam("$p1", types.Uint64Value(2)), 524 table.ValueParam("$p2", types.TextValue("3")), 525 ), params) 526 } 527 ``` 528 529 ## Accessing the native driver from `*sql.DB` <a name="unwrap"></a> 530 531 ```go 532 db, err := sql.Open("ydb", "grpc://localhost:2136/local") 533 if err != nil { 534 t.Fatal(err) 535 } 536 nativeDriver, err = ydb.Unwrap(db) 537 if err != nil { 538 t.Fatal(err) 539 } 540 nativeDriver.Table().Do(ctx, func(ctx context.Context, s table.Session) error { 541 // doing with native YDB session 542 return nil 543 }) 544 ``` 545 546 ### Driver with go's 1.18 supports also `*sql.Conn` for unwrapping <a name="unwrap-cc"></a> 547 548 For example, this feature may be helps with `retry.Do`: 549 ```go 550 err := retry.Do(context.TODO(), db, func(ctx context.Context, cc *sql.Conn) error { 551 nativeDriver, err := ydb.Unwrap(cc) 552 if err != nil { 553 return err // return err to retryer 554 } 555 res, err := nativeDriver.Scripting().Execute(ctx, 556 "SELECT 1+1", 557 table.NewQueryParameters(), 558 ) 559 if err != nil { 560 return err // return err to retryer 561 } 562 // work with cc 563 rows, err := cc.QueryContext(ctx, "SELECT 1;") 564 if err != nil { 565 return err // return err to retryer 566 } 567 ... 568 return nil // good final of retry operation 569 }, retry.WithIdempotent(true)) 570 ``` 571 572 ## Troubleshooting <a name="troubleshooting"></a> 573 574 ### Logging driver events <a name="logging"></a> 575 576 Adding a logging driver events allowed only if connection to `YDB` opens over [connector](##init-connector). 577 Adding of logging provides with [debug adapters](README.md#debug) and wrotes in [migration notes](MIGRATION_v2_v3.md#logs). 578 579 Example of adding `zap` logging: 580 ```go 581 import ( 582 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 583 ydbZap "github.com/ydb-platform/ydb-go-sdk-zap" 584 ) 585 ... 586 nativeDriver, err := ydb.Open(ctx, connectionString, 587 ... 588 ydbZap.WithTraces(log, trace.DetailsAll), 589 ) 590 if err != nil { 591 // fallback on error 592 } 593 connector, err := ydb.Connector(nativeDriver) 594 if err != nil { 595 // fallback on error 596 } 597 db := sql.OpenDB(connector) 598 ``` 599 600 ### Add metrics about SDK's events <a name="metrics"></a> 601 602 Adding of driver events monitoring allowed only if connection to `YDB` opens over [connector](##init-connector). 603 Monitoring of driver events provides with [debug adapters](README.md#debug) and wrotes in [migration notes](MIGRATION_v2_v3.md#metrics). 604 605 Example of adding `Prometheus` monitoring: 606 ```go 607 import ( 608 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 609 ydbMetrics "github.com/ydb-platform/ydb-go-sdk-prometheus" 610 ) 611 ... 612 nativeDriver, err := ydb.Open(ctx, connectionString, 613 ... 614 ydbMetrics.WithTraces(registry, ydbMetrics.WithDetails(trace.DetailsAll)), 615 ) 616 if err != nil { 617 // fallback on error 618 } 619 connector, err := ydb.Connector(nativeDriver) 620 if err != nil { 621 // fallback on error 622 } 623 db := sql.OpenDB(connector) 624 ``` 625 626 ### Add `Jaeger` traces about driver events <a name="jaeger"></a> 627 628 Adding of `Jaeger` traces about driver events allowed only if connection to `YDB` opens over [connector](##init-connector). 629 `Jaeger` tracing provides with [debug adapters](README.md#debug) and wrotes in [migration notes](MIGRATION_v2_v3.md#jaeger). 630 631 Example of adding `Jaeger` tracing: 632 ```go 633 import ( 634 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 635 ydbOpentracing "github.com/ydb-platform/ydb-go-sdk-opentracing" 636 ) 637 ... 638 nativeDriver, err := ydb.Open(ctx, connectionString, 639 ... 640 ydbOpentracing.WithTraces(trace.DriverConnEvents | trace.DriverClusterEvents | trace.DriverRepeaterEvents | trace.DiscoveryEvents), 641 ) 642 if err != nil { 643 // fallback on error 644 } 645 connector, err := ydb.Connector(nativeDriver) 646 if err != nil { 647 // fallback on error 648 } 649 db := sql.OpenDB(connector) 650 ``` 651 652 ## Example of usage <a name="example"></a> 653 654 [Basic example](https://github.com/ydb-platform/ydb-go-examples/tree/master/basic) about series written with `database/sql` driver for `YDB` placed in [examples repository](https://github.com/ydb-platform/ydb-go-examples/tree/master/database_sql)