diff --git a/BirthdayBot/BackgroundServices/StaleDataCleaner.vb b/BirthdayBot/BackgroundServices/StaleDataCleaner.vb index 1bdc2cb..eb22f95 100644 --- a/BirthdayBot/BackgroundServices/StaleDataCleaner.vb +++ b/BirthdayBot/BackgroundServices/StaleDataCleaner.vb @@ -1,5 +1,4 @@ -Imports Npgsql -''' +''' ''' Automatically removes database information for guilds that have not been accessed in a long time. ''' Class StaleDataCleaner @@ -10,29 +9,66 @@ Class StaleDataCleaner End Sub Public Overrides Async Function OnTick() As Task - Using db = Await BotInstance.Config.DatabaseSettings.OpenConnectionAsync() - ' Update only for all guilds the bot has cached - Using c = db.CreateCommand() - c.CommandText = $"update {GuildStateInformation.BackingTable} set last_seen = now() " + - "where guild_id = @Gid" - Dim updateGuild = c.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint) - c.Prepare() + ' Build a list of all values to update + Dim updateList As New Dictionary(Of ULong, List(Of ULong)) + For Each gi In BotInstance.GuildCache + Dim existingUsers As New List(Of ULong)() + updateList(gi.Key) = existingUsers - Dim list As New List(Of ULong)(BotInstance.GuildCache.Keys) - For Each id In list - updateGuild.Value = CLng(id) - c.ExecuteNonQuery() + Dim guild = BotInstance.DiscordClient.GetGuild(gi.Key) + If guild Is Nothing Then Continue For ' Have cache without being in guild. Unlikely, but... + + ' Get IDs of cached users which are currently in the guild + Dim cachedUserIds = From cu In gi.Value.Users Select cu.UserId + Dim guildUserIds = From gu In guild.Users Select gu.Id + Dim existingCachedIds = cachedUserIds.Intersect(guildUserIds) + existingUsers.AddRange(existingCachedIds) + Next + + Using db = Await BotInstance.Config.DatabaseSettings.OpenConnectionAsync() + ' Prepare to update a lot of last-seen values + Dim cUpdateGuild = db.CreateCommand() + cUpdateGuild.CommandText = $"update {GuildStateInformation.BackingTable} set last_seen = now() " + + "where guild_id = @Gid" + Dim pUpdateG = cUpdateGuild.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint) + cUpdateGuild.Prepare() + + Dim cUpdateGuildUser = db.CreateCommand() + cUpdateGuildUser.CommandText = $"update {GuildUserSettings.BackingTable} set last_seen = now() " + + "where guild_id = @Gid and user_id = @Uid" + Dim pUpdateGU_g = cUpdateGuildUser.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint) + Dim pUpdateGU_u = cUpdateGuildUser.Parameters.Add("@Uid", NpgsqlTypes.NpgsqlDbType.Bigint) + cUpdateGuildUser.Prepare() + + ' Do actual updates + For Each item In updateList + Dim guild = item.Key + Dim userlist = item.Value + + pUpdateG.Value = CLng(guild) + cUpdateGuild.ExecuteNonQuery() + + pUpdateGU_g.Value = CLng(guild) + For Each userid In userlist + pUpdateGU_u.Value = CLng(userid) + cUpdateGuildUser.ExecuteNonQuery() Next - End Using + Next ' Delete all old values - expects referencing tables to have 'on delete cascade' Using t = db.BeginTransaction() Using c = db.CreateCommand() - ' Delete data for guilds not seen in 2 weeks + ' Delete data for guilds not seen in 4 weeks c.CommandText = $"delete from {GuildStateInformation.BackingTable} where (now() - interval '28 days') > last_seen" Dim r = c.ExecuteNonQuery() If r <> 0 Then Log($"Removed {r} stale guild(s).") End Using + Using c = db.CreateCommand() + ' Delete data for users not seen in 8 weeks + c.CommandText = $"delete from {GuildUserSettings.BackingTable} where (now() - interval '56 days') > last_seen" + Dim r = c.ExecuteNonQuery() + If r <> 0 Then Log($"Removed {r} stale user(s).") + End Using End Using End Using End Function diff --git a/BirthdayBot/Data/GuildUserSettings.vb b/BirthdayBot/Data/GuildUserSettings.vb index 173c666..45ca57b 100644 --- a/BirthdayBot/Data/GuildUserSettings.vb +++ b/BirthdayBot/Data/GuildUserSettings.vb @@ -130,6 +130,7 @@ Class GuildUserSettings "birth_month integer not null, " + "birth_day integer not null, " + "time_zone text null, " + + "last_seen timestamptz not null default NOW(), " + "PRIMARY KEY (guild_id, user_id)" + ")" c.ExecuteNonQuery()