mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 04:56:49 +00:00
feat(bot): run both local and http cache to log differences
This commit is contained in:
parent
9aad6f329c
commit
f1ad221b0f
5 changed files with 150 additions and 29 deletions
|
|
@ -102,8 +102,9 @@ public static class DiscordCacheExtensions
|
||||||
|
|
||||||
public static async Task<PermissionSet> BotPermissionsIn(this IDiscordCache cache, ulong guildId, ulong channelId)
|
public static async Task<PermissionSet> BotPermissionsIn(this IDiscordCache cache, ulong guildId, ulong channelId)
|
||||||
{
|
{
|
||||||
if (cache is HttpDiscordCache)
|
// disable this for now
|
||||||
return await ((HttpDiscordCache)cache).BotChannelPermissions(guildId, channelId);
|
//if (cache is HttpDiscordCache)
|
||||||
|
// return await ((HttpDiscordCache)cache).BotChannelPermissions(guildId, channelId);
|
||||||
|
|
||||||
var channel = await cache.GetRootChannel(guildId, channelId);
|
var channel = await cache.GetRootChannel(guildId, channelId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,12 @@ public class HttpDiscordCache: IDiscordCache
|
||||||
private readonly int _shardCount;
|
private readonly int _shardCount;
|
||||||
private readonly ulong _ownUserId;
|
private readonly ulong _ownUserId;
|
||||||
|
|
||||||
|
private readonly MemoryDiscordCache _innerCache;
|
||||||
|
|
||||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||||
|
|
||||||
|
public EventHandler<(bool?, string)> OnDebug;
|
||||||
|
|
||||||
public HttpDiscordCache(ILogger logger, HttpClient client, string cacheEndpoint, int shardCount, ulong ownUserId)
|
public HttpDiscordCache(ILogger logger, HttpClient client, string cacheEndpoint, int shardCount, ulong ownUserId)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
@ -25,18 +29,19 @@ public class HttpDiscordCache: IDiscordCache
|
||||||
_shardCount = shardCount;
|
_shardCount = shardCount;
|
||||||
_ownUserId = ownUserId;
|
_ownUserId = ownUserId;
|
||||||
_jsonSerializerOptions = new JsonSerializerOptions().ConfigureForMyriad();
|
_jsonSerializerOptions = new JsonSerializerOptions().ConfigureForMyriad();
|
||||||
|
_innerCache = new MemoryDiscordCache(ownUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask SaveGuild(Guild guild) => default;
|
public ValueTask SaveGuild(Guild guild) => _innerCache.SaveGuild(guild);
|
||||||
public ValueTask SaveChannel(Channel channel) => default;
|
public ValueTask SaveChannel(Channel channel) => _innerCache.SaveChannel(channel);
|
||||||
public ValueTask SaveUser(User user) => default;
|
public ValueTask SaveUser(User user) => default;
|
||||||
public ValueTask SaveSelfMember(ulong guildId, GuildMemberPartial member) => default;
|
public ValueTask SaveSelfMember(ulong guildId, GuildMemberPartial member) => _innerCache.SaveSelfMember(guildId, member);
|
||||||
public ValueTask SaveRole(ulong guildId, Myriad.Types.Role role) => default;
|
public ValueTask SaveRole(ulong guildId, Myriad.Types.Role role) => _innerCache.SaveRole(guildId, role);
|
||||||
public ValueTask SaveDmChannelStub(ulong channelId) => default;
|
public ValueTask SaveDmChannelStub(ulong channelId) => _innerCache.SaveDmChannelStub(channelId);
|
||||||
public ValueTask RemoveGuild(ulong guildId) => default;
|
public ValueTask RemoveGuild(ulong guildId) => _innerCache.RemoveGuild(guildId);
|
||||||
public ValueTask RemoveChannel(ulong channelId) => default;
|
public ValueTask RemoveChannel(ulong channelId) => _innerCache.RemoveChannel(channelId);
|
||||||
public ValueTask RemoveUser(ulong userId) => default;
|
public ValueTask RemoveUser(ulong userId) => _innerCache.RemoveUser(userId);
|
||||||
public ValueTask RemoveRole(ulong guildId, ulong roleId) => default;
|
public ValueTask RemoveRole(ulong guildId, ulong roleId) => _innerCache.RemoveRole(guildId, roleId);
|
||||||
|
|
||||||
public ulong GetOwnUser() => _ownUserId;
|
public ulong GetOwnUser() => _ownUserId;
|
||||||
|
|
||||||
|
|
@ -59,23 +64,120 @@ public class HttpDiscordCache: IDiscordCache
|
||||||
return JsonSerializer.Deserialize<T>(plaintext, _jsonSerializerOptions);
|
return JsonSerializer.Deserialize<T>(plaintext, _jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Guild?> TryGetGuild(ulong guildId)
|
public async Task<Guild?> TryGetGuild(ulong guildId)
|
||||||
=> QueryCache<Guild?>($"/guilds/{guildId}", guildId);
|
{
|
||||||
|
var lres = await _innerCache.TryGetGuild(guildId);
|
||||||
|
var hres = await QueryCache<Guild?>($"/guilds/{guildId}", guildId);
|
||||||
|
|
||||||
public Task<Channel?> TryGetChannel(ulong guildId, ulong channelId)
|
if (lres == null && hres == null) return null;
|
||||||
=> QueryCache<Channel?>($"/guilds/{guildId}/channels/{channelId}", guildId);
|
if (lres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"TryGetGuild({guildId}) was only successful on remote cache");
|
||||||
|
OnDebug(null, (true, "guild"));
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
if (hres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"TryGetGuild({guildId}) was only successful on local cache");
|
||||||
|
OnDebug(null, (false, "guild"));
|
||||||
|
return lres;
|
||||||
|
}
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Channel?> TryGetChannel(ulong guildId, ulong channelId)
|
||||||
|
{
|
||||||
|
var lres = await _innerCache.TryGetChannel(guildId, channelId);
|
||||||
|
var hres = await QueryCache<Channel?>($"/guilds/{guildId}/channels/{channelId}", guildId);
|
||||||
|
if (lres == null && hres == null) return null;
|
||||||
|
if (lres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"TryGetChannel({guildId}, {channelId}) was only successful on remote cache");
|
||||||
|
OnDebug(null, (true, "channel"));
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
if (hres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"TryGetChannel({guildId}, {channelId}) was only successful on local cache");
|
||||||
|
OnDebug(null, (false, "channel"));
|
||||||
|
return lres;
|
||||||
|
}
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
|
||||||
// this should be a GetUserCached method on nirn-proxy (it's always called as GetOrFetchUser)
|
// this should be a GetUserCached method on nirn-proxy (it's always called as GetOrFetchUser)
|
||||||
// so just return nothing
|
// so just return nothing
|
||||||
public Task<User?> TryGetUser(ulong userId)
|
public Task<User?> TryGetUser(ulong userId)
|
||||||
=> Task.FromResult<User?>(null);
|
=> Task.FromResult<User?>(null);
|
||||||
|
|
||||||
public Task<GuildMemberPartial?> TryGetSelfMember(ulong guildId)
|
public async Task<GuildMemberPartial?> TryGetSelfMember(ulong guildId)
|
||||||
=> QueryCache<GuildMemberPartial?>($"/guilds/{guildId}/members/@me", guildId);
|
{
|
||||||
|
var lres = await _innerCache.TryGetSelfMember(guildId);
|
||||||
|
var hres = await QueryCache<GuildMemberPartial?>($"/guilds/{guildId}/members/@me", guildId);
|
||||||
|
if (lres == null && hres == null) return null;
|
||||||
|
if (lres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"TryGetSelfMember({guildId}) was only successful on remote cache");
|
||||||
|
OnDebug(null, (true, "self_member"));
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
if (hres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"TryGetSelfMember({guildId}) was only successful on local cache");
|
||||||
|
OnDebug(null, (false, "self_member"));
|
||||||
|
return lres;
|
||||||
|
}
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
|
||||||
public Task<PermissionSet> BotChannelPermissions(ulong guildId, ulong channelId)
|
// public async Task<PermissionSet> BotChannelPermissions(ulong guildId, ulong channelId)
|
||||||
=> QueryCache<PermissionSet>($"/guilds/{guildId}/channels/{channelId}/permissions/@me", guildId);
|
// {
|
||||||
|
// // todo: local cache throws rather than returning null
|
||||||
|
// // we need to throw too, and try/catch local cache here
|
||||||
|
// var lres = await _innerCache.BotPermissionsIn(guildId, channelId);
|
||||||
|
// var hres = await QueryCache<PermissionSet?>($"/guilds/{guildId}/channels/{channelId}/permissions/@me", guildId);
|
||||||
|
// if (lres == null && hres == null) return null;
|
||||||
|
// if (lres == null)
|
||||||
|
// {
|
||||||
|
// _logger.Warning($"TryGetChannel({guildId}, {channelId}) was only successful on remote cache");
|
||||||
|
// OnDebug(null, (true, "botchannelperms"));
|
||||||
|
// return hres;
|
||||||
|
// }
|
||||||
|
// if (hres == null)
|
||||||
|
// {
|
||||||
|
// _logger.Warning($"TryGetChannel({guildId}, {channelId}) was only successful on local cache");
|
||||||
|
// OnDebug(null, (false, "botchannelperms"));
|
||||||
|
// return lres;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // this one is easy to check, so let's check it
|
||||||
|
// if ((int)lres != (int)hres)
|
||||||
|
// {
|
||||||
|
// // trust local
|
||||||
|
// _logger.Warning($"got different permissions for {channelId} (local {(int)lres}, remote {(int)hres})");
|
||||||
|
// OnDebug(null, (null, "botchannelperms"));
|
||||||
|
// return lres;
|
||||||
|
// }
|
||||||
|
// return hres;
|
||||||
|
// }
|
||||||
|
|
||||||
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId)
|
public async Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId)
|
||||||
=> QueryCache<IEnumerable<Channel>>($"/guilds/{guildId}/channels", guildId);
|
{
|
||||||
|
var lres = await _innerCache.GetGuildChannels(guildId);
|
||||||
|
var hres = await QueryCache<IEnumerable<Channel>>($"/guilds/{guildId}/channels", guildId);
|
||||||
|
if (lres == null && hres == null) return null;
|
||||||
|
if (lres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"GetGuildChannels({guildId}) was only successful on remote cache");
|
||||||
|
OnDebug(null, (true, "guild_channels"));
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
|
if (hres == null)
|
||||||
|
{
|
||||||
|
_logger.Warning($"GetGuildChannels({guildId}) was only successful on local cache");
|
||||||
|
OnDebug(null, (false, "guild_channels"));
|
||||||
|
return lres;
|
||||||
|
}
|
||||||
|
return hres;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,13 +99,9 @@ public class Bot
|
||||||
|
|
||||||
private async Task OnEventReceived(int shardId, IGatewayEvent evt)
|
private async Task OnEventReceived(int shardId, IGatewayEvent evt)
|
||||||
{
|
{
|
||||||
if (_cache is MemoryDiscordCache)
|
// we HandleGatewayEvent **before** getting the own user, because the own user is set in HandleGatewayEvent for ReadyEvent
|
||||||
{
|
await _cache.HandleGatewayEvent(evt);
|
||||||
// we HandleGatewayEvent **before** getting the own user, because the own user is set in HandleGatewayEvent for ReadyEvent
|
await _cache.TryUpdateSelfMember(_config.ClientId, evt);
|
||||||
await _cache.HandleGatewayEvent(evt);
|
|
||||||
|
|
||||||
await _cache.TryUpdateSelfMember(_config.ClientId, evt);
|
|
||||||
}
|
|
||||||
await OnEventReceivedInner(shardId, evt);
|
await OnEventReceivedInner(shardId, evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,4 +136,11 @@ public static class BotMetrics
|
||||||
DurationUnit = TimeUnit.Seconds,
|
DurationUnit = TimeUnit.Seconds,
|
||||||
Context = "Bot"
|
Context = "Bot"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static MeterOptions CacheDebug => new()
|
||||||
|
{
|
||||||
|
Name = "Bad responses to cache lookups",
|
||||||
|
Context = "Bot",
|
||||||
|
MeasurementUnit = Unit.Calls
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -49,9 +49,24 @@ public class BotModule: Module
|
||||||
var botConfig = c.Resolve<BotConfig>();
|
var botConfig = c.Resolve<BotConfig>();
|
||||||
|
|
||||||
if (botConfig.HttpCacheUrl != null)
|
if (botConfig.HttpCacheUrl != null)
|
||||||
return new HttpDiscordCache(c.Resolve<ILogger>(),
|
{
|
||||||
|
var cache = new HttpDiscordCache(c.Resolve<ILogger>(),
|
||||||
c.Resolve<HttpClient>(), botConfig.HttpCacheUrl, botConfig.Cluster?.TotalShards ?? 1, botConfig.ClientId);
|
c.Resolve<HttpClient>(), botConfig.HttpCacheUrl, botConfig.Cluster?.TotalShards ?? 1, botConfig.ClientId);
|
||||||
|
|
||||||
|
var metrics = c.Resolve<IMetrics>();
|
||||||
|
|
||||||
|
cache.OnDebug += (_, ev) =>
|
||||||
|
{
|
||||||
|
var (remote, key) = ev;
|
||||||
|
metrics.Measure.Meter.Mark(BotMetrics.CacheDebug, new MetricTags(
|
||||||
|
new[] { "remote", "key" },
|
||||||
|
new[] { remote.ToString(), key }
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
return new MemoryDiscordCache(botConfig.ClientId);
|
return new MemoryDiscordCache(botConfig.ClientId);
|
||||||
}).AsSelf().SingleInstance();
|
}).AsSelf().SingleInstance();
|
||||||
builder.RegisterType<PrivateChannelService>().AsSelf().SingleInstance();
|
builder.RegisterType<PrivateChannelService>().AsSelf().SingleInstance();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue