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