package distributedlock_test import ( "context" "strings" "testing" "time" dl "modelRT/distributedlock" constants "modelRT/distributedlock/constant" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" ) var rdb *redis.Client func init() { rdb = redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.104:30001", // pool config PoolSize: 100, // max connections PoolFIFO: true, PoolTimeout: 4 * time.Second, MinIdleConns: 10, // min idle connections MaxIdleConns: 20, // max idle connections // tiemout config DialTimeout: 5 * time.Second, ReadTimeout: 3 * time.Second, WriteTimeout: 3 * time.Second, }) } func TestRWLockRLockAndUnRLock(t *testing.T) { ctx := context.TODO() rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) duration := 10 * time.Second // 第一次加读锁 err := rwLocker.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":") num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rwLocker.UnRLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock rlock and unrlock test success") return } func TestRWLockReentrantRLock(t *testing.T) { ctx := context.TODO() rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) duration := 10 * time.Second // 第一次加读锁 err := rwLocker.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":") num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次加读锁 err = rwLocker.RLock(ctx, duration) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 2, num) // 第一次解读锁 err = rwLocker.UnRLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次解读锁 err = rwLocker.UnRLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock reentrant lock test success") return } func TestRWLockRefreshRLock(t *testing.T) { ctx := context.TODO() rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 10, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) duration := 10 * time.Second // 第一次加读锁 err := rwLocker.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":") num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) time.Sleep(10 * time.Second) script := `return redis.call('httl', KEYS[1], 'fields', '1', ARGV[1]);` result, err := rdb.Eval(ctx, script, []string{rwLocker.Key}, tokenKey).Result() assert.Equal(t, nil, err) // ttls, ok := result.([]interface{}) ttls, ok := result.([]any) assert.Equal(t, true, ok) ttl, ok := ttls[0].(int64) assert.Equal(t, true, ok) compareValue := int64(8) assert.Greater(t, ttl, compareValue) err = rwLocker.UnRLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock refresh lock test success") return } func TestRWLock2ClientRLock(t *testing.T) { ctx := context.TODO() rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":") num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2加读锁 err = rwLocker2.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":") num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rdb.HLen(ctx, rwLocker1.Key).Err() assert.Equal(t, nil, err) hLen := rdb.HLen(ctx, rwLocker1.Key).Val() assert.Equal(t, int64(3), hLen) // locker1解读锁 err = rwLocker1.UnRLock(ctx) assert.Equal(t, nil, err) // locker2解读锁 err = rwLocker2.UnRLock(ctx) assert.Equal(t, nil, err) err = rdb.Exists(ctx, rwLocker1.Key).Err() assert.Equal(t, nil, err) existNum := rdb.Exists(ctx, rwLocker1.Key).Val() assert.Equal(t, int64(0), existNum) t.Log("rwLock 2 client lock test success") return } func TestRWLock2CWith2DifTimeRLock(t *testing.T) { ctx := context.TODO() rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 30, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":") num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2加读锁 err = rwLocker2.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":") num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rdb.HLen(ctx, rwLocker1.Key).Err() assert.Equal(t, nil, err) hLen := rdb.HLen(ctx, rwLocker1.Key).Val() assert.Equal(t, int64(3), hLen) script := `return redis.call('httl', KEYS[1], 'fields', '1', ARGV[1]);` result, err := rdb.Eval(ctx, script, []string{rwLocker1.Key}, tokenKey1).Result() assert.Equal(t, nil, err) // ttls, ok := result.([]interface{}) ttls, ok := result.([]any) assert.Equal(t, true, ok) ttl, ok := ttls[0].(int64) assert.Equal(t, true, ok) compareValue := int64(110) assert.Greater(t, ttl, compareValue) // locker1解读锁 err = rwLocker1.UnRLock(ctx) assert.Equal(t, nil, err) hashTTL := rdb.TTL(ctx, rwLocker1.Key).Val().Seconds() assert.Greater(t, hashTTL, float64(20)) // locker2解读锁 err = rwLocker2.UnRLock(ctx) assert.Equal(t, nil, err) err = rdb.Exists(ctx, rwLocker1.Key).Err() assert.Equal(t, nil, err) existNum := rdb.Exists(ctx, rwLocker1.Key).Val() assert.Equal(t, int64(0), existNum) t.Log("rwLock 2 client lock test success") return } func TestRWLock2CWithTimeTransformRLock(t *testing.T) { ctx := context.TODO() rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 30, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":") num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2加读锁 err = rwLocker2.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":") num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rdb.HLen(ctx, rwLocker1.Key).Err() assert.Equal(t, nil, err) hLen := rdb.HLen(ctx, rwLocker1.Key).Val() assert.Equal(t, int64(3), hLen) hashTTL := rdb.TTL(ctx, rwLocker2.Key).Val().Seconds() assert.Greater(t, hashTTL, float64(100)) // locker2解读锁 err = rwLocker2.UnRLock(ctx) assert.Equal(t, nil, err) time.Sleep(10 * time.Second) hashTTL = rdb.TTL(ctx, rwLocker1.Key).Val().Seconds() assert.Greater(t, hashTTL, float64(15)) // locker1解读锁 err = rwLocker1.UnRLock(ctx) assert.Equal(t, nil, err) err = rdb.Exists(ctx, rwLocker1.Key).Err() assert.Equal(t, nil, err) existNum := rdb.Exists(ctx, rwLocker1.Key).Val() assert.Equal(t, int64(0), existNum) t.Log("rwLock 2 client lock test success") return } func TestRWLockWLockAndUnWLock(t *testing.T) { ctx := context.TODO() rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) duration := 10 * time.Second // 第一次加读锁 err := rwLocker.WLock(ctx, duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":") num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rwLocker.UnWLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock rlock and unrlock test success") return } func TestRWLockReentrantWLock(t *testing.T) { ctx := context.TODO() rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) duration := 10 * time.Second // 第一次加写锁 err := rwLocker.WLock(ctx, duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":") num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次加写锁 err = rwLocker.WLock(ctx, duration) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 2, num) // 第一次解写锁 err = rwLocker.UnWLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次解写锁 err = rwLocker.UnWLock(ctx) assert.Equal(t, nil, err) num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock reentrant lock test success") return } func TestRWLock2CWithRLockAndWLockFailed(t *testing.T) { ctx := context.TODO() rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 30, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":") num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2加写锁锁 duration = 10 * time.Second err = rwLocker2.WLock(ctx, duration) assert.Equal(t, constants.AcquireTimeoutErr, err) err = rwLocker1.UnRLock(ctx) assert.Equal(t, nil, err) t.Log("rwLock 2 client lock test success") return } func TestRWLock2CWithRLockAndWLockSucceed(t *testing.T) { ctx := context.TODO() rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":") num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) go func() { // locker1解写锁 time.Sleep(10 * time.Second) err = rwLocker1.UnRLock(ctx) assert.Equal(t, nil, err) }() // locker2加写锁 duration = 30 * time.Second err = rwLocker2.WLock(ctx, duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":") num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2解写锁 err = rwLocker2.UnWLock(ctx) assert.Equal(t, nil, err) t.Log("rwLock 2 client lock test success") return } func TestRWLock2CWithWLockAndRLock(t *testing.T) { ctx := context.TODO() rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{ LockLeaseTime: 30, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) duration := 10 * time.Second // locker1加写锁 err := rwLocker1.WLock(ctx, duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":") num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) go func() { // locker1解写锁 time.Sleep(10 * time.Second) err = rwLocker1.UnWLock(ctx) assert.Equal(t, nil, err) }() // locker2加读锁 duration = 30 * time.Second err = rwLocker2.RLock(ctx, duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":") num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2解读锁 err = rwLocker2.UnRLock(ctx) assert.Equal(t, nil, err) t.Log("rwLock 2 client lock test success") return }