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 }