You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
* Once a log is retrieved, server adds an entry with this log id to a sorted set in Redis, scored by expiry time and broadcasts it to any other server instances. * If a log is being tailed, server keeps the total number of lines in a Redis key and appends chunks of log data to a sorted set for that log scored by index of the first line. * Chunks are split on fixed boundaries, in order to allow older entries to be purged by score without having to count the number of lines they contain first. * Agent polls for requests to provide tail data via LogRpcService.UpdateLogTail, which calls LogTailService.WaitForTailNext. WaitForTailNext returns the index of the total line count for this log if it is being tailed (indicating the index of the next line that the agent should send to the server), or blocks until the log is being tailed. * Once data is flushed to persistent storage, the number of flushed lines is added to LogRpcService._trimQueue and the line data is removed from Redis after LogRpcService.TrimAfter. * Log node contains a "complete" flag indicating whether it is necessary to check for tail data. #preflight none [CL 23215856 by Ben Marsh in ue5-main branch]
136 lines
4.8 KiB
C#
136 lines
4.8 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using StackExchange.Redis;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace EpicGames.Redis
|
|
{
|
|
/// <summary>
|
|
/// Represents a typed Redis list with a given key
|
|
/// </summary>
|
|
/// <typeparam name="TElement">The type of element stored in the list</typeparam>
|
|
public readonly struct RedisString<TElement>
|
|
{
|
|
internal readonly RedisConnectionPool? ConnectionPool = null;
|
|
internal readonly IDatabaseAsync? Database = null;
|
|
|
|
/// <summary>
|
|
/// The key for the list
|
|
/// </summary>
|
|
public readonly RedisKey Key { get; }
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="database"></param>
|
|
/// <param name="key"></param>
|
|
public RedisString(IDatabaseAsync database, RedisKey key)
|
|
{
|
|
Database = database;
|
|
Key = key;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="connectionPool"></param>
|
|
/// <param name="key"></param>
|
|
public RedisString(RedisConnectionPool connectionPool, RedisKey key)
|
|
{
|
|
ConnectionPool = connectionPool;
|
|
Key = key;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the Redis database in use
|
|
/// </summary>
|
|
/// <returns>A connection pool or explicitly set Redis database</returns>
|
|
/// <exception cref="InvalidOperationException">If neither are set</exception>
|
|
public IDatabaseAsync GetDatabase()
|
|
{
|
|
if (Database != null) return Database;
|
|
if (ConnectionPool != null) return ConnectionPool.GetDatabase();
|
|
throw new InvalidOperationException($"Neither {nameof(Database)} or {nameof(ConnectionPool)} has been set!");
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringLengthAsync(RedisKey, CommandFlags)"/>
|
|
public Task<long> LengthAsync(CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return GetDatabase().StringLengthAsync(Key, flags);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringSetAsync(RedisKey, RedisValue, TimeSpan?, When, CommandFlags)"/>
|
|
public Task<bool> SetAsync(TElement value, TimeSpan? expiry = null, When when = When.Always, CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return GetDatabase().StringSetAsync(Key, RedisSerializer.Serialize(value), expiry, when, flags);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension methods for sets
|
|
/// </summary>
|
|
public static class RedisStringExtensions
|
|
{
|
|
/// <inheritdoc cref="IDatabaseAsync.KeyDeleteAsync(RedisKey, CommandFlags)"/>
|
|
public static Task<bool> DeleteAsync<TElement>(this RedisString<TElement> str, CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return str.GetDatabase().KeyDeleteAsync(str.Key, flags);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringGetAsync(RedisKey, CommandFlags)"/>
|
|
public static async Task<TElement?> GetAsync<TElement>(this RedisString<TElement> str, CommandFlags flags = CommandFlags.None) where TElement : class
|
|
{
|
|
RedisValue value = await str.GetDatabase().StringGetAsync(str.Key, flags);
|
|
if (value.IsNullOrEmpty)
|
|
{
|
|
return null;
|
|
}
|
|
return RedisSerializer.Deserialize<TElement>(value);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringGetAsync(RedisKey, CommandFlags)"/>
|
|
public static async Task<TElement?> GetValueAsync<TElement>(this RedisString<TElement> str, CommandFlags flags = CommandFlags.None) where TElement : struct
|
|
{
|
|
RedisValue value = await str.GetDatabase().StringGetAsync(str.Key, flags);
|
|
if (value.IsNullOrEmpty)
|
|
{
|
|
return null;
|
|
}
|
|
return RedisSerializer.Deserialize<TElement>(value);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringDecrementAsync(RedisKey, Int64, CommandFlags)"/>
|
|
public static Task<long> DecrementAsync(this RedisString<long> str, long value = 1L, CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return str.GetDatabase().StringDecrementAsync(str.Key, value, flags);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringDecrementAsync(RedisKey, Double, CommandFlags)"/>
|
|
public static Task<double> DecrementAsync(this RedisString<double> str, double value = 1.0, CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return str.GetDatabase().StringDecrementAsync(str.Key, value, flags);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringDecrementAsync(RedisKey, Double, CommandFlags)"/>
|
|
public static Task<long> IncrementAsync(this RedisString<long> str, long value = 1L, CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return str.GetDatabase().StringIncrementAsync(str.Key, value, flags);
|
|
}
|
|
|
|
/// <inheritdoc cref="IDatabaseAsync.StringDecrementAsync(RedisKey, Double, CommandFlags)"/>
|
|
public static Task<double> IncrementAsync(this RedisString<double> str, double value = 1.0, CommandFlags flags = CommandFlags.None)
|
|
{
|
|
return str.GetDatabase().StringIncrementAsync(str.Key, value, flags);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a version of this string which modifies a transaction rather than the direct DB
|
|
/// </summary>
|
|
public static RedisString<TElement> With<TElement>(this ITransaction transaction, RedisString<TElement> str)
|
|
{
|
|
return new RedisString<TElement>(transaction, str.Key);
|
|
}
|
|
}
|
|
}
|