// 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); } } } }