github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/async/async_initializer_test.go (about)

     1  // Copyright 2024 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package async
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/pingcap/errors"
    23  	"github.com/pingcap/tiflow/cdc/vars"
    24  	"github.com/stretchr/testify/require"
    25  	"go.uber.org/atomic"
    26  )
    27  
    28  type fakePool struct {
    29  	f           func()
    30  	submitErr   error
    31  	submitTimes int
    32  }
    33  
    34  func (f *fakePool) Go(_ context.Context, fn func()) error {
    35  	f.f = fn
    36  	f.submitTimes++
    37  	return f.submitErr
    38  }
    39  
    40  func (f *fakePool) Run(_ context.Context) error {
    41  	return nil
    42  }
    43  
    44  func TestTryInitialize(t *testing.T) {
    45  	initializer := NewInitializer()
    46  	pool := &vars.NonAsyncPool{}
    47  	initialized, err := initializer.TryInitialize(context.Background(),
    48  		func(ctx context.Context) error {
    49  			return nil
    50  		}, pool)
    51  	require.Nil(t, err)
    52  	require.True(t, initialized)
    53  	// Try to initialize again
    54  	initialized, err = initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
    55  		return nil
    56  	}, pool)
    57  	require.Nil(t, err)
    58  	require.True(t, initialized)
    59  	// init failed
    60  	initializer = NewInitializer()
    61  	initialized, err = initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
    62  		return errors.New("failed to init")
    63  	}, pool)
    64  	require.NotNil(t, err)
    65  	require.False(t, initializer.initialized.Load())
    66  	require.True(t, initializer.initializing.Load())
    67  	require.False(t, initialized)
    68  	initialized, err = initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
    69  		return errors.New("failed to init")
    70  	}, pool)
    71  	require.NotNil(t, err)
    72  	require.False(t, initializer.initialized.Load())
    73  	require.True(t, initializer.initializing.Load())
    74  	require.False(t, initialized)
    75  
    76  	// test submit error
    77  	initializer = NewInitializer()
    78  	initialized, err = initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
    79  		return nil
    80  	}, &fakePool{submitErr: errors.New("submit error")})
    81  	require.NotNil(t, err)
    82  	require.False(t, initialized)
    83  	require.False(t, initializer.initialized.Load())
    84  	require.True(t, initializer.initializing.Load())
    85  }
    86  
    87  func TestTerminate(t *testing.T) {
    88  	initializer := NewInitializer()
    89  	pool := &vars.NonAsyncPool{}
    90  	initialized, err := initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
    91  		return nil
    92  	}, pool)
    93  	require.Nil(t, err)
    94  	require.True(t, initialized)
    95  	initializer.Terminate()
    96  	require.False(t, initializer.initialized.Load())
    97  	require.False(t, initializer.initializing.Load())
    98  
    99  	// test submit error
   100  	initializer = NewInitializer()
   101  	fpool := &fakePool{}
   102  	initialized, err = initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
   103  		return nil
   104  	}, fpool)
   105  	require.Nil(t, err)
   106  	require.False(t, initialized)
   107  	require.True(t, initializer.initializing.Load())
   108  	require.Equal(t, 1, fpool.submitTimes)
   109  
   110  	initialized, err = initializer.TryInitialize(context.Background(), func(ctx context.Context) error {
   111  		return nil
   112  	}, fpool)
   113  	require.Nil(t, err)
   114  	require.False(t, initialized)
   115  	require.True(t, initializer.initializing.Load())
   116  	require.Equal(t, 1, fpool.submitTimes)
   117  
   118  	wg := sync.WaitGroup{}
   119  	wg.Add(1)
   120  	terminated := atomic.NewInt32(1)
   121  	go func() {
   122  		defer wg.Done()
   123  		initializer.Terminate()
   124  		require.Equal(t, int32(2), terminated.Swap(3))
   125  	}()
   126  	require.Equal(t, int32(1), terminated.Swap(2))
   127  	time.Sleep(1 * time.Second)
   128  	fpool.f()
   129  	wg.Wait()
   130  	require.Equal(t, int32(3), terminated.Load())
   131  }