Update background services to remove guild cache references

Possibly optimized StaleDataCleaner as well by making certain database
updates asynchronous
This commit is contained in:
Noi 2020-07-16 14:48:09 -07:00
parent 488685bc79
commit 608ab37aec
3 changed files with 62 additions and 74 deletions

View file

@ -66,23 +66,17 @@ namespace BirthdayBot.BackgroundServices
{ {
var diag = new PGDiagnostic(); var diag = new PGDiagnostic();
// Skip processing of guild if local info has not yet been loaded var gc = await GuildConfiguration.LoadAsync(guild.Id);
if (!BotInstance.GuildCache.TryGetValue(guild.Id, out var gs))
{
diag.FetchCachedGuild = "Server information not yet loaded by the bot. Try again later.";
return diag;
}
diag.FetchCachedGuild = null;
// Check if role settings are correct before continuing with further processing // Check if role settings are correct before continuing with further processing
SocketRole role = null; SocketRole role = null;
if (gs.RoleId.HasValue) role = guild.GetRole(gs.RoleId.Value); if (gc.RoleId.HasValue) role = guild.GetRole(gc.RoleId.Value);
diag.RoleCheck = CheckCorrectRoleSettings(guild, role); diag.RoleCheck = CheckCorrectRoleSettings(guild, role);
if (diag.RoleCheck != null) return diag; if (diag.RoleCheck != null) return diag;
// Determine who's currently having a birthday // Determine who's currently having a birthday
var users = gs.Users; var users = await GuildUserConfiguration.LoadAllAsync(guild.Id);
var tz = gs.TimeZone; var tz = gc.TimeZone;
var birthdays = GetGuildCurrentBirthdays(users, tz); var birthdays = GetGuildCurrentBirthdays(users, tz);
// Note: Don't quit here if zero people are having birthdays. Roles may still need to be removed by BirthdayApply. // Note: Don't quit here if zero people are having birthdays. Roles may still need to be removed by BirthdayApply.
diag.CurrentBirthdays = birthdays.Count.ToString(); diag.CurrentBirthdays = birthdays.Count.ToString();
@ -103,10 +97,10 @@ namespace BirthdayBot.BackgroundServices
diag.RoleApply = null; diag.RoleApply = null;
// Birthday announcement // Birthday announcement
var announce = gs.AnnounceMessages; var announce = gc.AnnounceMessages;
var announceping = gs.AnnouncePing; var announceping = gc.AnnouncePing;
SocketTextChannel channel = null; SocketTextChannel channel = null;
if (gs.AnnounceChannelId.HasValue) channel = guild.GetTextChannel(gs.AnnounceChannelId.Value); if (gc.AnnounceChannelId.HasValue) channel = guild.GetTextChannel(gc.AnnounceChannelId.Value);
if (announcementList.Count() != 0) if (announcementList.Count() != 0)
{ {
var announceResult = await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList); var announceResult = await AnnounceBirthdaysAsync(announce, announceping, channel, announcementList);
@ -145,7 +139,7 @@ namespace BirthdayBot.BackgroundServices
/// Gets all known users from the given guild and returns a list including only those who are /// Gets all known users from the given guild and returns a list including only those who are
/// currently experiencing a birthday in the respective time zone. /// currently experiencing a birthday in the respective time zone.
/// </summary> /// </summary>
private HashSet<ulong> GetGuildCurrentBirthdays(IEnumerable<GuildUserSettings> guildUsers, string defaultTzStr) private HashSet<ulong> GetGuildCurrentBirthdays(IEnumerable<GuildUserConfiguration> guildUsers, string defaultTzStr)
{ {
var birthdayUsers = new HashSet<ulong>(); var birthdayUsers = new HashSet<ulong>();
@ -271,7 +265,6 @@ namespace BirthdayBot.BackgroundServices
{ {
const string DefaultValue = "--"; const string DefaultValue = "--";
public string FetchCachedGuild = DefaultValue;
public string RoleCheck = DefaultValue; public string RoleCheck = DefaultValue;
public string CurrentBirthdays = DefaultValue; public string CurrentBirthdays = DefaultValue;
public string RoleApply = DefaultValue; public string RoleApply = DefaultValue;
@ -282,7 +275,6 @@ namespace BirthdayBot.BackgroundServices
{ {
var result = new StringBuilder(); var result = new StringBuilder();
result.AppendLine("Test result:"); result.AppendLine("Test result:");
result.AppendLine("Fetch guild information: " + (FetchCachedGuild ?? ":white_check_mark:"));
result.AppendLine("Check role permissions: " + (RoleCheck ?? ":white_check_mark:")); result.AppendLine("Check role permissions: " + (RoleCheck ?? ":white_check_mark:"));
result.AppendLine("Number of known users currently with a birthday: " + CurrentBirthdays); result.AppendLine("Number of known users currently with a birthday: " + CurrentBirthdays);
result.AppendLine("Role application process: " + (RoleApply ?? ":white_check_mark:")); result.AppendLine("Role application process: " + (RoleApply ?? ":white_check_mark:"));

View file

@ -14,8 +14,7 @@ namespace BirthdayBot.BackgroundServices
public async override Task OnTick() public async override Task OnTick()
{ {
var count = BotInstance.DiscordClient.Guilds.Count; var count = BotInstance.DiscordClient.Guilds.Count;
var cacheCount = BotInstance.GuildCache.Count; Log($"Currently in {count} guilds.");
Log($"Currently in {count} guilds. Cached guild settings: {cacheCount}.");
await SendExternalStatistics(count); await SendExternalStatistics(count);
} }

View file

@ -17,73 +17,70 @@ namespace BirthdayBot.BackgroundServices
{ {
// Build a list of all values to update // Build a list of all values to update
var updateList = new Dictionary<ulong, List<ulong>>(); var updateList = new Dictionary<ulong, List<ulong>>();
foreach (var gi in BotInstance.GuildCache) foreach (var g in BotInstance.DiscordClient.Guilds)
{ {
var existingUsers = new List<ulong>(); var existingUsers = new List<ulong>();
updateList[gi.Key] = existingUsers; updateList[g.Id] = existingUsers;
var guild = BotInstance.DiscordClient.GetGuild(gi.Key); // Get list of IDs for all users who exist in the database and currently exist in the guild
if (guild == null) continue; // Have cache without being in guild. Unlikely, but... var savedUserIds = from cu in await GuildUserConfiguration.LoadAllAsync(g.Id) select cu.UserId;
var guildUserIds = from gu in g.Users select gu.Id;
// Get IDs of cached users which are currently in the guild var existingCachedIds = savedUserIds.Intersect(guildUserIds);
var cachedUserIds = from cu in gi.Value.Users select cu.UserId;
var guildUserIds = from gu in guild.Users select gu.Id;
var existingCachedIds = cachedUserIds.Intersect(guildUserIds);
} }
using (var db = await BotInstance.Config.DatabaseSettings.OpenConnectionAsync()) using var db = await Database.OpenConnectionAsync();
{
// Prepare to update a lot of last-seen values // Statement for updating last_seen in guilds
var cUpdateGuild = db.CreateCommand(); var cUpdateGuild = db.CreateCommand();
cUpdateGuild.CommandText = $"update {GuildStateInformation.BackingTable} set last_seen = now() " cUpdateGuild.CommandText = $"update {GuildConfiguration.BackingTable} set last_seen = now() "
+ "where guild_id = @Gid"; + "where guild_id = @Gid";
var pUpdateG = cUpdateGuild.Parameters.Add("@Gid", NpgsqlDbType.Bigint); var pUpdateG = cUpdateGuild.Parameters.Add("@Gid", NpgsqlDbType.Bigint);
cUpdateGuild.Prepare(); cUpdateGuild.Prepare();
// Statement for updating last_seen in guild users
var cUpdateGuildUser = db.CreateCommand(); var cUpdateGuildUser = db.CreateCommand();
cUpdateGuildUser.CommandText = $"update {GuildUserSettings.BackingTable} set last_seen = now() " cUpdateGuildUser.CommandText = $"update {GuildUserConfiguration.BackingTable} set last_seen = now() "
+ "where guild_id = @Gid and user_id = @Uid"; + "where guild_id = @Gid and user_id = @Uid";
var pUpdateGU_g = cUpdateGuildUser.Parameters.Add("@Gid", NpgsqlDbType.Bigint); var pUpdateGU_g = cUpdateGuildUser.Parameters.Add("@Gid", NpgsqlDbType.Bigint);
var pUpdateGU_u = cUpdateGuildUser.Parameters.Add("@Uid", NpgsqlDbType.Bigint); var pUpdateGU_u = cUpdateGuildUser.Parameters.Add("@Uid", NpgsqlDbType.Bigint);
cUpdateGuildUser.Prepare(); cUpdateGuildUser.Prepare();
// Do actual updates // Do actual updates
var updates = new List<Task>();
foreach (var item in updateList) foreach (var item in updateList)
{ {
var guild = item.Key; var guild = item.Key;
var userlist = item.Value; var userlist = item.Value;
pUpdateG.Value = (long)guild; pUpdateG.Value = (long)guild;
cUpdateGuild.ExecuteNonQuery(); updates.Add(cUpdateGuild.ExecuteNonQueryAsync());
pUpdateGU_g.Value = (long)guild; pUpdateGU_g.Value = (long)guild;
foreach (var userid in userlist) foreach (var userid in userlist)
{ {
pUpdateGU_u.Value = (long)userid; pUpdateGU_u.Value = (long)userid;
cUpdateGuildUser.ExecuteNonQuery(); updates.Add(cUpdateGuildUser.ExecuteNonQueryAsync());
} }
} }
await Task.WhenAll(updates);
// Delete all old values - expects referencing tables to have 'on delete cascade' // Delete all old values - expects referencing tables to have 'on delete cascade'
using (var t = db.BeginTransaction()) using var t = db.BeginTransaction();
{
int staleGuilds, staleUsers; int staleGuilds, staleUsers;
using (var c = db.CreateCommand()) using (var c = db.CreateCommand())
{ {
// Delete data for guilds not seen in 4 weeks // Delete data for guilds not seen in 4 weeks
c.CommandText = $"delete from {GuildStateInformation.BackingTable} where (now() - interval '28 days') > last_seen"; c.CommandText = $"delete from {GuildConfiguration.BackingTable} where (now() - interval '28 days') > last_seen";
staleGuilds = c.ExecuteNonQuery(); staleGuilds = c.ExecuteNonQuery();
} }
using (var c = db.CreateCommand()) using (var c = db.CreateCommand())
{ {
// Delete data for users not seen in 8 weeks // Delete data for users not seen in 8 weeks
c.CommandText = $"delete from {GuildUserSettings.BackingTable} where (now() - interval '56 days') > last_seen"; c.CommandText = $"delete from {GuildUserConfiguration.BackingTable} where (now() - interval '56 days') > last_seen";
staleUsers = c.ExecuteNonQuery(); staleUsers = c.ExecuteNonQuery();
} }
Log($"Will remove {staleGuilds} guilds, {staleUsers} users."); Log($"Will remove {staleGuilds} guilds, {staleUsers} users.");
t.Commit(); t.Commit();
} }
} }
}
}
} }