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 }