jhilden
jhilden

Reputation: 12429

StackExchange.Redis - best way to wait for a lock

I have the following method that waits for an exclusive lock on a Redis key. This method works but I'm wondering if there is a better way without a for loop and a Thread.Sleep.

    /// <summary>
    /// wait up to 2 seconds to achieve a lock!  
    /// The lock is good for a maximum of 3 seconds
    /// </summary>
    /// <param name="codeID"></param>
    internal void WaitForSingleUseLock(CodeID codeID)
    {
        var key = _redemptionRepo.SingleUseCodeLockPrefix + codeID.Value;
        var expiration = TimeSpan.FromSeconds(3);
        for (var i = 0; i < 20; i++)
        {
            var lockAchieved = _cacheRepo.LockTake(key, "1", expiration);
            if (lockAchieved)
            {
                break;
            }
            Thread.Sleep(TimeSpan.FromMilliseconds(100));
        }
    }

Upvotes: 3

Views: 5964

Answers (2)

jhilden
jhilden

Reputation: 12429

After taking @Marc's comments into consideration and meeting with my team about the benefits of Task.Delay() over Thread.Sleep() in this context I decided on this as a final solution:

    /// <summary>
    /// wait up to 3 seconds to achieve a lock!  
    /// The lock is good for a maximum of 3 seconds
    /// 
    /// Returns the total amount of time until the lock was taken
    /// </summary>
    internal virtual async Task<TimeSpan> WaitForSingleUseLock(CodeID codeID)
    {
        var key = _redemptionRepo.SingleUseCodeLockPrefix + codeID.Value;
        var totalTime = TimeSpan.Zero;
        var maxTime = TimeSpan.FromSeconds(3);
        var expiration = TimeSpan.FromSeconds(3);
        var sleepTime = TimeSpan.FromMilliseconds(50);
        var lockAchieved = false;

        while (!lockAchieved && totalTime < maxTime)
        {
            lockAchieved = _cacheRepo.LockTake(key, "1", expiration);
            if (lockAchieved)
            {
                continue;
            }
            await Task.Delay(sleepTime);
            totalTime += sleepTime;
        }
        return totalTime;
    }

Upvotes: 2

Marc Gravell
Marc Gravell

Reputation: 1062755

The only thing I could suggest different would be to consider pub/sub as a side channel (meaning: in addition, not replacement) for indicating when the lock might now be available - i.e. publish when releasing, and use the sub to release a timer (via a monitor or an async-wait-handle).

Other than that: nope. Redis doesn't have the idea of a pending queue. You could perhaps possibly construct one using lists, but...

Upvotes: 5

Related Questions