github.com/polarismesh/polaris@v1.17.8/common/batchjob/batch_test.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package batchjob
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"math/rand"
    25  	"sync"
    26  	"sync/atomic"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  )
    32  
    33  func TestNewBatchController(t *testing.T) {
    34  	total := 1000
    35  
    36  	totalTasks := int32(0)
    37  	testHandle := func(futures []Future) {
    38  		atomic.AddInt32(&totalTasks, int32(len(futures)))
    39  		time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
    40  		for i := range futures {
    41  			futures[i].Reply(nil, nil)
    42  		}
    43  	}
    44  
    45  	ctrl := NewBatchController(context.Background(), CtrlConfig{
    46  		QueueSize:     32,
    47  		MaxBatchCount: 16,
    48  		WaitTime:      32 * time.Millisecond,
    49  		Concurrency:   8,
    50  		Handler:       testHandle,
    51  	})
    52  
    53  	wg := &sync.WaitGroup{}
    54  
    55  	for i := 0; i < total; i++ {
    56  		wg.Add(1)
    57  		go func(i int) {
    58  			defer wg.Done()
    59  			future := ctrl.Submit(fmt.Sprintf("%d", i))
    60  			_, _ = future.Done()
    61  		}(i)
    62  	}
    63  
    64  	wg.Wait()
    65  	assert.Equal(t, total, int(atomic.LoadInt32(&totalTasks)))
    66  	ctrl.Stop()
    67  }
    68  
    69  func TestNewBatchControllerTSubmitTimeout(t *testing.T) {
    70  	total := 1000
    71  
    72  	totalTasks := int32(0)
    73  	testHandle := func(futures []Future) {
    74  		time.Sleep(100 * time.Millisecond)
    75  		atomic.AddInt32(&totalTasks, int32(len(futures)))
    76  		for i := range futures {
    77  			futures[i].Reply(nil, nil)
    78  		}
    79  	}
    80  
    81  	ctrl := NewBatchController(context.Background(), CtrlConfig{
    82  		QueueSize:     1,
    83  		MaxBatchCount: uint32(total * 2),
    84  		WaitTime:      32 * time.Millisecond,
    85  		Concurrency:   8,
    86  		Handler:       testHandle,
    87  	})
    88  
    89  	wg := &sync.WaitGroup{}
    90  
    91  	for i := 0; i < total; i++ {
    92  		wg.Add(1)
    93  		go func(i int) {
    94  			defer wg.Done()
    95  			future := ctrl.SubmitWithTimeout(fmt.Sprintf("%d", i), time.Millisecond)
    96  			_, err := future.Done()
    97  			if err != nil {
    98  				assert.True(t, errors.Is(err, ErrorSubmitTaskTimeout), err)
    99  			}
   100  		}(i)
   101  	}
   102  
   103  	wg.Wait()
   104  	ctrl.Stop()
   105  }
   106  
   107  func TestNewBatchControllerDoneTimeout(t *testing.T) {
   108  	total := 100
   109  
   110  	totalTasks := int32(0)
   111  	testHandle := func(futures []Future) {
   112  		time.Sleep(100 * time.Millisecond)
   113  		atomic.AddInt32(&totalTasks, int32(len(futures)))
   114  	}
   115  
   116  	ctrl := NewBatchController(context.Background(), CtrlConfig{
   117  		QueueSize:     1,
   118  		MaxBatchCount: uint32(total * 2),
   119  		WaitTime:      32 * time.Millisecond,
   120  		Concurrency:   8,
   121  		Handler:       testHandle,
   122  	})
   123  
   124  	wg := &sync.WaitGroup{}
   125  
   126  	for i := 0; i < total; i++ {
   127  		wg.Add(1)
   128  		go func(i int) {
   129  			defer wg.Done()
   130  			future := ctrl.Submit(fmt.Sprintf("%d", i))
   131  			_, err := future.DoneTimeout(time.Second)
   132  			assert.Error(t, err)
   133  			assert.True(t, errors.Is(err, context.DeadlineExceeded), err)
   134  		}(i)
   135  	}
   136  
   137  	t.Log("BatchController already stop")
   138  	wg.Wait()
   139  	ctrl.Stop()
   140  }
   141  
   142  func TestNewBatchControllerStop(t *testing.T) {
   143  	total := 1000
   144  
   145  	totalTasks := int32(0)
   146  	testHandle := func(futures []Future) {
   147  		atomic.AddInt32(&totalTasks, int32(len(futures)))
   148  		for i := range futures {
   149  			futures[i].Reply(atomic.LoadInt32(&totalTasks), nil)
   150  		}
   151  	}
   152  
   153  	ctrl := NewBatchController(context.Background(), CtrlConfig{
   154  		QueueSize:     uint32(total * 2),
   155  		MaxBatchCount: 64,
   156  		WaitTime:      32 * time.Millisecond,
   157  		Concurrency:   8,
   158  		Handler:       testHandle,
   159  	})
   160  
   161  	sbWg := &sync.WaitGroup{}
   162  	wg := &sync.WaitGroup{}
   163  	sbWg.Add(total)
   164  	wg.Add(total)
   165  	cancelTask := int32(0)
   166  	for i := 0; i < total; i++ {
   167  		go func(i int) {
   168  			defer func() {
   169  				wg.Done()
   170  			}()
   171  			future := ctrl.Submit(fmt.Sprintf("%d", i))
   172  			sbWg.Done()
   173  			_, err := future.Done()
   174  			if err != nil {
   175  				if assert.ErrorIs(t, err, ErrorBatchControllerStopped) {
   176  					atomic.AddInt32(&cancelTask, 1)
   177  				}
   178  			}
   179  		}(i)
   180  	}
   181  
   182  	ctrl.Stop()
   183  	t.Log("BatchController already stop")
   184  	sbWg.Wait()
   185  	t.Logf("cancel jobs : %d", atomic.LoadInt32(&cancelTask))
   186  	wg.Wait()
   187  	t.Log("finish all batch job")
   188  }
   189  
   190  func TestNewBatchControllerGracefulStop(t *testing.T) {
   191  	total := 1000
   192  
   193  	ctrl := NewBatchController(context.Background(), CtrlConfig{
   194  		QueueSize:     uint32(total * 2),
   195  		MaxBatchCount: 64,
   196  		WaitTime:      32 * time.Millisecond,
   197  		Concurrency:   8,
   198  		Handler: func(futures []Future) {
   199  			for i := range futures {
   200  				futures[i].Reply(nil, nil)
   201  			}
   202  		},
   203  	})
   204  
   205  	sbWg := &sync.WaitGroup{}
   206  	wg := &sync.WaitGroup{}
   207  	sbWg.Add(total)
   208  	wg.Add(total)
   209  	submitTask := int32(0)
   210  	cancelTask := int32(0)
   211  	for i := 0; i < total; i++ {
   212  		go func(i int) {
   213  			defer func() {
   214  				wg.Done()
   215  			}()
   216  			future := ctrl.Submit(fmt.Sprintf("%d", i))
   217  			atomic.AddInt32(&submitTask, 1)
   218  			sbWg.Done()
   219  			_, err := future.Done()
   220  			if err != nil {
   221  				if assert.ErrorIs(t, err, ErrorBatchControllerStopped) {
   222  					atomic.AddInt32(&cancelTask, 1)
   223  				}
   224  			}
   225  		}(i)
   226  	}
   227  
   228  	ctrl.GracefulStop()
   229  	t.Log("BatchController already stop")
   230  	sbWg.Wait()
   231  	t.Logf("submit jobs : %d, cancel jobs : %d", atomic.LoadInt32(&submitTask), atomic.LoadInt32(&cancelTask))
   232  	wg.Wait()
   233  	t.Log("finish all batch job")
   234  }