github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/dlock/redis_dlock_test.go (about)

     1  package dlock
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"os"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	mredisv2 "github.com/alicebob/miniredis/v2"
    12  	"github.com/redis/go-redis/v9"
    13  	redisv9 "github.com/redis/go-redis/v9"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestRedisDLock_MiniRedis(t *testing.T) {
    18  	require.NotEmpty(t, luaDLockAcquireScript)
    19  	require.NotEmpty(t, luaDLockReleaseScript)
    20  	require.NotEmpty(t, luaDLockRenewalTTLScript)
    21  	require.NotEmpty(t, luaDLockLoadTTLScript)
    22  
    23  	const addr = "127.0.0.1:6480"
    24  	mredis := mredisv2.NewMiniRedis()
    25  	defer func() { mredis.Close() }()
    26  	go func() {
    27  		err := mredis.StartAddr(addr)
    28  		require.NoError(t, err)
    29  	}()
    30  
    31  	rclient := redisv9.NewClient(&redisv9.Options{
    32  		Addr:     addr,
    33  		Password: "",
    34  		DB:       0,
    35  	})
    36  	defer func() { _ = rclient.Close() }()
    37  
    38  	_, err := rclient.Set(context.TODO(), "test", "test", 0).Result()
    39  	require.NoError(t, err)
    40  	_, err = rclient.PExpire(context.TODO(), "test", 100*time.Millisecond).Result()
    41  	require.NoError(t, err)
    42  	res, err := rclient.PTTL(context.TODO(), "test").Result()
    43  	require.NoError(t, err)
    44  	t.Log(res)
    45  	require.GreaterOrEqual(t, 100*time.Millisecond, res)
    46  
    47  	lock, err := RedisDLockBuilder(context.TODO(),
    48  		func() redis.Scripter {
    49  			return rclient.Conn()
    50  		},
    51  	).Retry(DefaultExponentialBackoffRetry()).
    52  		TTL(200*time.Millisecond).
    53  		Keys("testKey1_1", "testKey2_1").
    54  		Token("test1").
    55  		Build()
    56  	require.NoError(t, err)
    57  
    58  	err = lock.Unlock()
    59  	require.Error(t, err)
    60  	_, err = lock.TTL()
    61  	require.Error(t, err)
    62  	err = lock.Renewal(300 * time.Millisecond)
    63  	require.Error(t, err)
    64  	err = lock.Renewal(-300 * time.Millisecond)
    65  	require.Error(t, err)
    66  
    67  	err = lock.Lock()
    68  	require.NoError(t, err)
    69  	ttl, err := lock.TTL()
    70  	require.NoError(t, err)
    71  	t.Log("ttl1", ttl)
    72  	err = lock.Renewal(300 * time.Millisecond)
    73  	require.NoError(t, err)
    74  	ttl, err = lock.TTL()
    75  	require.NoError(t, err)
    76  	t.Log("ttl2", ttl)
    77  	err = lock.Unlock()
    78  	require.NoError(t, err)
    79  
    80  	lock, err = RedisDLockBuilder(nil, nil).
    81  		Retry(DefaultExponentialBackoffRetry()).
    82  		TTL(200*time.Millisecond).
    83  		Keys("testKey1_1", "testKey2_1").
    84  		Token("test1").
    85  		Build()
    86  	require.Error(t, err)
    87  
    88  	lock, err = RedisDLockBuilder(nil,
    89  		func() redis.Scripter {
    90  			return rclient.Conn()
    91  		}).
    92  		Retry(DefaultExponentialBackoffRetry()).
    93  		TTL(200*time.Nanosecond).
    94  		Keys("testKey1_1", "testKey2_1").
    95  		Token("test1").
    96  		Build()
    97  	require.Error(t, err)
    98  
    99  	lock, err = RedisDLock(nil,
   100  		func() redis.Scripter {
   101  			return rclient.Conn()
   102  		},
   103  		WithRedisDLockRetry(DefaultExponentialBackoffRetry()),
   104  		WithRedisDLockTTL(200*time.Millisecond),
   105  		WithRedisDLockKeys("testKey1", "testKey2"),
   106  		WithRedisDLockToken("test1"),
   107  	)
   108  	require.NoError(t, err)
   109  }
   110  
   111  func TestRedisDLock_MiniRedis_DataRace(t *testing.T) {
   112  	require.NotEmpty(t, luaDLockAcquireScript)
   113  	require.NotEmpty(t, luaDLockReleaseScript)
   114  	require.NotEmpty(t, luaDLockRenewalTTLScript)
   115  	require.NotEmpty(t, luaDLockLoadTTLScript)
   116  
   117  	const addr = "127.0.0.1:6481"
   118  	mredis := mredisv2.NewMiniRedis()
   119  	defer func() { mredis.Close() }()
   120  	go func() {
   121  		err := mredis.StartAddr(addr)
   122  		require.NoError(t, err)
   123  	}()
   124  
   125  	rclient := redisv9.NewClient(&redisv9.Options{
   126  		Addr:     addr,
   127  		Password: "",
   128  		DB:       0,
   129  	})
   130  	defer func() { _ = rclient.Close() }()
   131  
   132  	_, err := rclient.Set(context.TODO(), "test", "test", 0).Result()
   133  	require.NoError(t, err)
   134  
   135  	lock1, err := RedisDLockBuilder(context.TODO(),
   136  		func() redis.Scripter {
   137  			return rclient.Conn()
   138  		},
   139  	).Retry(DefaultExponentialBackoffRetry()).
   140  		TTL(200*time.Millisecond).
   141  		Keys("testKey1_2", "testKey2_2").
   142  		Token("test1").
   143  		Build()
   144  	require.NoError(t, err)
   145  
   146  	lock2, err := RedisDLockBuilder(context.TODO(),
   147  		func() redis.Scripter {
   148  			return rclient.Conn()
   149  		},
   150  	).Retry(DefaultExponentialBackoffRetry()).
   151  		TTL(200*time.Millisecond).
   152  		Keys("testKey1_2", "testKey2_2").
   153  		Token("test2").
   154  		Build()
   155  	require.NoError(t, err)
   156  
   157  	var wg sync.WaitGroup
   158  	wg.Add(2)
   159  	go func() {
   160  		_err := lock1.Lock()
   161  		if _err != nil {
   162  			t.Logf("lock1 acquire dlock filed")
   163  			require.True(t, errors.Is(_err, ErrDLockAcquireFailed))
   164  			wg.Done()
   165  			return
   166  		}
   167  		time.Sleep(100 * time.Millisecond)
   168  		_err = lock1.Unlock()
   169  		require.NoError(t, _err)
   170  		wg.Done()
   171  	}()
   172  
   173  	go func() {
   174  		_err := lock2.Lock()
   175  		if _err != nil {
   176  			t.Logf("lock2 acquire dlock filed")
   177  			require.True(t, errors.Is(_err, ErrDLockAcquireFailed))
   178  			wg.Done()
   179  			return
   180  		}
   181  		time.Sleep(100 * time.Millisecond)
   182  		_err = lock2.Unlock()
   183  		require.NoError(t, _err)
   184  		wg.Done()
   185  	}()
   186  	wg.Wait()
   187  }
   188  
   189  func TestRedisDLock_SingleRedis(t *testing.T) {
   190  	require.NotEmpty(t, luaDLockAcquireScript)
   191  	require.NotEmpty(t, luaDLockReleaseScript)
   192  	require.NotEmpty(t, luaDLockRenewalTTLScript)
   193  	require.NotEmpty(t, luaDLockLoadTTLScript)
   194  
   195  	addr := os.Getenv("REDIS_DLOCK_ADDR")
   196  	if addr == "" {
   197  		t.Skip("REDIS_DLOCK_ADDR is empty")
   198  	}
   199  	pwd := os.Getenv("REDIS_DLOCK_PWD")
   200  
   201  	rclient := redisv9.NewClient(&redisv9.Options{
   202  		Addr:     addr,
   203  		Password: pwd,
   204  		DB:       0,
   205  	})
   206  	defer func() { _ = rclient.Close() }()
   207  
   208  	_, err := rclient.Set(context.TODO(), "test", "test", 0).Result()
   209  	require.NoError(t, err)
   210  	_, err = rclient.PExpire(context.TODO(), "test", 300*time.Millisecond).Result()
   211  	require.NoError(t, err)
   212  	res, err := rclient.PTTL(context.TODO(), "test").Result()
   213  	require.NoError(t, err)
   214  	t.Log(res)
   215  	require.GreaterOrEqual(t, 300*time.Millisecond, res)
   216  
   217  	lock, err := RedisDLockBuilder(context.TODO(),
   218  		func() redis.Scripter {
   219  			return rclient.Conn()
   220  		},
   221  	).Retry(DefaultExponentialBackoffRetry()).
   222  		TTL(200*time.Millisecond).
   223  		Keys("testKey1_3", "testKey2_4").
   224  		Token("test1").
   225  		Build()
   226  	require.NoError(t, err)
   227  	err = lock.Lock()
   228  	require.NoError(t, err)
   229  	ttl, err := lock.TTL()
   230  	require.NoError(t, err)
   231  	t.Log("ttl1", ttl)
   232  	err = lock.Renewal(300 * time.Millisecond)
   233  	require.NoError(t, err)
   234  	ttl, err = lock.TTL()
   235  	require.NoError(t, err)
   236  	t.Log("ttl2", ttl)
   237  	err = lock.Unlock()
   238  	require.NoError(t, err)
   239  }
   240  
   241  func TestRedisDLock_SingleRedis_DataRace(t *testing.T) {
   242  	require.NotEmpty(t, luaDLockAcquireScript)
   243  	require.NotEmpty(t, luaDLockReleaseScript)
   244  	require.NotEmpty(t, luaDLockRenewalTTLScript)
   245  	require.NotEmpty(t, luaDLockLoadTTLScript)
   246  
   247  	addr := os.Getenv("REDIS_DLOCK_ADDR")
   248  	if addr == "" {
   249  		t.Skip("REDIS_DLOCK_ADDR is empty")
   250  	}
   251  	pwd := os.Getenv("REDIS_DLOCK_PWD")
   252  
   253  	rclient := redisv9.NewClient(&redisv9.Options{
   254  		Addr:     addr,
   255  		Password: pwd,
   256  		DB:       0,
   257  	})
   258  	defer func() { _ = rclient.Close() }()
   259  
   260  	_, err := rclient.Set(context.TODO(), "test", "test", 0).Result()
   261  	require.NoError(t, err)
   262  
   263  	lock1, err := RedisDLockBuilder(context.TODO(),
   264  		func() redis.Scripter {
   265  			return rclient.Conn()
   266  		},
   267  	).Retry(DefaultExponentialBackoffRetry()).
   268  		TTL(200*time.Millisecond).
   269  		Keys("testKey1_4", "testKey2_4").
   270  		Token("test1").
   271  		Build()
   272  	require.NoError(t, err)
   273  
   274  	lock2, err := RedisDLockBuilder(context.TODO(),
   275  		func() redis.Scripter {
   276  			return rclient.Conn()
   277  		},
   278  	).Retry(DefaultExponentialBackoffRetry()).
   279  		TTL(200*time.Millisecond).
   280  		Keys("testKey1_4", "testKey2_4").
   281  		Token("test2").
   282  		Build()
   283  	require.NoError(t, err)
   284  
   285  	var wg sync.WaitGroup
   286  	wg.Add(2)
   287  	go func() {
   288  		_err := lock1.Lock()
   289  		if _err != nil {
   290  			t.Logf("lock1 acquire dlock filed")
   291  			require.True(t, errors.Is(_err, ErrDLockAcquireFailed))
   292  			wg.Done()
   293  			return
   294  		}
   295  		time.Sleep(100 * time.Millisecond)
   296  		_err = lock1.Unlock()
   297  		require.NoError(t, _err)
   298  		wg.Done()
   299  	}()
   300  
   301  	go func() {
   302  		_err := lock2.Lock()
   303  		if _err != nil {
   304  			t.Logf("lock2 acquire dlock filed")
   305  			require.True(t, errors.Is(_err, ErrDLockAcquireFailed))
   306  			wg.Done()
   307  			return
   308  		}
   309  		time.Sleep(100 * time.Millisecond)
   310  		_err = lock2.Unlock()
   311  		require.NoError(t, _err)
   312  		wg.Done()
   313  	}()
   314  	wg.Wait()
   315  }
   316