44namespace ServiceStack . Redis . Support . Locking
55{
66 public class DistributedLock : IDistributedLock
7- {
7+ {
88 public const int LOCK_NOT_ACQUIRED = 0 ;
99 public const int LOCK_ACQUIRED = 1 ;
1010 public const int LOCK_RECOVERED = 2 ;
1111
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>
1818 /// <param name="client"></param>
1919 /// <param name="lockExpire"></param>
2020 public virtual long Lock ( string key , int acquisitionTimeout , int lockTimeout , out long lockExpire , IRedisClient client )
21- {
22- lockExpire = 0 ;
21+ {
22+ lockExpire = 0 ;
2323
2424 // cannot lock on a null key
2525 if ( key == null )
2626 return LOCK_NOT_ACQUIRED ;
2727
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 ;
3131
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 ) ;
3434
3535 var localClient = ( RedisClient ) client ;
3636 long wasSet = localClient . SetNX ( key , BitConverter . GetBytes ( newLockExpire ) ) ;
37- int totalTime = 0 ;
37+ int totalTime = 0 ;
3838 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 ) ;
4747 wasSet = localClient . SetNX ( key , BitConverter . GetBytes ( newLockExpire ) ) ;
48- count ++ ;
49- }
50- // acquired lock!
48+ count ++ ;
49+ }
50+ // acquired lock!
5151 if ( wasSet != LOCK_NOT_ACQUIRED ) break ;
5252
53- // handle possibliity of crashed client still holding the lock
53+ // handle possibliity of crashed client still holding the lock
5454 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 ( ) ;
6060
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
6262 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+ {
7777 localClient . UnWatch ( ) ;
78- }
79- }
78+ }
79+ }
8080 if ( wasSet != LOCK_NOT_ACQUIRED ) break ;
81- TaskUtils . Sleep ( sleepIfLockSet ) ;
82- totalTime += sleepIfLockSet ;
83- }
81+ TaskUtils . Sleep ( sleepIfLockSet ) ;
82+ totalTime += sleepIfLockSet ;
83+ }
8484 if ( wasSet != LOCK_NOT_ACQUIRED )
8585 {
8686 lockExpire = newLockExpire ;
8787 }
88- return wasSet ;
89-
90- }
88+ return wasSet ;
89+ }
9190
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 ;
10199 var localClient = ( RedisClient ) client ;
102100 using ( var pipe = localClient . CreatePipeline ( ) )
103101 {
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 ) ,
107105 x => lockVal = ( x != null ) ? BitConverter . ToInt64 ( x , 0 ) : 0 ) ;
108106 pipe . Flush ( ) ;
109107 }
110108
111- if ( lockVal != lockExpire )
112- {
109+ if ( lockVal != lockExpire )
110+ {
113111 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 ") ;
115113 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 ") ;
117115 localClient . UnWatch ( ) ;
118- return false ;
119- }
116+ return false ;
117+ }
120118
121119 using ( var trans = localClient . CreateTransaction ( ) )
122120 {
123121 trans . QueueCommand ( r => ( ( RedisNativeClient ) r ) . Del ( key ) ) ;
124- var rc = trans . Commit ( ) ;
122+ var rc = trans . Commit ( ) ;
125123 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 ") ;
127125 return rc ;
128126 }
129-
130- }
131-
127+ }
132128
133129 /// <summary>
134130 ///
@@ -140,6 +136,5 @@ private static long CalculateLockExpire(TimeSpan ts, int timeout)
140136 {
141137 return ( long ) ( ts . TotalSeconds + timeout + 1.5 ) ;
142138 }
143-
144- }
139+ }
145140}
0 commit comments