github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/queue/queue_disk_channel_test.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package queue
     7  
     8  import (
     9  	"os"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/gitbundle/modules/log"
    15  	"github.com/gitbundle/modules/util"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestPersistableChannelQueue(t *testing.T) {
    21  	handleChan := make(chan *testData)
    22  	handle := func(data ...Data) []Data {
    23  		for _, datum := range data {
    24  			if datum == nil {
    25  				continue
    26  			}
    27  			testDatum := datum.(*testData)
    28  			handleChan <- testDatum
    29  		}
    30  		return nil
    31  	}
    32  
    33  	lock := sync.Mutex{}
    34  	queueShutdown := []func(){}
    35  	queueTerminate := []func(){}
    36  
    37  	tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-test-data")
    38  	assert.NoError(t, err)
    39  	defer util.RemoveAll(tmpDir)
    40  
    41  	queue, err := NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
    42  		DataDir:      tmpDir,
    43  		BatchLength:  2,
    44  		QueueLength:  20,
    45  		Workers:      1,
    46  		BoostWorkers: 0,
    47  		MaxWorkers:   10,
    48  		Name:         "first",
    49  	}, &testData{})
    50  	assert.NoError(t, err)
    51  
    52  	readyForShutdown := make(chan struct{})
    53  	readyForTerminate := make(chan struct{})
    54  
    55  	go queue.Run(func(shutdown func()) {
    56  		lock.Lock()
    57  		defer lock.Unlock()
    58  		select {
    59  		case <-readyForShutdown:
    60  		default:
    61  			close(readyForShutdown)
    62  		}
    63  		queueShutdown = append(queueShutdown, shutdown)
    64  	}, func(terminate func()) {
    65  		lock.Lock()
    66  		defer lock.Unlock()
    67  		select {
    68  		case <-readyForTerminate:
    69  		default:
    70  			close(readyForTerminate)
    71  		}
    72  		queueTerminate = append(queueTerminate, terminate)
    73  	})
    74  
    75  	test1 := testData{"A", 1}
    76  	test2 := testData{"B", 2}
    77  
    78  	err = queue.Push(&test1)
    79  	assert.NoError(t, err)
    80  	go func() {
    81  		err := queue.Push(&test2)
    82  		assert.NoError(t, err)
    83  	}()
    84  
    85  	result1 := <-handleChan
    86  	assert.Equal(t, test1.TestString, result1.TestString)
    87  	assert.Equal(t, test1.TestInt, result1.TestInt)
    88  
    89  	result2 := <-handleChan
    90  	assert.Equal(t, test2.TestString, result2.TestString)
    91  	assert.Equal(t, test2.TestInt, result2.TestInt)
    92  
    93  	// test1 is a testData not a *testData so will be rejected
    94  	err = queue.Push(test1)
    95  	assert.Error(t, err)
    96  
    97  	<-readyForShutdown
    98  	// Now shutdown the queue
    99  	lock.Lock()
   100  	callbacks := make([]func(), len(queueShutdown))
   101  	copy(callbacks, queueShutdown)
   102  	lock.Unlock()
   103  	for _, callback := range callbacks {
   104  		callback()
   105  	}
   106  
   107  	// Wait til it is closed
   108  	<-queue.(*PersistableChannelQueue).closed
   109  
   110  	err = queue.Push(&test1)
   111  	assert.NoError(t, err)
   112  	err = queue.Push(&test2)
   113  	assert.NoError(t, err)
   114  	select {
   115  	case <-handleChan:
   116  		assert.Fail(t, "Handler processing should have stopped")
   117  	default:
   118  	}
   119  
   120  	// terminate the queue
   121  	<-readyForTerminate
   122  	lock.Lock()
   123  	callbacks = make([]func(), len(queueTerminate))
   124  	copy(callbacks, queueTerminate)
   125  	lock.Unlock()
   126  	for _, callback := range callbacks {
   127  		callback()
   128  	}
   129  
   130  	select {
   131  	case <-handleChan:
   132  		assert.Fail(t, "Handler processing should have stopped")
   133  	default:
   134  	}
   135  
   136  	// Reopen queue
   137  	queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
   138  		DataDir:      tmpDir,
   139  		BatchLength:  2,
   140  		QueueLength:  20,
   141  		Workers:      1,
   142  		BoostWorkers: 0,
   143  		MaxWorkers:   10,
   144  		Name:         "second",
   145  	}, &testData{})
   146  	assert.NoError(t, err)
   147  
   148  	readyForShutdown = make(chan struct{})
   149  	readyForTerminate = make(chan struct{})
   150  
   151  	go queue.Run(func(shutdown func()) {
   152  		lock.Lock()
   153  		defer lock.Unlock()
   154  		select {
   155  		case <-readyForShutdown:
   156  		default:
   157  			close(readyForShutdown)
   158  		}
   159  		queueShutdown = append(queueShutdown, shutdown)
   160  	}, func(terminate func()) {
   161  		lock.Lock()
   162  		defer lock.Unlock()
   163  		select {
   164  		case <-readyForTerminate:
   165  		default:
   166  			close(readyForTerminate)
   167  		}
   168  		queueTerminate = append(queueTerminate, terminate)
   169  	})
   170  
   171  	result3 := <-handleChan
   172  	assert.Equal(t, test1.TestString, result3.TestString)
   173  	assert.Equal(t, test1.TestInt, result3.TestInt)
   174  
   175  	result4 := <-handleChan
   176  	assert.Equal(t, test2.TestString, result4.TestString)
   177  	assert.Equal(t, test2.TestInt, result4.TestInt)
   178  
   179  	<-readyForShutdown
   180  	lock.Lock()
   181  	callbacks = make([]func(), len(queueShutdown))
   182  	copy(callbacks, queueShutdown)
   183  	lock.Unlock()
   184  	for _, callback := range callbacks {
   185  		callback()
   186  	}
   187  	<-readyForTerminate
   188  	lock.Lock()
   189  	callbacks = make([]func(), len(queueTerminate))
   190  	copy(callbacks, queueTerminate)
   191  	lock.Unlock()
   192  	for _, callback := range callbacks {
   193  		callback()
   194  	}
   195  }
   196  
   197  func TestPersistableChannelQueue_Pause(t *testing.T) {
   198  	lock := sync.Mutex{}
   199  	var queue Queue
   200  	var err error
   201  	pushBack := false
   202  
   203  	handleChan := make(chan *testData)
   204  	handle := func(data ...Data) []Data {
   205  		lock.Lock()
   206  		if pushBack {
   207  			if pausable, ok := queue.(Pausable); ok {
   208  				log.Info("pausing")
   209  				pausable.Pause()
   210  			}
   211  			lock.Unlock()
   212  			return data
   213  		}
   214  		lock.Unlock()
   215  
   216  		for _, datum := range data {
   217  			testDatum := datum.(*testData)
   218  			handleChan <- testDatum
   219  		}
   220  		return nil
   221  	}
   222  
   223  	queueShutdown := []func(){}
   224  	queueTerminate := []func(){}
   225  	terminated := make(chan struct{})
   226  
   227  	tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-pause-test-data")
   228  	assert.NoError(t, err)
   229  	defer util.RemoveAll(tmpDir)
   230  
   231  	queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
   232  		DataDir:      tmpDir,
   233  		BatchLength:  2,
   234  		QueueLength:  20,
   235  		Workers:      1,
   236  		BoostWorkers: 0,
   237  		MaxWorkers:   10,
   238  		Name:         "first",
   239  	}, &testData{})
   240  	assert.NoError(t, err)
   241  
   242  	go func() {
   243  		queue.Run(func(shutdown func()) {
   244  			lock.Lock()
   245  			defer lock.Unlock()
   246  			queueShutdown = append(queueShutdown, shutdown)
   247  		}, func(terminate func()) {
   248  			lock.Lock()
   249  			defer lock.Unlock()
   250  			queueTerminate = append(queueTerminate, terminate)
   251  		})
   252  		close(terminated)
   253  	}()
   254  
   255  	// Shutdown and Terminate in defer
   256  	defer func() {
   257  		lock.Lock()
   258  		callbacks := make([]func(), len(queueShutdown))
   259  		copy(callbacks, queueShutdown)
   260  		lock.Unlock()
   261  		for _, callback := range callbacks {
   262  			callback()
   263  		}
   264  		lock.Lock()
   265  		log.Info("Finally terminating")
   266  		callbacks = make([]func(), len(queueTerminate))
   267  		copy(callbacks, queueTerminate)
   268  		lock.Unlock()
   269  		for _, callback := range callbacks {
   270  			callback()
   271  		}
   272  	}()
   273  
   274  	test1 := testData{"A", 1}
   275  	test2 := testData{"B", 2}
   276  
   277  	err = queue.Push(&test1)
   278  	assert.NoError(t, err)
   279  
   280  	pausable, ok := queue.(Pausable)
   281  	if !assert.True(t, ok) {
   282  		return
   283  	}
   284  	result1 := <-handleChan
   285  	assert.Equal(t, test1.TestString, result1.TestString)
   286  	assert.Equal(t, test1.TestInt, result1.TestInt)
   287  
   288  	pausable.Pause()
   289  	paused, _ := pausable.IsPausedIsResumed()
   290  
   291  	select {
   292  	case <-paused:
   293  	case <-time.After(100 * time.Millisecond):
   294  		assert.Fail(t, "Queue is not paused")
   295  		return
   296  	}
   297  
   298  	queue.Push(&test2)
   299  
   300  	var result2 *testData
   301  	select {
   302  	case result2 = <-handleChan:
   303  		assert.Fail(t, "handler chan should be empty")
   304  	case <-time.After(100 * time.Millisecond):
   305  	}
   306  
   307  	assert.Nil(t, result2)
   308  
   309  	pausable.Resume()
   310  	_, resumed := pausable.IsPausedIsResumed()
   311  
   312  	select {
   313  	case <-resumed:
   314  	case <-time.After(100 * time.Millisecond):
   315  		assert.Fail(t, "Queue should be resumed")
   316  		return
   317  	}
   318  
   319  	select {
   320  	case result2 = <-handleChan:
   321  	case <-time.After(500 * time.Millisecond):
   322  		assert.Fail(t, "handler chan should contain test2")
   323  	}
   324  
   325  	assert.Equal(t, test2.TestString, result2.TestString)
   326  	assert.Equal(t, test2.TestInt, result2.TestInt)
   327  
   328  	// Set pushBack to so that the next handle will result in a Pause
   329  	lock.Lock()
   330  	pushBack = true
   331  	lock.Unlock()
   332  
   333  	// Ensure that we're still resumed
   334  	_, resumed = pausable.IsPausedIsResumed()
   335  
   336  	select {
   337  	case <-resumed:
   338  	case <-time.After(100 * time.Millisecond):
   339  		assert.Fail(t, "Queue is not resumed")
   340  		return
   341  	}
   342  
   343  	// push test1
   344  	queue.Push(&test1)
   345  
   346  	// Now as this is handled it should pause
   347  	paused, _ = pausable.IsPausedIsResumed()
   348  
   349  	select {
   350  	case <-paused:
   351  	case <-handleChan:
   352  		assert.Fail(t, "handler chan should not contain test1")
   353  		return
   354  	case <-time.After(500 * time.Millisecond):
   355  		assert.Fail(t, "queue should be paused")
   356  		return
   357  	}
   358  
   359  	lock.Lock()
   360  	pushBack = false
   361  	lock.Unlock()
   362  
   363  	pausable.Resume()
   364  
   365  	_, resumed = pausable.IsPausedIsResumed()
   366  	select {
   367  	case <-resumed:
   368  	case <-time.After(500 * time.Millisecond):
   369  		assert.Fail(t, "Queue should be resumed")
   370  		return
   371  	}
   372  
   373  	select {
   374  	case result1 = <-handleChan:
   375  	case <-time.After(500 * time.Millisecond):
   376  		assert.Fail(t, "handler chan should contain test1")
   377  		return
   378  	}
   379  	assert.Equal(t, test1.TestString, result1.TestString)
   380  	assert.Equal(t, test1.TestInt, result1.TestInt)
   381  
   382  	lock.Lock()
   383  	callbacks := make([]func(), len(queueShutdown))
   384  	copy(callbacks, queueShutdown)
   385  	queueShutdown = queueShutdown[:0]
   386  	lock.Unlock()
   387  	// Now shutdown the queue
   388  	for _, callback := range callbacks {
   389  		callback()
   390  	}
   391  
   392  	// Wait til it is closed
   393  	select {
   394  	case <-queue.(*PersistableChannelQueue).closed:
   395  	case <-time.After(5 * time.Second):
   396  		assert.Fail(t, "queue should close")
   397  		return
   398  	}
   399  
   400  	err = queue.Push(&test1)
   401  	assert.NoError(t, err)
   402  	err = queue.Push(&test2)
   403  	assert.NoError(t, err)
   404  	select {
   405  	case <-handleChan:
   406  		assert.Fail(t, "Handler processing should have stopped")
   407  		return
   408  	default:
   409  	}
   410  
   411  	// terminate the queue
   412  	lock.Lock()
   413  	callbacks = make([]func(), len(queueTerminate))
   414  	copy(callbacks, queueTerminate)
   415  	queueShutdown = queueTerminate[:0]
   416  	lock.Unlock()
   417  	for _, callback := range callbacks {
   418  		callback()
   419  	}
   420  
   421  	select {
   422  	case <-handleChan:
   423  		assert.Fail(t, "Handler processing should have stopped")
   424  		return
   425  	case <-terminated:
   426  	case <-time.After(10 * time.Second):
   427  		assert.Fail(t, "Queue should have terminated")
   428  		return
   429  	}
   430  
   431  	lock.Lock()
   432  	pushBack = true
   433  	lock.Unlock()
   434  
   435  	// Reopen queue
   436  	terminated = make(chan struct{})
   437  	queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
   438  		DataDir:      tmpDir,
   439  		BatchLength:  1,
   440  		QueueLength:  20,
   441  		Workers:      1,
   442  		BoostWorkers: 0,
   443  		MaxWorkers:   10,
   444  		Name:         "second",
   445  	}, &testData{})
   446  	assert.NoError(t, err)
   447  	pausable, ok = queue.(Pausable)
   448  	if !assert.True(t, ok) {
   449  		return
   450  	}
   451  
   452  	paused, _ = pausable.IsPausedIsResumed()
   453  
   454  	go func() {
   455  		queue.Run(func(shutdown func()) {
   456  			lock.Lock()
   457  			defer lock.Unlock()
   458  			queueShutdown = append(queueShutdown, shutdown)
   459  		}, func(terminate func()) {
   460  			lock.Lock()
   461  			defer lock.Unlock()
   462  			queueTerminate = append(queueTerminate, terminate)
   463  		})
   464  		close(terminated)
   465  	}()
   466  
   467  	select {
   468  	case <-handleChan:
   469  		assert.Fail(t, "Handler processing should have stopped")
   470  		return
   471  	case <-paused:
   472  	}
   473  
   474  	paused, _ = pausable.IsPausedIsResumed()
   475  
   476  	select {
   477  	case <-paused:
   478  	case <-time.After(500 * time.Millisecond):
   479  		assert.Fail(t, "Queue is not paused")
   480  		return
   481  	}
   482  
   483  	select {
   484  	case <-handleChan:
   485  		assert.Fail(t, "Handler processing should have stopped")
   486  		return
   487  	default:
   488  	}
   489  
   490  	lock.Lock()
   491  	pushBack = false
   492  	lock.Unlock()
   493  
   494  	pausable.Resume()
   495  	_, resumed = pausable.IsPausedIsResumed()
   496  	select {
   497  	case <-resumed:
   498  	case <-time.After(500 * time.Millisecond):
   499  		assert.Fail(t, "Queue should be resumed")
   500  		return
   501  	}
   502  
   503  	var result3, result4 *testData
   504  
   505  	select {
   506  	case result3 = <-handleChan:
   507  	case <-time.After(1 * time.Second):
   508  		assert.Fail(t, "Handler processing should have resumed")
   509  		return
   510  	}
   511  	select {
   512  	case result4 = <-handleChan:
   513  	case <-time.After(1 * time.Second):
   514  		assert.Fail(t, "Handler processing should have resumed")
   515  		return
   516  	}
   517  	if result4.TestString == test1.TestString {
   518  		result3, result4 = result4, result3
   519  	}
   520  	assert.Equal(t, test1.TestString, result3.TestString)
   521  	assert.Equal(t, test1.TestInt, result3.TestInt)
   522  
   523  	assert.Equal(t, test2.TestString, result4.TestString)
   524  	assert.Equal(t, test2.TestInt, result4.TestInt)
   525  
   526  	lock.Lock()
   527  	callbacks = make([]func(), len(queueShutdown))
   528  	copy(callbacks, queueShutdown)
   529  	queueShutdown = queueShutdown[:0]
   530  	lock.Unlock()
   531  	// Now shutdown the queue
   532  	for _, callback := range callbacks {
   533  		callback()
   534  	}
   535  
   536  	// terminate the queue
   537  	lock.Lock()
   538  	callbacks = make([]func(), len(queueTerminate))
   539  	copy(callbacks, queueTerminate)
   540  	queueShutdown = queueTerminate[:0]
   541  	lock.Unlock()
   542  	for _, callback := range callbacks {
   543  		callback()
   544  	}
   545  
   546  	select {
   547  	case <-time.After(10 * time.Second):
   548  		assert.Fail(t, "Queue should have terminated")
   549  		return
   550  	case <-terminated:
   551  	}
   552  }