github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/tests/integration/table_long_stream_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  package integration
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  	"path"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/ydb-platform/ydb-go-sdk/v3"
    17  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    18  	"github.com/ydb-platform/ydb-go-sdk/v3/table"
    19  	"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
    20  	"github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
    21  	"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
    22  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    23  )
    24  
    25  func TestLongStream(t *testing.T) {
    26  	var (
    27  		folder            = t.Name()
    28  		tableName         = `long_stream_query`
    29  		discoveryInterval = 10 * time.Second
    30  		db                *ydb.Driver
    31  		err               error
    32  		upsertRowsCount   = 100000
    33  		batchSize         = 10000
    34  		expectedCheckSum  = uint64(4999950000)
    35  		ctx               = xtest.Context(t)
    36  	)
    37  
    38  	db, err = ydb.Open(ctx,
    39  		os.Getenv("YDB_CONNECTION_STRING"),
    40  		ydb.WithAccessTokenCredentials(
    41  			os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS"),
    42  		),
    43  		ydb.WithDiscoveryInterval(0), // disable re-discovery on upsert time
    44  		ydb.WithLogger(
    45  			newLogger(t),
    46  			trace.MatchDetails(`ydb\.(driver|discovery|retry|scheme).*`),
    47  		),
    48  	)
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	defer func(db *ydb.Driver) {
    53  		// cleanup
    54  		_ = db.Close(ctx)
    55  	}(db)
    56  
    57  	t.Run("creating", func(t *testing.T) {
    58  		t.Run("stream", func(t *testing.T) {
    59  			t.Run("table", func(t *testing.T) {
    60  				if err = db.Table().Do(ctx,
    61  					func(ctx context.Context, s table.Session) (err error) {
    62  						_, err = s.DescribeTable(ctx, path.Join(db.Name(), folder, tableName))
    63  						if err == nil {
    64  							if err = s.DropTable(ctx, path.Join(db.Name(), folder, tableName)); err != nil {
    65  								return err
    66  							}
    67  						}
    68  						return s.ExecuteSchemeQuery(
    69  							ctx,
    70  							"CREATE TABLE `"+path.Join(db.Name(), folder, tableName)+"` (val Int64, PRIMARY KEY (val))",
    71  						)
    72  					},
    73  					table.WithIdempotent(),
    74  				); err != nil {
    75  					t.Fatalf("create table failed: %v\n", err)
    76  				}
    77  			})
    78  		})
    79  	})
    80  
    81  	t.Run("check", func(t *testing.T) {
    82  		t.Run("batch", func(t *testing.T) {
    83  			t.Run("size", func(t *testing.T) {
    84  				if upsertRowsCount%batchSize != 0 {
    85  					t.Fatalf("wrong batch size: (%d mod %d = %d) != 0", upsertRowsCount, batchSize, upsertRowsCount%batchSize)
    86  				}
    87  			})
    88  		})
    89  	})
    90  
    91  	t.Run("upserting", func(t *testing.T) {
    92  		t.Run("rows", func(t *testing.T) {
    93  			var upserted uint32
    94  			for i := 0; i < (upsertRowsCount / batchSize); i++ {
    95  				var (
    96  					from = int32(i * batchSize)
    97  					to   = int32((i + 1) * batchSize)
    98  				)
    99  				t.Run(fmt.Sprintf("upserting %d..%d", from, to-1), func(t *testing.T) {
   100  					values := make([]types.Value, 0, batchSize)
   101  					for j := from; j < to; j++ {
   102  						values = append(
   103  							values,
   104  							types.StructValue(
   105  								types.StructFieldValue("val", types.Int32Value(j)),
   106  							),
   107  						)
   108  					}
   109  					if err = db.Table().Do(ctx,
   110  						func(ctx context.Context, s table.Session) (err error) {
   111  							_, _, err = s.Execute(
   112  								ctx,
   113  								table.TxControl(
   114  									table.BeginTx(
   115  										table.WithSerializableReadWrite(),
   116  									),
   117  									table.CommitTx(),
   118  								), `
   119  								DECLARE $values AS List<Struct<
   120  									val: Int32,
   121  								>>;
   122  								UPSERT INTO `+"`"+path.Join(db.Name(), folder, tableName)+"`"+`
   123  								SELECT
   124  									val 
   125  								FROM
   126  									AS_TABLE($values);            
   127  							`, table.NewQueryParameters(
   128  									table.ValueParam(
   129  										"$values",
   130  										types.ListValue(values...),
   131  									),
   132  								),
   133  							)
   134  							return err
   135  						},
   136  						table.WithIdempotent(),
   137  					); err != nil {
   138  						t.Fatalf("upsert failed: %v\n", err)
   139  					} else {
   140  						upserted += uint32(to - from)
   141  					}
   142  				})
   143  			}
   144  			t.Run("check", func(t *testing.T) {
   145  				t.Run("upserted", func(t *testing.T) {
   146  					t.Run("rows", func(t *testing.T) {
   147  						if upserted != uint32(upsertRowsCount) {
   148  							t.Fatalf("wrong rows count: %v, expected: %d", upserted, upsertRowsCount)
   149  						}
   150  						err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
   151  							_, res, err := s.Execute(ctx, table.DefaultTxControl(),
   152  								"SELECT CAST(COUNT(*) AS Uint64) FROM `"+path.Join(db.Name(), folder, tableName)+"`;",
   153  								nil,
   154  							)
   155  							if err != nil {
   156  								return err
   157  							}
   158  							if !res.NextResultSet(ctx) {
   159  								return fmt.Errorf("no result sets")
   160  							}
   161  							if !res.NextRow() {
   162  								return fmt.Errorf("no rows")
   163  							}
   164  							var rowsFromDb uint64
   165  							if err := res.ScanWithDefaults(indexed.Required(&rowsFromDb)); err != nil {
   166  								return err
   167  							}
   168  							if rowsFromDb != uint64(upsertRowsCount) {
   169  								return fmt.Errorf("wrong rows count: %d, expected: %d",
   170  									rowsFromDb,
   171  									upsertRowsCount,
   172  								)
   173  							}
   174  							return res.Err()
   175  						}, table.WithIdempotent())
   176  						require.NoError(t, err)
   177  						err = db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
   178  							_, res, err := s.Execute(ctx, table.DefaultTxControl(),
   179  								"SELECT CAST(SUM(val) AS Uint64) FROM `"+path.Join(db.Name(), folder, tableName)+"`;",
   180  								nil,
   181  							)
   182  							if err != nil {
   183  								return err
   184  							}
   185  							if !res.NextResultSet(ctx) {
   186  								return fmt.Errorf("no result sets")
   187  							}
   188  							if !res.NextRow() {
   189  								return fmt.Errorf("no rows")
   190  							}
   191  							var checkSumFromDb uint64
   192  							if err := res.ScanWithDefaults(indexed.Required(&checkSumFromDb)); err != nil {
   193  								return err
   194  							}
   195  							if checkSumFromDb != expectedCheckSum {
   196  								return fmt.Errorf("wrong checksum: %d, expected: %d",
   197  									checkSumFromDb,
   198  									expectedCheckSum,
   199  								)
   200  							}
   201  							return res.Err()
   202  						}, table.WithIdempotent())
   203  						require.NoError(t, err)
   204  					})
   205  				})
   206  			})
   207  		})
   208  	})
   209  
   210  	t.Run("make", func(t *testing.T) {
   211  		t.Run("child", func(t *testing.T) {
   212  			t.Run("discovered", func(t *testing.T) {
   213  				t.Run("connection", func(t *testing.T) {
   214  					db, err = db.With(ctx, ydb.WithDiscoveryInterval(discoveryInterval))
   215  					if err != nil {
   216  						t.Fatal(err)
   217  					}
   218  				})
   219  			})
   220  		})
   221  	})
   222  
   223  	defer func(db *ydb.Driver) {
   224  		// cleanup
   225  		_ = db.Close(ctx)
   226  	}(db)
   227  
   228  	t.Run("execute", func(t *testing.T) {
   229  		t.Run("stream", func(t *testing.T) {
   230  			t.Run("query", func(t *testing.T) {
   231  				if err = db.Table().Do(ctx,
   232  					func(ctx context.Context, s table.Session) (err error) {
   233  						var (
   234  							start     = time.Now()
   235  							rowsCount = 0
   236  							checkSum  = uint64(0)
   237  						)
   238  						res, err := s.StreamExecuteScanQuery(ctx,
   239  							"SELECT val FROM `"+path.Join(db.Name(), folder, tableName)+"`;", nil,
   240  						)
   241  						if err != nil {
   242  							return err
   243  						}
   244  						defer func() {
   245  							_ = res.Close()
   246  						}()
   247  						for res.NextResultSet(ctx) {
   248  							count := 0
   249  							for res.NextRow() {
   250  								count++
   251  								var val int64
   252  								if err = res.ScanWithDefaults(indexed.Required(&val)); err != nil {
   253  									return err
   254  								}
   255  								checkSum += uint64(val)
   256  							}
   257  							rowsCount += count
   258  							time.Sleep(discoveryInterval)
   259  						}
   260  						if err = res.Err(); err != nil {
   261  							return fmt.Errorf("received error (duration: %v): %w", time.Since(start), err)
   262  						}
   263  						if rowsCount != upsertRowsCount {
   264  							return fmt.Errorf("wrong rows count: %v, expected: %d (duration: %v)",
   265  								rowsCount,
   266  								upsertRowsCount,
   267  								time.Since(start),
   268  							)
   269  						}
   270  						if checkSum != expectedCheckSum {
   271  							return fmt.Errorf("wrong checksum: %d, expected: %d",
   272  								checkSum,
   273  								expectedCheckSum,
   274  							)
   275  						}
   276  						return res.Err()
   277  					},
   278  					table.WithIdempotent(),
   279  				); err != nil {
   280  					t.Fatalf("stream query failed: %v\n", err)
   281  				}
   282  			})
   283  		})
   284  	})
   285  
   286  	t.Run("stream", func(t *testing.T) {
   287  		t.Run("read", func(t *testing.T) {
   288  			t.Run("table", func(t *testing.T) {
   289  				if err = db.Table().Do(ctx,
   290  					func(ctx context.Context, s table.Session) (err error) {
   291  						var (
   292  							start     = time.Now()
   293  							rowsCount = 0
   294  							checkSum  = uint64(0)
   295  						)
   296  						res, err := s.StreamReadTable(ctx, path.Join(db.Name(), folder, tableName), options.ReadColumn("val"))
   297  						if err != nil {
   298  							return err
   299  						}
   300  						defer func() {
   301  							_ = res.Close()
   302  						}()
   303  						for res.NextResultSet(ctx) {
   304  							count := 0
   305  							for res.NextRow() {
   306  								count++
   307  								var val int64
   308  								if err = res.ScanWithDefaults(indexed.Required(&val)); err != nil {
   309  									return err
   310  								}
   311  								checkSum += uint64(val)
   312  							}
   313  							rowsCount += count
   314  							time.Sleep(discoveryInterval)
   315  						}
   316  						if err = res.Err(); err != nil {
   317  							return fmt.Errorf("received error (duration: %v): %w", time.Since(start), err)
   318  						}
   319  						if rowsCount != upsertRowsCount {
   320  							return fmt.Errorf("wrong rows count: %v, expected: %d (duration: %v)",
   321  								rowsCount,
   322  								upsertRowsCount,
   323  								time.Since(start),
   324  							)
   325  						}
   326  						if checkSum != expectedCheckSum {
   327  							return fmt.Errorf("wrong checksum: %d, expected: %d",
   328  								checkSum,
   329  								expectedCheckSum,
   330  							)
   331  						}
   332  						return res.Err()
   333  					},
   334  					table.WithIdempotent(),
   335  				); err != nil {
   336  					t.Fatalf("stream query failed: %v\n", err)
   337  				}
   338  			})
   339  		})
   340  	})
   341  }