github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/changefeedccl/schemafeed/schema_feed_test.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package schemafeed 10 11 import ( 12 "context" 13 "testing" 14 15 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 16 "github.com/cockroachdb/cockroach/pkg/util/hlc" 17 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 18 "github.com/cockroachdb/errors" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func TestTableHistoryIngestionTracking(t *testing.T) { 23 defer leaktest.AfterTest(t)() 24 25 ctx := context.Background() 26 ts := func(wt int64) hlc.Timestamp { return hlc.Timestamp{WallTime: wt} } 27 validateFn := func(_ context.Context, desc *sqlbase.TableDescriptor) error { 28 if desc.Name != `` { 29 return errors.Newf("descriptor: %s", desc.Name) 30 } 31 return nil 32 } 33 requireChannelEmpty := func(t *testing.T, ch chan error) { 34 t.Helper() 35 select { 36 case err := <-ch: 37 t.Fatalf(`expected empty channel got %v`, err) 38 default: 39 } 40 } 41 42 m := SchemaFeed{} 43 m.mu.highWater = ts(0) 44 45 require.Equal(t, ts(0), m.highWater()) 46 47 // advance 48 require.NoError(t, m.ingestDescriptors(ctx, ts(0), ts(1), nil, validateFn)) 49 require.Equal(t, ts(1), m.highWater()) 50 require.NoError(t, m.ingestDescriptors(ctx, ts(1), ts(2), nil, validateFn)) 51 require.Equal(t, ts(2), m.highWater()) 52 53 // no-ops 54 require.NoError(t, m.ingestDescriptors(ctx, ts(0), ts(1), nil, validateFn)) 55 require.Equal(t, ts(2), m.highWater()) 56 require.NoError(t, m.ingestDescriptors(ctx, ts(1), ts(2), nil, validateFn)) 57 require.Equal(t, ts(2), m.highWater()) 58 59 // overlap 60 require.NoError(t, m.ingestDescriptors(ctx, ts(1), ts(3), nil, validateFn)) 61 require.Equal(t, ts(3), m.highWater()) 62 63 // gap 64 require.EqualError(t, m.ingestDescriptors(ctx, ts(4), ts(5), nil, validateFn), 65 `gap between 0.000000003,0 and 0.000000004,0`) 66 require.Equal(t, ts(3), m.highWater()) 67 68 // validates 69 require.NoError(t, m.ingestDescriptors(ctx, ts(3), ts(4), []*sqlbase.TableDescriptor{ 70 {ID: 0}, 71 }, validateFn)) 72 require.Equal(t, ts(4), m.highWater()) 73 74 // high-water already high enough. fast-path 75 require.NoError(t, m.waitForTS(ctx, ts(3))) 76 require.NoError(t, m.waitForTS(ctx, ts(4))) 77 78 // high-water not there yet. blocks 79 errCh6 := make(chan error, 1) 80 errCh7 := make(chan error, 1) 81 go func() { errCh7 <- m.waitForTS(ctx, ts(7)) }() 82 go func() { errCh6 <- m.waitForTS(ctx, ts(6)) }() 83 requireChannelEmpty(t, errCh6) 84 requireChannelEmpty(t, errCh7) 85 86 // high-water advances, but not enough 87 require.NoError(t, m.ingestDescriptors(ctx, ts(4), ts(5), nil, validateFn)) 88 requireChannelEmpty(t, errCh6) 89 requireChannelEmpty(t, errCh7) 90 91 // high-water advances, unblocks only errCh6 92 require.NoError(t, m.ingestDescriptors(ctx, ts(5), ts(6), nil, validateFn)) 93 require.NoError(t, <-errCh6) 94 requireChannelEmpty(t, errCh7) 95 96 // high-water advances again, unblocks errCh7 97 require.NoError(t, m.ingestDescriptors(ctx, ts(6), ts(7), nil, validateFn)) 98 require.NoError(t, <-errCh7) 99 100 // validate ctx cancellation 101 errCh8 := make(chan error, 1) 102 ctxTS8, cancelTS8 := context.WithCancel(ctx) 103 go func() { errCh8 <- m.waitForTS(ctxTS8, ts(8)) }() 104 requireChannelEmpty(t, errCh8) 105 cancelTS8() 106 require.EqualError(t, <-errCh8, `context canceled`) 107 108 // does not validate, high-water does not change 109 require.EqualError(t, m.ingestDescriptors(ctx, ts(7), ts(10), []*sqlbase.TableDescriptor{ 110 {ID: 0, Name: `whoops!`}, 111 }, validateFn), `descriptor: whoops!`) 112 require.Equal(t, ts(7), m.highWater()) 113 114 // ts 10 has errored, so validate can return its error without blocking 115 require.EqualError(t, m.waitForTS(ctx, ts(10)), `descriptor: whoops!`) 116 117 // ts 8 and 9 are still unknown 118 errCh8 = make(chan error, 1) 119 errCh9 := make(chan error, 1) 120 go func() { errCh8 <- m.waitForTS(ctx, ts(8)) }() 121 go func() { errCh9 <- m.waitForTS(ctx, ts(9)) }() 122 requireChannelEmpty(t, errCh8) 123 requireChannelEmpty(t, errCh9) 124 125 // turns out ts 10 is not a tight bound. ts 9 also has an error 126 require.EqualError(t, m.ingestDescriptors(ctx, ts(7), ts(9), []*sqlbase.TableDescriptor{ 127 {ID: 0, Name: `oh no!`}, 128 }, validateFn), `descriptor: oh no!`) 129 require.Equal(t, ts(7), m.highWater()) 130 require.EqualError(t, <-errCh9, `descriptor: oh no!`) 131 132 // ts 8 is still unknown 133 requireChannelEmpty(t, errCh8) 134 135 // always return the earlist error seen (so waiting for ts 10 immediately 136 // returns the 9 error now, it returned the ts 10 error above) 137 require.EqualError(t, m.waitForTS(ctx, ts(9)), `descriptor: oh no!`) 138 139 // something earlier than ts 10 can still be okay 140 require.NoError(t, m.ingestDescriptors(ctx, ts(7), ts(8), nil, validateFn)) 141 require.Equal(t, ts(8), m.highWater()) 142 require.NoError(t, <-errCh8) 143 }