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

     1  //go:build integration
     2  // +build integration
     3  
     4  package integration
     5  
     6  import (
     7  	"context"
     8  	"crypto/sha256"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"os"
    12  	"path"
    13  	"testing"
    14  	"time"
    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/types"
    21  )
    22  
    23  func TestSplitRangesAndRead(t *testing.T) {
    24  	var (
    25  		tableName       = `ranges_table`
    26  		db              *ydb.Driver
    27  		err             error
    28  		upsertRowsCount = 100000
    29  		batchSize       = 10000
    30  	)
    31  
    32  	ctx := xtest.Context(t)
    33  
    34  	db, err = ydb.Open(ctx,
    35  		os.Getenv("YDB_CONNECTION_STRING"),
    36  		ydb.WithAccessTokenCredentials(
    37  			os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS"),
    38  		),
    39  		ydb.WithDiscoveryInterval(0), // disable re-discovery on upsert time
    40  	)
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	defer func(db *ydb.Driver) {
    45  		// cleanup
    46  		_ = db.Close(ctx)
    47  	}(db)
    48  
    49  	t.Run("creating table", func(t *testing.T) {
    50  		if err = db.Table().Do(ctx,
    51  			func(ctx context.Context, s table.Session) (err error) {
    52  				_, err = s.DescribeTable(ctx, path.Join(db.Name(), tableName))
    53  				if err == nil {
    54  					if err = s.DropTable(ctx, path.Join(db.Name(), tableName)); err != nil {
    55  						return err
    56  					}
    57  				}
    58  				return s.ExecuteSchemeQuery(
    59  					ctx,
    60  					`CREATE TABLE `+tableName+` (
    61  						id Uint64,
    62  						PRIMARY KEY (id)
    63  					)
    64  					WITH (
    65  						UNIFORM_PARTITIONS = 8
    66  					)`,
    67  				)
    68  			},
    69  			table.WithIdempotent(),
    70  		); err != nil {
    71  			t.Fatalf("create table failed: %v\n", err)
    72  		}
    73  	})
    74  
    75  	t.Run("check batch size", func(t *testing.T) {
    76  		if upsertRowsCount%batchSize != 0 {
    77  			t.Fatalf("wrong batch size: (%d mod %d = %d) != 0", upsertRowsCount, batchSize, upsertRowsCount%batchSize)
    78  		}
    79  	})
    80  
    81  	t.Run("upserting rows", func(t *testing.T) {
    82  		var upserted uint32
    83  		for i := 0; i < (upsertRowsCount / batchSize); i++ {
    84  			from, to := uint32(i*batchSize), uint32((i+1)*batchSize)
    85  			t.Run(fmt.Sprintf("upserting %v...%v", from, to-1), func(t *testing.T) {
    86  				values := make([]types.Value, 0, batchSize)
    87  				for j := from; j < to; j++ {
    88  					b := make([]byte, 4)
    89  					binary.BigEndian.PutUint32(b, j)
    90  					s := sha256.Sum224(b)
    91  					values = append(
    92  						values,
    93  						types.StructValue(
    94  							types.StructFieldValue("id", types.Uint64Value(binary.BigEndian.Uint64(s[:]))),
    95  						),
    96  					)
    97  				}
    98  				if err = db.Table().Do(ctx,
    99  					func(ctx context.Context, s table.Session) (err error) {
   100  						_, _, err = s.Execute(
   101  							ctx,
   102  							table.TxControl(
   103  								table.BeginTx(
   104  									table.WithSerializableReadWrite(),
   105  								),
   106  								table.CommitTx(),
   107  							), `
   108  								DECLARE $values AS List<Struct<
   109  									id: Uint64,
   110  								>>;
   111  								UPSERT INTO `+"`"+path.Join(db.Name(), tableName)+"`"+`
   112  								SELECT
   113  									id 
   114  								FROM
   115  									AS_TABLE($values);            
   116  							`, table.NewQueryParameters(
   117  								table.ValueParam(
   118  									"$values",
   119  									types.ListValue(values...),
   120  								),
   121  							),
   122  						)
   123  						return err
   124  					},
   125  					table.WithIdempotent(),
   126  				); err != nil {
   127  					t.Fatalf("upsert failed: %v\n", err)
   128  				} else {
   129  					upserted += to - from
   130  				}
   131  			})
   132  		}
   133  		t.Run("check upserted rows", func(t *testing.T) {
   134  			if upserted != uint32(upsertRowsCount) {
   135  				t.Fatalf("wrong rows count: %v, expected: %d", upserted, upsertRowsCount)
   136  			}
   137  		})
   138  	})
   139  
   140  	var ranges []options.KeyRange
   141  
   142  	t.Run("make ranges", func(t *testing.T) {
   143  		if err = db.Table().Do(ctx,
   144  			func(ctx context.Context, s table.Session) (err error) {
   145  				d, err := s.DescribeTable(ctx,
   146  					path.Join(db.Name(), tableName),
   147  					options.WithShardKeyBounds(),
   148  				)
   149  				if err != nil {
   150  					return err
   151  				}
   152  				for _, r := range d.KeyRanges {
   153  					if r.From == nil || r.To == nil {
   154  						ranges = append(ranges, r)
   155  					} else {
   156  						var from, to uint64
   157  						if err := types.CastTo(r.From, &from); err != nil {
   158  							return err
   159  						}
   160  						if err := types.CastTo(r.To, &to); err != nil {
   161  							return err
   162  						}
   163  						ranges = append(ranges,
   164  							options.KeyRange{
   165  								From: r.From,
   166  								To: types.TupleValue(
   167  									types.OptionalValue(types.Uint64Value(from + (to-from)/2)),
   168  								),
   169  							},
   170  							options.KeyRange{
   171  								From: types.TupleValue(
   172  									types.OptionalValue(types.Uint64Value(from + (to-from)/2)),
   173  								),
   174  								To: r.To,
   175  							},
   176  						)
   177  					}
   178  				}
   179  				return nil
   180  			},
   181  			table.WithIdempotent(),
   182  		); err != nil {
   183  			t.Fatalf("stream query failed: %v\n", err)
   184  		}
   185  	})
   186  
   187  	t.Run("read ranges", func(t *testing.T) {
   188  		var (
   189  			start     = time.Now()
   190  			rowsCount = 0
   191  		)
   192  		for _, r := range ranges {
   193  			if err = db.Table().Do(ctx,
   194  				func(ctx context.Context, s table.Session) (err error) {
   195  					res, err := s.StreamReadTable(ctx, path.Join(db.Name(), tableName), options.ReadKeyRange(r))
   196  					if err != nil {
   197  						return err
   198  					}
   199  					defer func() {
   200  						_ = res.Close()
   201  					}()
   202  					for res.NextResultSet(ctx) {
   203  						count := 0
   204  						for res.NextRow() {
   205  							count++
   206  						}
   207  						rowsCount += count
   208  					}
   209  					if err = res.Err(); err != nil {
   210  						return fmt.Errorf("received error (duration: %v): %w", time.Since(start), err)
   211  					}
   212  					return nil
   213  				},
   214  				table.WithIdempotent(),
   215  			); err != nil {
   216  				t.Fatalf("stream query failed: %v\n", err)
   217  			}
   218  		}
   219  		if rowsCount != upsertRowsCount {
   220  			t.Errorf("wrong rows count: %v, expected: %d (duration: %v, ranges: %v)",
   221  				rowsCount,
   222  				upsertRowsCount,
   223  				time.Since(start),
   224  				ranges,
   225  			)
   226  		}
   227  	})
   228  }