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  }