/// <summary>
        /// Must call this periodically to move work items from priority queue to pending queue
        /// </summary>
        public bool PrepareNextWorkItem()
        {
            //harvest zombies every 5 minutes
            var now = DateTime.UtcNow;
            var ts  = now - harvestTime;

            if (ts.TotalMinutes > 5)
            {
                HarvestZombies();
                harvestTime = now;
            }

            using (var disposableClient = clientManager.GetDisposableClient <SerializingRedisClient>())
            {
                var client = disposableClient.Client;

                //1. get next workItemId, or return if there isn't one
                var smallest = client.ZRangeWithScores(workItemIdPriorityQueue, 0, 0);
                if (smallest == null || smallest.Length <= 1 ||
                    RedisNativeClient.ParseDouble(smallest[1]) == CONVENIENTLY_SIZED_FLOAT)
                {
                    return(false);
                }
                var workItemId = client.Deserialize(smallest[0]) as string;

                // lock work item id
                var lockKey = queueNamespace.GlobalLockKey(workItemId);
                using (var disposableLock = new DisposableDistributedLock(client, lockKey, lockAcquisitionTimeout, lockTimeout))
                {
                    // if another client has queued this work item id,
                    // then the work item id score will be set to CONVENIENTLY_SIZED_FLOAT
                    // so we return false in this case
                    var score = client.ZScore(workItemIdPriorityQueue, smallest[0]);
                    if (score == CONVENIENTLY_SIZED_FLOAT)
                    {
                        return(false);
                    }

                    using (var pipe = client.CreatePipeline())
                    {
                        var rawWorkItemId = client.Serialize(workItemId);

                        // lock work item id in priority queue
                        pipe.QueueCommand(
                            r =>
                            ((RedisNativeClient)r).ZAdd(workItemIdPriorityQueue, CONVENIENTLY_SIZED_FLOAT, smallest[0]));

                        // track dequeue lock id
                        pipe.QueueCommand(r => ((RedisNativeClient)r).SAdd(dequeueIdSet, rawWorkItemId));

                        // push into pending set
                        pipe.QueueCommand(r => ((RedisNativeClient)r).LPush(pendingWorkItemIdQueue, rawWorkItemId));

                        pipe.Flush();
                    }
                }
            }
            return(true);
        }