// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Threading;
using System.Threading.Tasks;
using StackExchange.Redis;
namespace EpicGames.Redis.Utility
{
///
/// Utility method for queue instances
///
public static class RedisQueue
{
///
/// Create a new queue instance
///
/// Multiplexer for the connection
/// Key for the queue
/// Channel to use for posting update notifications
/// Cancellation token for the operation
public static async Task> CreateAsync(IConnectionMultiplexer multiplexer, RedisKey queueKey, RedisChannel eventChannel, CancellationToken cancellationToken = default)
{
RedisEvent asyncEvent = await RedisEvent.CreateAsync(multiplexer, eventChannel, cancellationToken);
return new RedisQueue(multiplexer, asyncEvent, new RedisListKey(queueKey));
}
}
///
/// Implements a waitable FIFO queue with Redis. Wraps a pub/sub channel and list, ensuring that each item is only popped from the queue once.
///
/// Type of item in the queue
public sealed class RedisQueue : IAsyncDisposable
{
readonly IConnectionMultiplexer _multiplexer;
readonly RedisEvent _asyncEvent;
readonly RedisListKey _listKey;
internal RedisQueue(IConnectionMultiplexer multiplexer, RedisEvent asyncEvent, RedisListKey listKey)
{
_multiplexer = multiplexer;
_asyncEvent = asyncEvent;
_listKey = listKey;
}
///
public ValueTask DisposeAsync()
=> _asyncEvent.DisposeAsync();
///
/// Push a new item onto the queue
///
/// Item to add to the queue
/// Flags for the push operation
public async Task PushAsync(T item, CommandFlags flags = CommandFlags.None)
{
IDatabase database = _multiplexer.GetDatabase();
await database.ListRightPushAsync(_listKey, item, flags: flags);
_asyncEvent.Pulse();
}
///
/// Attempt to pop an item from the front of the queue. Returns the default value for the item if the queue is empty.
///
public async Task TryPopAsync(CancellationToken cancellationToken = default)
{
_ = cancellationToken; // Don't currently support cancellation due to potential of items being dropped
IDatabase database = _multiplexer.GetDatabase();
return await database.ListLeftPopAsync(_listKey);
}
///
/// Wait for a new item to be pushed onto the queue
///
/// Cancellation token for the operation
public async Task WaitForDataAsync(CancellationToken cancellationToken = default)
{
Task task = _asyncEvent.Task;
IDatabase database = _multiplexer.GetDatabase();
if (await database.ListLengthAsync(_listKey) == 0)
{
await task.WaitAsync(cancellationToken);
}
}
}
}