4
4
namespace ServiceStack . Redis . Support . Locking
5
5
{
6
6
public class DistributedLock : IDistributedLock
7
- {
7
+ {
8
8
public const int LOCK_NOT_ACQUIRED = 0 ;
9
9
public const int LOCK_ACQUIRED = 1 ;
10
10
public const int LOCK_RECOVERED = 2 ;
11
11
12
- /// <summary>
13
- /// acquire distributed, non-reentrant lock on key
14
- /// </summary>
15
- /// <param name="key">global key for this lock</param>
16
- /// <param name="acquisitionTimeout">timeout for acquiring lock</param>
17
- /// <param name="lockTimeout">timeout for lock, in seconds (stored as value against lock key) </param>
12
+ /// <summary>
13
+ /// acquire distributed, non-reentrant lock on key
14
+ /// </summary>
15
+ /// <param name="key">global key for this lock</param>
16
+ /// <param name="acquisitionTimeout">timeout for acquiring lock</param>
17
+ /// <param name="lockTimeout">timeout for lock, in seconds (stored as value against lock key) </param>
18
18
/// <param name="client"></param>
19
19
/// <param name="lockExpire"></param>
20
20
public virtual long Lock ( string key , int acquisitionTimeout , int lockTimeout , out long lockExpire , IRedisClient client )
21
- {
22
- lockExpire = 0 ;
21
+ {
22
+ lockExpire = 0 ;
23
23
24
24
// cannot lock on a null key
25
25
if ( key == null )
26
26
return LOCK_NOT_ACQUIRED ;
27
27
28
- const int sleepIfLockSet = 200 ;
29
- acquisitionTimeout *= 1000 ; //convert to ms
30
- int tryCount = ( acquisitionTimeout / sleepIfLockSet ) + 1 ;
28
+ const int sleepIfLockSet = 200 ;
29
+ acquisitionTimeout *= 1000 ; //convert to ms
30
+ int tryCount = ( acquisitionTimeout / sleepIfLockSet ) + 1 ;
31
31
32
- var ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
33
- var newLockExpire = CalculateLockExpire ( ts , lockTimeout ) ;
32
+ var ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
33
+ var newLockExpire = CalculateLockExpire ( ts , lockTimeout ) ;
34
34
35
35
var localClient = ( RedisClient ) client ;
36
36
long wasSet = localClient . SetNX ( key , BitConverter . GetBytes ( newLockExpire ) ) ;
37
- int totalTime = 0 ;
37
+ int totalTime = 0 ;
38
38
while ( wasSet == LOCK_NOT_ACQUIRED && totalTime < acquisitionTimeout )
39
- {
40
- int count = 0 ;
41
- while ( wasSet == 0 && count < tryCount && totalTime < acquisitionTimeout )
42
- {
43
- TaskUtils . Sleep ( sleepIfLockSet ) ;
44
- totalTime += sleepIfLockSet ;
45
- ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
46
- newLockExpire = CalculateLockExpire ( ts , lockTimeout ) ;
39
+ {
40
+ int count = 0 ;
41
+ while ( wasSet == 0 && count < tryCount && totalTime < acquisitionTimeout )
42
+ {
43
+ TaskUtils . Sleep ( sleepIfLockSet ) ;
44
+ totalTime += sleepIfLockSet ;
45
+ ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
46
+ newLockExpire = CalculateLockExpire ( ts , lockTimeout ) ;
47
47
wasSet = localClient . SetNX ( key , BitConverter . GetBytes ( newLockExpire ) ) ;
48
- count ++ ;
49
- }
50
- // acquired lock!
48
+ count ++ ;
49
+ }
50
+ // acquired lock!
51
51
if ( wasSet != LOCK_NOT_ACQUIRED ) break ;
52
52
53
- // handle possibliity of crashed client still holding the lock
53
+ // handle possibliity of crashed client still holding the lock
54
54
using ( var pipe = localClient . CreatePipeline ( ) )
55
- {
56
- long lockValue = 0 ;
57
- pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Watch ( key ) ) ;
58
- pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Get ( key ) , x => lockValue = ( x != null ) ? BitConverter . ToInt64 ( x , 0 ) : 0 ) ;
59
- pipe . Flush ( ) ;
55
+ {
56
+ long lockValue = 0 ;
57
+ pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Watch ( key ) ) ;
58
+ pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Get ( key ) , x => lockValue = ( x != null ) ? BitConverter . ToInt64 ( x , 0 ) : 0 ) ;
59
+ pipe . Flush ( ) ;
60
60
61
- // if lock value is 0 (key is empty), or expired, then we can try to acquire it
61
+ // if lock value is 0 (key is empty), or expired, then we can try to acquire it
62
62
ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
63
- if ( lockValue < ts . TotalSeconds )
64
- {
65
- ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
66
- newLockExpire = CalculateLockExpire ( ts , lockTimeout ) ;
67
- using ( var trans = localClient . CreateTransaction ( ) )
68
- {
69
- var expire = newLockExpire ;
70
- trans . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Set ( key , BitConverter . GetBytes ( expire ) ) ) ;
71
- if ( trans . Commit ( ) )
72
- wasSet = LOCK_RECOVERED ; //recovered lock!
73
- }
74
- }
75
- else
76
- {
63
+ if ( lockValue < ts . TotalSeconds )
64
+ {
65
+ ts = ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) ) ;
66
+ newLockExpire = CalculateLockExpire ( ts , lockTimeout ) ;
67
+ using ( var trans = localClient . CreateTransaction ( ) )
68
+ {
69
+ var expire = newLockExpire ;
70
+ trans . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Set ( key , BitConverter . GetBytes ( expire ) ) ) ;
71
+ if ( trans . Commit ( ) )
72
+ wasSet = LOCK_RECOVERED ; //recovered lock!
73
+ }
74
+ }
75
+ else
76
+ {
77
77
localClient . UnWatch ( ) ;
78
- }
79
- }
78
+ }
79
+ }
80
80
if ( wasSet != LOCK_NOT_ACQUIRED ) break ;
81
- TaskUtils . Sleep ( sleepIfLockSet ) ;
82
- totalTime += sleepIfLockSet ;
83
- }
81
+ TaskUtils . Sleep ( sleepIfLockSet ) ;
82
+ totalTime += sleepIfLockSet ;
83
+ }
84
84
if ( wasSet != LOCK_NOT_ACQUIRED )
85
85
{
86
86
lockExpire = newLockExpire ;
87
87
}
88
- return wasSet ;
89
-
90
- }
88
+ return wasSet ;
89
+ }
91
90
92
-
93
- /// <summary>
94
- /// unlock key
95
- /// </summary>
96
- public virtual bool Unlock ( string key , long lockExpire , IRedisClient client )
97
- {
98
- if ( lockExpire <= 0 )
99
- return false ;
100
- long lockVal = 0 ;
91
+ /// <summary>
92
+ /// unlock key
93
+ /// </summary>
94
+ public virtual bool Unlock ( string key , long lockExpire , IRedisClient client )
95
+ {
96
+ if ( lockExpire <= 0 )
97
+ return false ;
98
+ long lockVal = 0 ;
101
99
var localClient = ( RedisClient ) client ;
102
100
using ( var pipe = localClient . CreatePipeline ( ) )
103
101
{
104
-
105
- pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Watch ( key ) ) ;
106
- pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Get ( key ) ,
102
+
103
+ pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Watch ( key ) ) ;
104
+ pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Get ( key ) ,
107
105
x => lockVal = ( x != null ) ? BitConverter . ToInt64 ( x , 0 ) : 0 ) ;
108
106
pipe . Flush ( ) ;
109
107
}
110
108
111
- if ( lockVal != lockExpire )
112
- {
109
+ if ( lockVal != lockExpire )
110
+ {
113
111
if ( lockVal != 0 )
114
- Debug . WriteLine ( String . Format ( "Unlock(): Failed to unlock key {0 }; lock has been acquired by another client " , key ) ) ;
112
+ Debug . WriteLine ( $ "Unlock(): Failed to unlock key { key } ; lock has been acquired by another client ") ;
115
113
else
116
- Debug . WriteLine ( String . Format ( "Unlock(): Failed to unlock key {0 }; lock has been identifed as a zombie and harvested " , key ) ) ;
114
+ Debug . WriteLine ( $ "Unlock(): Failed to unlock key { key } ; lock has been identifed as a zombie and harvested ") ;
117
115
localClient . UnWatch ( ) ;
118
- return false ;
119
- }
116
+ return false ;
117
+ }
120
118
121
119
using ( var trans = localClient . CreateTransaction ( ) )
122
120
{
123
121
trans . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Del ( key ) ) ;
124
- var rc = trans . Commit ( ) ;
122
+ var rc = trans . Commit ( ) ;
125
123
if ( ! rc )
126
- Debug . WriteLine ( String . Format ( "Unlock(): Failed to delete key {0 }; lock has been acquired by another client " , key ) ) ;
124
+ Debug . WriteLine ( $ "Unlock(): Failed to delete key { key } ; lock has been acquired by another client ") ;
127
125
return rc ;
128
126
}
129
-
130
- }
131
-
127
+ }
132
128
133
129
/// <summary>
134
130
///
@@ -140,6 +136,5 @@ private static long CalculateLockExpire(TimeSpan ts, int timeout)
140
136
{
141
137
return ( long ) ( ts . TotalSeconds + timeout + 1.5 ) ;
142
138
}
143
-
144
- }
139
+ }
145
140
}
0 commit comments