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 }