1- using System . Collections . Generic ;
1+ using System ;
2+ using System . Collections . Generic ;
23using ServiceStack . Redis . Support . Locking ;
34
45
@@ -13,18 +14,18 @@ namespace ServiceStack.Redis.Support.Queue.Implementation
1314 /// </summary>
1415 public class RedisSequentialWorkQueue < T > : RedisWorkQueue < T > , ISequentialWorkQueue < T > where T : class
1516 {
16- public class DequeueLock : DistributedLock
17+ public class WorkItemIdLock : DistributedLock
1718 {
18- private RedisSequentialWorkQueue < T > workQueue ;
19- private string workItemId ;
20- public DequeueLock ( IRedisClient client , RedisSequentialWorkQueue < T > workQueue , string workItemId ) : base ( client )
19+ private readonly RedisSequentialWorkQueue < T > workQueue ;
20+ private readonly string workItemId ;
21+ public WorkItemIdLock ( IRedisClient client , RedisSequentialWorkQueue < T > workQueue , string workItemId ) : base ( client )
2122 {
2223 this . workQueue = workQueue ;
2324 this . workItemId = workItemId ;
2425 }
2526 public override bool Unlock ( )
2627 {
27- workQueue . PostDequeue ( workItemId ) ;
28+ workQueue . Unlock ( workItemId ) ;
2829 return base . Unlock ( ) ;
2930 }
3031 }
@@ -34,7 +35,7 @@ public override bool Unlock()
3435 private int lockTimeout = 2 ;
3536 protected const double CONVENIENTLY_SIZED_FLOAT = 18014398509481984.0 ;
3637
37- private string dequeueLockIds = "DequeueLockIds " ;
38+ private string dequeueIds = "DequeueIds " ;
3839 private const int numTagsForDequeueLock = RedisNamespace . NumTagsForLockKey + 1 ;
3940
4041 public RedisSequentialWorkQueue ( int maxReadPoolSize , int maxWritePoolSize , string host , int port )
@@ -70,14 +71,18 @@ public void Enqueue(string workItemId, T workItem)
7071 }
7172 }
7273
73- /// <summary>
74- /// Dequeue next batch of messages for processing. After this method is called,
75- /// no other messages with same work item id will be returned by subsequent calles
76- /// to this method until <see cref="PostDequeue"/> is called
77- /// </summary>
78- /// <returns>KeyValuePair: key is work item id, and value is list of dequeued items.
79- /// </returns>
80- public SequentialDeueueData < T > Dequeue ( int maxBatchSize )
74+ public SequentialData < T > Dequeue ( int maxBatchSize )
75+ {
76+ return Peek ( maxBatchSize , true ) ;
77+ }
78+
79+ public SequentialData < T > Peek ( int maxBatchSize )
80+ {
81+ return Peek ( maxBatchSize , false ) ;
82+ }
83+
84+
85+ private SequentialData < T > Peek ( int maxBatchSize , bool dequeue )
8186 {
8287 using ( var disposableClient = clientManager . GetDisposableClient < SerializingRedisClient > ( ) )
8388 {
@@ -87,7 +92,7 @@ public SequentialDeueueData<T> Dequeue(int maxBatchSize)
8792 string workItemId = null ;
8893 var dequeueItems = new List < T > ( ) ;
8994 var smallest = client . ZRangeWithScores ( pendingWorkItemIdQueue , 0 , 0 ) ;
90- IDistributedLock dequeueLock = null ;
95+ IDistributedLock workItemIdLock = null ;
9196 try
9297 {
9398 if ( smallest != null && smallest . Length > 1 && RedisNativeClient . ParseDouble ( smallest [ 1 ] ) != CONVENIENTLY_SIZED_FLOAT )
@@ -96,45 +101,56 @@ public SequentialDeueueData<T> Dequeue(int maxBatchSize)
96101
97102 // acquire dequeue lock
98103 var dequeueLockKey = queueNamespace . GlobalKey ( workItemId , numTagsForDequeueLock ) ;
99- dequeueLock = new DequeueLock ( client , this , workItemId ) ;
100- dequeueLock . Lock ( dequeueLockKey , 2 , 300 ) ;
104+ workItemIdLock = new WorkItemIdLock ( client , this , workItemId ) ;
105+ workItemIdLock . Lock ( dequeueLockKey , 2 , 300 ) ;
101106
102107 using ( var pipe = client . CreatePipeline ( ) )
103108 {
104109 // track dequeue lock id
105- pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . SAdd ( dequeueLockIds , client . Serialize ( dequeueLockKey ) ) ) ;
110+ pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . SAdd ( dequeueIds , client . Serialize ( workItemId ) ) ) ;
106111
107112 // lock work item id
108113 pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . ZAdd ( pendingWorkItemIdQueue , CONVENIENTLY_SIZED_FLOAT , smallest [ 0 ] ) ) ;
109114
110- var key = queueNamespace . GlobalCacheKey ( workItemId ) ;
111115 // dequeue items
116+ var key = queueNamespace . GlobalCacheKey ( workItemId ) ;
117+ Action < byte [ ] > dequeueCallback = x =>
118+ {
119+ if ( x != null )
120+ dequeueItems . Add ( ( T ) client . Deserialize ( x ) ) ;
121+ } ;
122+
112123 for ( var i = 0 ; i < maxBatchSize ; ++ i )
113124 {
114- pipe . QueueCommand (
115- r => ( ( RedisNativeClient ) r ) . LPop ( key ) ,
116- x =>
117- {
118- if ( x != null )
119- dequeueItems . Add ( ( T ) client . Deserialize ( x ) ) ;
120- } ) ;
125+ if ( dequeue )
126+ {
127+ pipe . QueueCommand (
128+ r => ( ( RedisNativeClient ) r ) . LPop ( key ) ,
129+ dequeueCallback ) ;
130+ }
131+ else
132+ {
133+ int index = i ;
134+ pipe . QueueCommand (
135+ r => ( ( RedisNativeClient ) r ) . LIndex ( key , index ) ,
136+ dequeueCallback ) ;
137+ }
121138 }
122139 pipe . Flush ( ) ;
123140 }
124141 }
125- return new SequentialDeueueData < T >
142+ return new SequentialData < T >
126143 {
127- DequeueItems = dequeueItems ,
144+ WorkItems = dequeueItems ,
128145 WorkItemId = workItemId ,
129- DequeueLock = dequeueLock
146+ WorkItemIdLock = workItemIdLock
130147 } ;
131148 }
132149 catch ( System . Exception )
133150 {
134151 //release resources
135- PostDequeue ( workItemId ) ;
136- if ( dequeueLock != null )
137- dequeueLock . Unlock ( ) ;
152+ if ( workItemIdLock != null )
153+ workItemIdLock . Unlock ( ) ;
138154
139155 throw ;
140156 }
@@ -147,13 +163,23 @@ private void HarvestZombies()
147163 // dequeue acquires dequeue lock on these ids
148164 // each dequeue will MGet on list of all dequeued workItemIds: then MGet on all lock keys.
149165 // if expired, then reacquire and PostDequeue
166+ using ( var disposableClient = clientManager . GetDisposableClient < SerializingRedisClient > ( ) )
167+ {
168+ var client = disposableClient . Client ;
169+ var dequeueWorkItemIds = client . SMembers ( dequeueIds ) ;
170+ foreach ( var workItemId in dequeueWorkItemIds )
171+ {
172+ var lockId = queueNamespace . GlobalKey ( client . Deserialize ( workItemId ) , RedisNamespace . NumTagsForLockKey ) ;
173+
174+ }
175+ }
150176 }
151177
152178 /// <summary>
153- /// Unlock message id, so other servers can process messages for this message id
179+ /// Unlock work item id, so other servers can process items for this id
154180 /// </summary>
155181 /// <param name="workItemId"></param>
156- private void PostDequeue ( string workItemId )
182+ private void Unlock ( string workItemId )
157183 {
158184 if ( workItemId == null )
159185 return ;
@@ -176,8 +202,7 @@ private void PostDequeue(string workItemId)
176202 pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . ZAdd ( pendingWorkItemIdQueue , len , client . Serialize ( workItemId ) ) ) ;
177203
178204 //untrack dequeue lock
179- var dequeueLockKey = queueNamespace . GlobalKey ( workItemId , numTagsForDequeueLock ) ;
180- pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . SRem ( dequeueLockIds , client . Serialize ( dequeueLockKey ) ) ) ;
205+ pipe . QueueCommand ( r => ( ( RedisNativeClient ) r ) . SRem ( dequeueIds , client . Serialize ( workItemId ) ) ) ;
181206
182207 pipe . Flush ( ) ;
183208 }
0 commit comments