code.gitea.io/gitea@v1.19.3/modules/queue/queue_channel_test.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package queue
     5  
     6  import (
     7  	"os"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"code.gitea.io/gitea/modules/log"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestChannelQueue(t *testing.T) {
    18  	handleChan := make(chan *testData)
    19  	handle := func(data ...Data) []Data {
    20  		for _, datum := range data {
    21  			testDatum := datum.(*testData)
    22  			handleChan <- testDatum
    23  		}
    24  		return nil
    25  	}
    26  
    27  	nilFn := func(_ func()) {}
    28  
    29  	queue, err := NewChannelQueue(handle,
    30  		ChannelQueueConfiguration{
    31  			WorkerPoolConfiguration: WorkerPoolConfiguration{
    32  				QueueLength:  0,
    33  				MaxWorkers:   10,
    34  				BlockTimeout: 1 * time.Second,
    35  				BoostTimeout: 5 * time.Minute,
    36  				BoostWorkers: 5,
    37  				Name:         "TestChannelQueue",
    38  			},
    39  			Workers: 0,
    40  		}, &testData{})
    41  	assert.NoError(t, err)
    42  
    43  	assert.Equal(t, 5, queue.(*ChannelQueue).WorkerPool.boostWorkers)
    44  
    45  	go queue.Run(nilFn, nilFn)
    46  
    47  	test1 := testData{"A", 1}
    48  	go queue.Push(&test1)
    49  	result1 := <-handleChan
    50  	assert.Equal(t, test1.TestString, result1.TestString)
    51  	assert.Equal(t, test1.TestInt, result1.TestInt)
    52  
    53  	err = queue.Push(test1)
    54  	assert.Error(t, err)
    55  }
    56  
    57  func TestChannelQueue_Batch(t *testing.T) {
    58  	handleChan := make(chan *testData)
    59  	handle := func(data ...Data) []Data {
    60  		assert.True(t, len(data) == 2)
    61  		for _, datum := range data {
    62  			testDatum := datum.(*testData)
    63  			handleChan <- testDatum
    64  		}
    65  		return nil
    66  	}
    67  
    68  	nilFn := func(_ func()) {}
    69  
    70  	queue, err := NewChannelQueue(handle,
    71  		ChannelQueueConfiguration{
    72  			WorkerPoolConfiguration: WorkerPoolConfiguration{
    73  				QueueLength:  20,
    74  				BatchLength:  2,
    75  				BlockTimeout: 0,
    76  				BoostTimeout: 0,
    77  				BoostWorkers: 0,
    78  				MaxWorkers:   10,
    79  			},
    80  			Workers: 1,
    81  		}, &testData{})
    82  	assert.NoError(t, err)
    83  
    84  	go queue.Run(nilFn, nilFn)
    85  
    86  	test1 := testData{"A", 1}
    87  	test2 := testData{"B", 2}
    88  
    89  	queue.Push(&test1)
    90  	go queue.Push(&test2)
    91  
    92  	result1 := <-handleChan
    93  	assert.Equal(t, test1.TestString, result1.TestString)
    94  	assert.Equal(t, test1.TestInt, result1.TestInt)
    95  
    96  	result2 := <-handleChan
    97  	assert.Equal(t, test2.TestString, result2.TestString)
    98  	assert.Equal(t, test2.TestInt, result2.TestInt)
    99  
   100  	err = queue.Push(test1)
   101  	assert.Error(t, err)
   102  }
   103  
   104  func TestChannelQueue_Pause(t *testing.T) {
   105  	if os.Getenv("CI") != "" {
   106  		t.Skip("Skipping because test is flaky on CI")
   107  	}
   108  	lock := sync.Mutex{}
   109  	var queue Queue
   110  	var err error
   111  	pushBack := false
   112  	handleChan := make(chan *testData)
   113  	handle := func(data ...Data) []Data {
   114  		lock.Lock()
   115  		if pushBack {
   116  			if pausable, ok := queue.(Pausable); ok {
   117  				pausable.Pause()
   118  			}
   119  			lock.Unlock()
   120  			return data
   121  		}
   122  		lock.Unlock()
   123  
   124  		for _, datum := range data {
   125  			testDatum := datum.(*testData)
   126  			handleChan <- testDatum
   127  		}
   128  		return nil
   129  	}
   130  
   131  	queueShutdown := []func(){}
   132  	queueTerminate := []func(){}
   133  
   134  	terminated := make(chan struct{})
   135  
   136  	queue, err = NewChannelQueue(handle,
   137  		ChannelQueueConfiguration{
   138  			WorkerPoolConfiguration: WorkerPoolConfiguration{
   139  				QueueLength:  20,
   140  				BatchLength:  1,
   141  				BlockTimeout: 0,
   142  				BoostTimeout: 0,
   143  				BoostWorkers: 0,
   144  				MaxWorkers:   10,
   145  			},
   146  			Workers: 1,
   147  		}, &testData{})
   148  	assert.NoError(t, err)
   149  
   150  	go func() {
   151  		queue.Run(func(shutdown func()) {
   152  			lock.Lock()
   153  			defer lock.Unlock()
   154  			queueShutdown = append(queueShutdown, shutdown)
   155  		}, func(terminate func()) {
   156  			lock.Lock()
   157  			defer lock.Unlock()
   158  			queueTerminate = append(queueTerminate, terminate)
   159  		})
   160  		close(terminated)
   161  	}()
   162  
   163  	// Shutdown and Terminate in defer
   164  	defer func() {
   165  		lock.Lock()
   166  		callbacks := make([]func(), len(queueShutdown))
   167  		copy(callbacks, queueShutdown)
   168  		lock.Unlock()
   169  		for _, callback := range callbacks {
   170  			callback()
   171  		}
   172  		lock.Lock()
   173  		log.Info("Finally terminating")
   174  		callbacks = make([]func(), len(queueTerminate))
   175  		copy(callbacks, queueTerminate)
   176  		lock.Unlock()
   177  		for _, callback := range callbacks {
   178  			callback()
   179  		}
   180  	}()
   181  
   182  	test1 := testData{"A", 1}
   183  	test2 := testData{"B", 2}
   184  	queue.Push(&test1)
   185  
   186  	pausable, ok := queue.(Pausable)
   187  	if !assert.True(t, ok) {
   188  		return
   189  	}
   190  	result1 := <-handleChan
   191  	assert.Equal(t, test1.TestString, result1.TestString)
   192  	assert.Equal(t, test1.TestInt, result1.TestInt)
   193  
   194  	pausable.Pause()
   195  
   196  	paused, _ := pausable.IsPausedIsResumed()
   197  
   198  	select {
   199  	case <-paused:
   200  	case <-time.After(100 * time.Millisecond):
   201  		assert.Fail(t, "Queue is not paused")
   202  		return
   203  	}
   204  
   205  	queue.Push(&test2)
   206  
   207  	var result2 *testData
   208  	select {
   209  	case result2 = <-handleChan:
   210  		assert.Fail(t, "handler chan should be empty")
   211  	case <-time.After(100 * time.Millisecond):
   212  	}
   213  
   214  	assert.Nil(t, result2)
   215  
   216  	pausable.Resume()
   217  	_, resumed := pausable.IsPausedIsResumed()
   218  
   219  	select {
   220  	case <-resumed:
   221  	case <-time.After(100 * time.Millisecond):
   222  		assert.Fail(t, "Queue should be resumed")
   223  	}
   224  
   225  	select {
   226  	case result2 = <-handleChan:
   227  	case <-time.After(500 * time.Millisecond):
   228  		assert.Fail(t, "handler chan should contain test2")
   229  	}
   230  
   231  	assert.Equal(t, test2.TestString, result2.TestString)
   232  	assert.Equal(t, test2.TestInt, result2.TestInt)
   233  
   234  	lock.Lock()
   235  	pushBack = true
   236  	lock.Unlock()
   237  
   238  	_, resumed = pausable.IsPausedIsResumed()
   239  
   240  	select {
   241  	case <-resumed:
   242  	case <-time.After(100 * time.Millisecond):
   243  		assert.Fail(t, "Queue is not resumed")
   244  		return
   245  	}
   246  
   247  	queue.Push(&test1)
   248  	paused, _ = pausable.IsPausedIsResumed()
   249  
   250  	select {
   251  	case <-paused:
   252  	case <-handleChan:
   253  		assert.Fail(t, "handler chan should not contain test1")
   254  		return
   255  	case <-time.After(100 * time.Millisecond):
   256  		assert.Fail(t, "queue should be paused")
   257  		return
   258  	}
   259  
   260  	lock.Lock()
   261  	pushBack = false
   262  	lock.Unlock()
   263  
   264  	paused, _ = pausable.IsPausedIsResumed()
   265  
   266  	select {
   267  	case <-paused:
   268  	case <-time.After(100 * time.Millisecond):
   269  		assert.Fail(t, "Queue is not paused")
   270  		return
   271  	}
   272  
   273  	pausable.Resume()
   274  	_, resumed = pausable.IsPausedIsResumed()
   275  
   276  	select {
   277  	case <-resumed:
   278  	case <-time.After(100 * time.Millisecond):
   279  		assert.Fail(t, "Queue should be resumed")
   280  	}
   281  
   282  	select {
   283  	case result1 = <-handleChan:
   284  	case <-time.After(500 * time.Millisecond):
   285  		assert.Fail(t, "handler chan should contain test1")
   286  	}
   287  	assert.Equal(t, test1.TestString, result1.TestString)
   288  	assert.Equal(t, test1.TestInt, result1.TestInt)
   289  
   290  	lock.Lock()
   291  	callbacks := make([]func(), len(queueShutdown))
   292  	copy(callbacks, queueShutdown)
   293  	queueShutdown = queueShutdown[:0]
   294  	lock.Unlock()
   295  	// Now shutdown the queue
   296  	for _, callback := range callbacks {
   297  		callback()
   298  	}
   299  
   300  	// terminate the queue
   301  	lock.Lock()
   302  	callbacks = make([]func(), len(queueTerminate))
   303  	copy(callbacks, queueTerminate)
   304  	queueShutdown = queueTerminate[:0]
   305  	lock.Unlock()
   306  	for _, callback := range callbacks {
   307  		callback()
   308  	}
   309  	select {
   310  	case <-terminated:
   311  	case <-time.After(10 * time.Second):
   312  		assert.Fail(t, "Queue should have terminated")
   313  		return
   314  	}
   315  }