package distributed_lock import ( "strings" "testing" "time" "github.com/go-redis/redis" "github.com/stretchr/testify/assert" "go.uber.org/zap" ) var log *zap.Logger func init() { log = zap.Must(zap.NewDevelopment()) } func TestRWLockRLockAndUnRLock(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker.logger = log duration := 10 * time.Second // 第一次加读锁 err := rwLocker.RLock(duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") num, err := rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rwLocker.UnRLock() assert.Equal(t, nil, err) num, err = rdb.HGet(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) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker.logger = log duration := 10 * time.Second // 第一次加读锁 err := rwLocker.RLock(duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") num, err := rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次加读锁 err = rwLocker.RLock(duration) assert.Equal(t, nil, err) num, err = rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 2, num) // 第一次解读锁 err = rwLocker.UnRLock() assert.Equal(t, nil, err) num, err = rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次解读锁 err = rwLocker.UnRLock() assert.Equal(t, nil, err) num, err = rdb.HGet(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) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 10, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker.logger = log duration := 10 * time.Second // 第一次加读锁 err := rwLocker.RLock(duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") num, err := rdb.HGet(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(script, []string{rwLocker.key}, tokenKey).Result() assert.Equal(t, nil, err) ttls, ok := result.([]interface{}) 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() assert.Equal(t, nil, err) num, err = rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock refresh lock test success") return } // TODO 设计两个客户端分别加读锁,测试是否可以加锁成功 func TestRWLock2ClientRLock(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker1.logger = log rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) rwLocker2.logger = log duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") num, err := rdb.HGet(rwLocker1.key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2加读锁 err = rwLocker2.RLock(duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") num, err = rdb.HGet(rwLocker2.key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rdb.HLen(rwLocker1.key).Err() assert.Equal(t, nil, err) hLen := rdb.HLen(rwLocker1.key).Val() assert.Equal(t, 3, hLen) // locker1解读锁 err = rwLocker1.UnRLock() assert.Equal(t, nil, err) // locker1解读锁 err = rwLocker2.UnRLock() assert.Equal(t, nil, err) err = rdb.Exists(rwLocker1.key).Err() assert.Equal(t, redis.Nil, err) existNum := rdb.Exists(rwLocker1.key).Val() assert.Equal(t, 0, existNum) t.Log("rwLock 2 client lock test success") return } // TODO 设计两个客户端分别加时间不同的读锁,测试ttl时间在有一个key删除后是否可以变换成功 func TestRWLock2CWith2DifTimeRLock(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker1.logger = log rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 30, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", }) rwLocker2.logger = log duration := 10 * time.Second // locker1加读锁 err := rwLocker1.RLock(duration) assert.Equal(t, nil, err) tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") num, err := rdb.HGet(rwLocker1.key, tokenKey1).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // locker2加读锁 err = rwLocker2.RLock(duration) assert.Equal(t, nil, err) tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") num, err = rdb.HGet(rwLocker2.key, tokenKey2).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rdb.HLen(rwLocker1.key).Err() assert.Equal(t, nil, err) hLen := rdb.HLen(rwLocker1.key).Val() assert.Equal(t, 3, hLen) // locker1解读锁 err = rwLocker1.UnRLock() assert.Equal(t, nil, err) // locker1解读锁 err = rwLocker2.UnRLock() assert.Equal(t, nil, err) err = rdb.Exists(rwLocker1.key).Err() assert.Equal(t, redis.Nil, err) existNum := rdb.Exists(rwLocker1.key).Val() assert.Equal(t, 0, existNum) t.Log("rwLock 2 client lock test success") return } func TestRWLockWLockAndUnWLock(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker.logger = log duration := 10 * time.Second // 第一次加读锁 err := rwLocker.WLock(duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") num, err := rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) err = rwLocker.UnWLock() assert.Equal(t, nil, err) num, err = rdb.HGet(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) { rdb := redis.NewClient(&redis.Options{ Network: "tcp", Addr: "192.168.2.103:6379", Password: "cnstar", PoolSize: 50, DialTimeout: 10 * time.Second, }) rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ LockLeaseTime: 120, NeedRefresh: true, Key: "component", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", }) rwLocker.logger = log duration := 10 * time.Second // 第一次加写锁 err := rwLocker.WLock(duration) assert.Equal(t, nil, err) tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") num, err := rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次加写锁 err = rwLocker.WLock(duration) assert.Equal(t, nil, err) num, err = rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 2, num) // 第一次解写锁 err = rwLocker.UnWLock() assert.Equal(t, nil, err) num, err = rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, nil, err) assert.Equal(t, 1, num) // 第二次解写锁 err = rwLocker.UnWLock() assert.Equal(t, nil, err) num, err = rdb.HGet(rwLocker.key, tokenKey).Int() assert.Equal(t, redis.Nil, err) assert.Equal(t, 0, num) t.Log("rwLock reentrant lock test success") return }