github.com/wfusion/gofusion@v1.1.14/test/lock/cases/reentrant_test.go (about)

     1  package cases
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"math/rand"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/suite"
    12  
    13  	"github.com/wfusion/gofusion/common/utils"
    14  	"github.com/wfusion/gofusion/lock"
    15  	"github.com/wfusion/gofusion/log"
    16  	"github.com/wfusion/gofusion/routine"
    17  
    18  	testLock "github.com/wfusion/gofusion/test/lock"
    19  )
    20  
    21  func TestReentrant(t *testing.T) {
    22  	testingSuite := &Reentrant{Test: new(testLock.Test)}
    23  	testingSuite.Init(testingSuite)
    24  	suite.Run(t, testingSuite)
    25  }
    26  
    27  type Reentrant struct {
    28  	*testLock.Test
    29  }
    30  
    31  func (t *Reentrant) BeforeTest(suiteName, testName string) {
    32  	t.Catch(func() {
    33  		log.Info(context.Background(), "right before %s %s", suiteName, testName)
    34  	})
    35  }
    36  
    37  func (t *Reentrant) AfterTest(suiteName, testName string) {
    38  	t.Catch(func() {
    39  		log.Info(context.Background(), "right after %s %s", suiteName, testName)
    40  	})
    41  }
    42  
    43  func (t *Reentrant) TestRedisLua() {
    44  	t.Catch(func() {
    45  		locker := lock.Use("redis_lua", lock.AppName(t.AppName()))
    46  		key := "redis_lua_lock_reentrant_key"
    47  		t.testReentrant(locker, key)
    48  	})
    49  }
    50  
    51  func (t *Reentrant) TestMongo() {
    52  	t.Catch(func() {
    53  		locker := lock.Use("mongo", lock.AppName(t.AppName()))
    54  		key := "mongo_lock_reentrant_key"
    55  		t.testReentrant(locker, key)
    56  	})
    57  }
    58  
    59  func (t *Reentrant) testReentrant(locker lock.Lockable, key string) {
    60  	ctx := context.Background()
    61  	parallel := 100
    62  	wg := new(sync.WaitGroup)
    63  	unsafeInt := 0
    64  	unsafeMap := make(map[int]int, parallel)
    65  	for i := 0; i < parallel; i++ {
    66  		wg.Add(1)
    67  		routine.Go(func(idx int) {
    68  			// jitter within 20ms ~ 50ms
    69  			reentrantKey := utils.ULID()
    70  			time.Sleep(20*time.Millisecond + time.Duration(rand.Float64()*float64(30*time.Millisecond)))
    71  			err := lock.Within(ctx, locker, key, time.Minute, time.Minute, func() (err error) {
    72  				unsafeMap[idx] = idx
    73  				unsafeInt += int(math.Pow(1, 1)) + len([]string{})
    74  
    75  				rwg := new(sync.WaitGroup)
    76  				for j := 0; j < parallel; j++ {
    77  					rwg.Add(1)
    78  					routine.Go(func() {
    79  						t.NoError(locker.Lock(ctx, key, lock.Expire(time.Millisecond), lock.ReentrantKey(reentrantKey)))
    80  						defer t.NoError(locker.Unlock(ctx, key, lock.ReentrantKey(reentrantKey)))
    81  						// jitter within 10ms
    82  						time.Sleep(time.Duration(rand.Float64() * float64(10*time.Millisecond)))
    83  					}, routine.WaitGroup(rwg), routine.AppName(t.AppName()))
    84  				}
    85  				rwg.Wait()
    86  
    87  				return
    88  			}, lock.ReentrantKey(reentrantKey), lock.AppName(t.AppName()))
    89  			t.NoError(err)
    90  		}, routine.Args(i), routine.WaitGroup(wg), routine.AppName(t.AppName()))
    91  	}
    92  
    93  	wg.Wait()
    94  	t.Len(unsafeMap, parallel)
    95  	t.EqualValues(parallel, unsafeInt)
    96  }