@@ -144,6 +144,10 @@ func TestIntegration_ManagedWriter(t *testing.T) {
144
144
t .Parallel ()
145
145
testPendingStream (ctx , t , mwClient , bqClient , dataset )
146
146
})
147
+ t .Run ("SchemaEvolution" , func (t * testing.T ) {
148
+ t .Parallel ()
149
+ testSchemaEvolution (ctx , t , mwClient , bqClient , dataset )
150
+ })
147
151
t .Run ("Instrumentation" , func (t * testing.T ) {
148
152
// Don't run this in parallel, we only want to collect stats from this subtest.
149
153
testInstrumentation (ctx , t , mwClient , bqClient , dataset )
@@ -181,7 +185,7 @@ func testDefaultStream(ctx context.Context, t *testing.T, mwClient *Client, bqCl
181
185
t .Errorf ("failed to marshal message %d: %v" , k , err )
182
186
}
183
187
data := [][]byte {b }
184
- result , err = ms .AppendRows (ctx , data , NoStreamOffset )
188
+ result , err = ms .AppendRows (ctx , data )
185
189
if err != nil {
186
190
t .Errorf ("single-row append %d failed: %v" , k , err )
187
191
}
@@ -201,7 +205,7 @@ func testDefaultStream(ctx context.Context, t *testing.T, mwClient *Client, bqCl
201
205
}
202
206
data = append (data , b )
203
207
}
204
- result , err = ms .AppendRows (ctx , data , NoStreamOffset )
208
+ result , err = ms .AppendRows (ctx , data )
205
209
if err != nil {
206
210
t .Errorf ("grouped-row append failed: %v" , err )
207
211
}
@@ -256,7 +260,7 @@ func testDefaultStreamDynamicJSON(ctx context.Context, t *testing.T, mwClient *C
256
260
if err != nil {
257
261
t .Fatalf ("failed to marshal proto bytes for row %d: %v" , k , err )
258
262
}
259
- result , err = ms .AppendRows (ctx , [][]byte {b }, NoStreamOffset )
263
+ result , err = ms .AppendRows (ctx , [][]byte {b })
260
264
if err != nil {
261
265
t .Errorf ("single-row append %d failed: %v" , k , err )
262
266
}
@@ -305,7 +309,7 @@ func testBufferedStream(ctx context.Context, t *testing.T, mwClient *Client, bqC
305
309
t .Errorf ("failed to marshal message %d: %v" , k , err )
306
310
}
307
311
data := [][]byte {b }
308
- results , err := ms .AppendRows (ctx , data , NoStreamOffset )
312
+ results , err := ms .AppendRows (ctx , data )
309
313
if err != nil {
310
314
t .Errorf ("single-row append %d failed: %v" , k , err )
311
315
}
@@ -358,7 +362,7 @@ func testCommittedStream(ctx context.Context, t *testing.T, mwClient *Client, bq
358
362
t .Errorf ("failed to marshal message %d: %v" , k , err )
359
363
}
360
364
data := [][]byte {b }
361
- result , err = ms .AppendRows (ctx , data , NoStreamOffset )
365
+ result , err = ms .AppendRows (ctx , data , WithOffset ( int64 ( k )) )
362
366
if err != nil {
363
367
t .Errorf ("single-row append %d failed: %v" , k , err )
364
368
}
@@ -397,12 +401,19 @@ func testPendingStream(ctx context.Context, t *testing.T, mwClient *Client, bqCl
397
401
t .Errorf ("failed to marshal message %d: %v" , k , err )
398
402
}
399
403
data := [][]byte {b }
400
- result , err = ms .AppendRows (ctx , data , NoStreamOffset )
404
+ result , err = ms .AppendRows (ctx , data , WithOffset ( int64 ( k )) )
401
405
if err != nil {
402
406
t .Errorf ("single-row append %d failed: %v" , k , err )
403
407
}
408
+ // be explicit about waiting/checking each response.
409
+ off , err := result .GetResult (ctx )
410
+ if err != nil {
411
+ t .Errorf ("response %d error: %v" , k , err )
412
+ }
413
+ if off != int64 (k ) {
414
+ t .Errorf ("offset mismatch, got %d want %d" , off , k )
415
+ }
404
416
}
405
- result .Ready ()
406
417
wantRows := int64 (len (testSimpleData ))
407
418
408
419
// Mark stream complete.
@@ -468,7 +479,7 @@ func testInstrumentation(ctx context.Context, t *testing.T, mwClient *Client, bq
468
479
t .Errorf ("failed to marshal message %d: %v" , k , err )
469
480
}
470
481
data := [][]byte {b }
471
- result , err = ms .AppendRows (ctx , data , NoStreamOffset )
482
+ result , err = ms .AppendRows (ctx , data )
472
483
if err != nil {
473
484
t .Errorf ("single-row append %d failed: %v" , k , err )
474
485
}
@@ -513,6 +524,85 @@ func testInstrumentation(ctx context.Context, t *testing.T, mwClient *Client, bq
513
524
}
514
525
}
515
526
527
+ func testSchemaEvolution (ctx context.Context , t * testing.T , mwClient * Client , bqClient * bigquery.Client , dataset * bigquery.Dataset ) {
528
+ testTable := dataset .Table (tableIDs .New ())
529
+ if err := testTable .Create (ctx , & bigquery.TableMetadata {Schema : testdata .SimpleMessageSchema }); err != nil {
530
+ t .Fatalf ("failed to create test table %s: %v" , testTable .FullyQualifiedName (), err )
531
+ }
532
+
533
+ m := & testdata.SimpleMessageProto2 {}
534
+ descriptorProto := protodesc .ToDescriptorProto (m .ProtoReflect ().Descriptor ())
535
+
536
+ // setup a new stream.
537
+ ms , err := mwClient .NewManagedStream (ctx ,
538
+ WithDestinationTable (fmt .Sprintf ("projects/%s/datasets/%s/tables/%s" , testTable .ProjectID , testTable .DatasetID , testTable .TableID )),
539
+ WithType (CommittedStream ),
540
+ WithSchemaDescriptor (descriptorProto ),
541
+ )
542
+ if err != nil {
543
+ t .Fatalf ("NewManagedStream: %v" , err )
544
+ }
545
+ validateTableConstraints (ctx , t , bqClient , testTable , "before send" ,
546
+ withExactRowCount (0 ))
547
+
548
+ var result * AppendResult
549
+ for k , mesg := range testSimpleData {
550
+ b , err := proto .Marshal (mesg )
551
+ if err != nil {
552
+ t .Errorf ("failed to marshal message %d: %v" , k , err )
553
+ }
554
+ data := [][]byte {b }
555
+ result , err = ms .AppendRows (ctx , data )
556
+ if err != nil {
557
+ t .Errorf ("single-row append %d failed: %v" , k , err )
558
+ }
559
+ }
560
+ // wait for the result to indicate ready, then validate.
561
+ _ , err = result .GetResult (ctx )
562
+ if err != nil {
563
+ t .Errorf ("error on append: %v" , err )
564
+ }
565
+
566
+ validateTableConstraints (ctx , t , bqClient , testTable , "after send" ,
567
+ withExactRowCount (int64 (len (testSimpleData ))))
568
+
569
+ // Now, evolve the underlying table schema.
570
+ _ , err = testTable .Update (ctx , bigquery.TableMetadataToUpdate {Schema : testdata .SimpleMessageEvolvedSchema }, "" )
571
+ if err != nil {
572
+ t .Errorf ("failed to evolve table schema: %v" , err )
573
+ }
574
+
575
+ // TODO: we need a more elegant mechanism for detecting when the backend has registered the schema change.
576
+ // In the continuous case, we'd get it from the response, but the change-and-wait case needs something more.
577
+ time .Sleep (6 * time .Second )
578
+
579
+ // ready descriptor, send an additional append
580
+ m2 := & testdata.SimpleMessageEvolvedProto2 {
581
+ Name : proto .String ("evolved" ),
582
+ Value : proto .Int64 (180 ),
583
+ Other : proto .String ("hello evolution" ),
584
+ }
585
+ descriptorProto = protodesc .ToDescriptorProto (m2 .ProtoReflect ().Descriptor ())
586
+ b , err := proto .Marshal (m2 )
587
+ if err != nil {
588
+ t .Errorf ("failed to marshal evolved message: %v" , err )
589
+ }
590
+ result , err = ms .AppendRows (ctx , [][]byte {b }, UpdateSchemaDescriptor (descriptorProto ))
591
+ if err != nil {
592
+ t .Errorf ("failed evolved append: %v" , err )
593
+ }
594
+ _ , err = result .GetResult (ctx )
595
+ if err != nil {
596
+ t .Errorf ("error on evolved append: %v" , err )
597
+ }
598
+
599
+ validateTableConstraints (ctx , t , bqClient , testTable , "after send" ,
600
+ withExactRowCount (int64 (len (testSimpleData )+ 1 )),
601
+ withNullCount ("name" , 0 ),
602
+ withNonNullCount ("other" , 1 ),
603
+ )
604
+ }
605
+
516
606
func TestIntegration_DetectProjectID (t * testing.T ) {
517
607
ctx := context .Background ()
518
608
testCreds := testutil .Credentials (ctx )
@@ -613,7 +703,7 @@ func testProtoNormalization(ctx context.Context, t *testing.T, mwClient *Client,
613
703
if err != nil {
614
704
t .Fatalf ("NewManagedStream: %v" , err )
615
705
}
616
- result , err := ms .AppendRows (ctx , [][]byte {sampleRow }, NoStreamOffset )
706
+ result , err := ms .AppendRows (ctx , [][]byte {sampleRow })
617
707
if err != nil {
618
708
t .Errorf ("append failed: %v" , err )
619
709
}
0 commit comments