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