Skip to content

Commit bea3b20

Browse files
committed
updating the code with exponential backoff for when it tries to retrieve a user
1 parent 9fbb527 commit bea3b20

File tree

2 files changed

+122
-96
lines changed

2 files changed

+122
-96
lines changed

wall_e/extensions/leveling.py

Lines changed: 121 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,15 @@
77
import discord
88
import pytz
99
from discord import NotFound, app_commands, Guild
10-
from discord.errors import DiscordServerError, HTTPException
10+
from discord.errors import DiscordException
1111
from discord.ext import commands, tasks
1212

13-
from utilities.global_vars import bot, wall_e_config
14-
15-
from wall_e_models.models import Level, UserPoint, UpdatedUser, ProfileBucketInProgress
16-
1713
from utilities.embed import embed, COLOUR_MAPPING, WallEColour
1814
from utilities.file_uploading import start_file_uploading
15+
from utilities.global_vars import bot, wall_e_config
1916
from utilities.paginate import paginate_embed
2017
from utilities.setup_logger import Loggers
21-
22-
BUGGY_USER = [234501345749630976]
18+
from wall_e_models.models import Level, UserPoint, UpdatedUser, ProfileBucketInProgress
2319

2420

2521
class Leveling(commands.Cog):
@@ -53,6 +49,7 @@ def __init__(self):
5349
self.council_channel = None
5450
self.levelling_website_avatar_channel = None
5551
self.bucket_update_in_progress = False
52+
self.NUMBER_OF_RETRIEVAL_ATTEMPTS_PER_USER = 15
5653
self.ensure_xp_roles_exist_and_have_right_users.start()
5754
self.process_leveling_profile_data_for_lurkers.start()
5855
self.process_outdated_profile_pics.start()
@@ -67,7 +64,8 @@ async def upload_debug_logs(self):
6764
while self.guild is None:
6865
await asyncio.sleep(2)
6966
await start_file_uploading(
70-
self.logger, self.guild, bot, wall_e_config, self.debug_log_file_absolute_path, "leveling_debug"
67+
self.logger, self.guild, bot, wall_e_config, self.debug_log_file_absolute_path,
68+
"leveling_debug"
7169
)
7270

7371
@commands.Cog.listener(name="on_ready")
@@ -84,7 +82,8 @@ async def upload_error_logs(self):
8482
while self.guild is None:
8583
await asyncio.sleep(2)
8684
await start_file_uploading(
87-
self.logger, self.guild, bot, wall_e_config, self.error_log_file_absolute_path, "leveling_error"
85+
self.logger, self.guild, bot, wall_e_config, self.error_log_file_absolute_path,
86+
"leveling_error"
8887
)
8988

9089
@commands.Cog.listener(name="on_ready")
@@ -558,7 +557,7 @@ async def process_leveling_profile_data_for_lurkers(self):
558557
f"[Leveling process_leveling_profile_data_for_lurkers()] {user_ids_to_update} "
559558
f"potential updates retrieved for bucket {entry.bucket_number_completed}"
560559
)
561-
await self._update_users(self.process_lurkers_logger, user_ids_to_update)
560+
await self._update_users_with_given_ids(self.process_lurkers_logger, user_ids_to_update)
562561
await ProfileBucketInProgress.async_save(entry)
563562

564563
async def _set_bucket_numbers(self, logger):
@@ -671,37 +670,44 @@ async def _get_current_bucket_number(self) -> ProfileBucketInProgress:
671670
entry.bucket_number_completed = 1
672671
return entry
673672

674-
async def _update_users(self, logger, updated_user_ids):
673+
async def _update_users_with_given_ids(self, logger, updated_user_ids):
675674
"""
676675
iterates through the given list of user_ids and updates them
677676
:param updated_user_ids:
678677
:return:
679678
"""
680679
total_number_of_updates_needed = len(updated_user_ids)
681680
for index, user_id in enumerate(updated_user_ids):
681+
if self.user_points[user_id].being_processed:
682+
continue
683+
self.user_points[user_id].being_processed = True
684+
successful = False
682685
logger.debug(
683-
f"[Leveling _update_users()] attempting to get updated user_point profile data for member {user_id} "
684-
f"{index + 1}/{total_number_of_updates_needed} "
686+
f"[Leveling _update_users_with_given_ids()] attempt "
687+
f"{self.user_points[user_id].leveling_update_attempt} to get updated user_point profile data for "
688+
f"member {user_id} {index + 1}/{total_number_of_updates_needed} "
685689
)
686-
member = None
687-
try:
688-
member = await self.guild.fetch_member(user_id)
689-
except (NotFound, DiscordServerError) as e:
690-
try:
691-
logger.info(
692-
f"[Leveling _update_users()] got following error when fetching guild member {user_id}\n{e}"
693-
)
694-
member = await bot.fetch_user(user_id)
695-
except DiscordServerError as e:
696-
logger.error(
697-
f"[Leveling _update_users()] got the following error when fetching member {user_id}\n{e}."
698-
)
699-
except HTTPException as e:
700-
logger.warn(
701-
f"[Leveling _update_users()] got the following error when fetching member {user_id}\n{e}."
690+
while (
691+
not successful and
692+
self.user_points[user_id].leveling_update_attempt < self.NUMBER_OF_RETRIEVAL_ATTEMPTS_PER_USER
693+
):
694+
self.user_points[user_id].leveling_update_attempt += 1
695+
random_number_milliseconds = random.randint(0, 1000) / 1000
696+
sleep_seconds = math.pow(
697+
2, self.user_points[user_id].leveling_update_attempt + random_number_milliseconds
698+
)
699+
logger.debug(
700+
f"[Leveling _update_users_with_given_ids()] Sleeping for {sleep_seconds} before attempt "
701+
f"{self.user_points[user_id].leveling_update_attempt} when trying to update user_point profile "
702+
f"data for member {user_id}."
702703
)
703-
if member:
704-
await self._update_member_profile_data(logger, member, user_id, index, total_number_of_updates_needed)
704+
await asyncio.sleep(sleep_seconds)
705+
member = await self.get_user(logger, user_id)
706+
if member:
707+
successful = await self._update_member_profile_data(
708+
logger, member, user_id, index, total_number_of_updates_needed
709+
)
710+
self.user_points[user_id].being_processed = False
705711

706712
@tasks.loop(seconds=2)
707713
async def process_leveling_profile_data_for_active_users(self):
@@ -715,21 +721,65 @@ async def process_leveling_profile_data_for_active_users(self):
715721
updated_user_logs = await UpdatedUser.get_updated_user_logs()
716722
total_number_of_updates_needed = len(updated_user_logs)
717723
for index, update_user in enumerate(updated_user_logs):
718-
updated_user_log_id = update_user[0] # noqa: F841
719724
updated_user_id = update_user[1]
725+
if self.user_points[updated_user_id].being_processed:
726+
continue
727+
self.user_points[updated_user_id].being_processed = True
728+
updated_user_log_id = update_user[0] # noqa: F841
729+
successful = False
720730
self.logger.debug(
721-
f"[Leveling process_leveling_profile_data_for_active_users()] attempting to get updated "
722-
f"user_point profile data for member {updated_user_id} "
723-
f"{index + 1}/{total_number_of_updates_needed} "
731+
f"[Leveling process_leveling_profile_data_for_active_users()] attempt "
732+
f"{self.user_points[updated_user_id].leveling_update_attempt} to get data for user in UpdatedUser"
733+
f" with id {updated_user_id} {index + 1}/{total_number_of_updates_needed} "
724734
)
735+
while (
736+
not successful and
737+
self.user_points[updated_user_id].leveling_update_attempt <
738+
self.NUMBER_OF_RETRIEVAL_ATTEMPTS_PER_USER
739+
):
740+
self.user_points[updated_user_id].leveling_update_attempt += 1
741+
random_number_milliseconds = random.randint(0, 1000) / 1000
742+
sleep_seconds = math.pow(
743+
2, self.user_points[updated_user_id].leveling_update_attempt + random_number_milliseconds
744+
)
745+
self.logger.debug(
746+
f"[Leveling process_leveling_profile_data_for_active_users()] Sleeping for {sleep_seconds} before"
747+
f" attempt {self.user_points[updated_user_id].leveling_update_attempt} when trying to get data"
748+
f" for user in UpdatedUser with id {updated_user_id}."
749+
)
750+
await asyncio.sleep(sleep_seconds)
751+
member = await self.get_user(self.logger, updated_user_id)
752+
if member:
753+
# cannot call _update_users_with_given_ids like process_outdated_profile_pics and
754+
# process_leveling_profile_data_for_lurkers because this task needs to be able to specify a
755+
# updated_user_log_id that will be deleted update_leveling_profile_info when the user is processed
756+
successful = await self._update_member_profile_data(
757+
self.logger, member, updated_user_id, index, total_number_of_updates_needed,
758+
updated_user_log_id=updated_user_log_id
759+
)
760+
self.user_points[updated_user_id].being_processed = False
761+
762+
async def get_user(self, logger, user_id):
763+
user = None
764+
try:
765+
user = await self.guild.fetch_member(user_id)
766+
except NotFound as e:
725767
try:
726-
member = await self.guild.fetch_member(updated_user_id)
727-
except NotFound:
728-
member = await bot.fetch_user(updated_user_id)
729-
await self._update_member_profile_data(
730-
self.logger, member, updated_user_id, index, total_number_of_updates_needed,
731-
updated_user_log_id=updated_user_log_id
768+
logger.info(
769+
f"[Leveling get_user()] unable to find guild member {user_id}\n{e}"
770+
)
771+
user = await bot.fetch_user(user_id)
772+
except DiscordException as e:
773+
logger.error(
774+
f"[Leveling get_user()] got the following error when fetching discord user "
775+
f"{user_id}\n{e}."
776+
)
777+
except DiscordException as e:
778+
logger.error(
779+
f"[Leveling get_user()] got the following error when fetching guild member "
780+
f"{user_id}\n{e}."
732781
)
782+
return user
733783

734784
@tasks.loop(seconds=5)
735785
async def process_outdated_profile_pics(self):
@@ -747,7 +797,7 @@ async def process_outdated_profile_pics(self):
747797
f"[Leveling process_outdated_profile_pics()] {number_of_users_to_update} users with outdated CDN links"
748798
f" to update"
749799
)
750-
await self._update_users(self.update_outdated_profile_pics_logger, user_ids_to_update)
800+
await self._update_users_with_given_ids(self.update_outdated_profile_pics_logger, user_ids_to_update)
751801
self.process_outdated_profile_pics_in_progress = False
752802

753803
async def _update_member_profile_data(self, logger, member, updated_user_id, index,
@@ -763,62 +813,38 @@ async def _update_member_profile_data(self, logger, member, updated_user_id, ind
763813
current loop
764814
:param updated_user_log_id: the ID of the UpdatedUser record to delete if this function was called as a
765815
result of member_update_listener's recording any detected changed in UpdatedUser
766-
:return:
816+
:return: True if member was successfully processed
767817
"""
768-
if member:
769-
if member.id in BUGGY_USER:
770-
return
771-
try:
772-
if self.user_points[member.id].leveling_update_attempt >= 5:
773-
log_level = logger.error if updated_user_log_id is None else logger.debug
774-
# wil print out this log line at ERROR only if its not part of the code that attempts to update
775-
# active users returned by UpdatedUser.get_updated_user_logs()
776-
# I am doing this only cause if you happen to have a backlog of users to work through, you will
777-
# mistakenly get alerted about attempts to update the user info not being successful when in
778-
# actuality what is happening is just the first process was all that was necessary and the
779-
# rest of superfluous but I don't want to update the database to take in just 1 log for each
780-
# user for the times when the task is active and therefore one log for each update would
781-
# actually be useful
782-
log_level(
783-
f"[Leveling _update_member_profile_data()] "
784-
f"attempt {self.user_points[member.id].leveling_update_attempt} to update the member profile"
785-
f" data in the database for member {member} with id [{member.id}], expiry_date of "
786-
f"[{self.user_points[member.id].discord_avatar_link_expiry_date.pst}] and a CDN link of "
787-
f"<{self.user_points[member.id].leveling_message_avatar_url}> "
788-
f"{index + 1}/{total_number_of_updates_needed}"
789-
)
790-
else:
791-
# leveling_update_attempt is reset to 0 in update_leveling_profile_info if member is successfully
792-
# updated THIS time
793-
logger.debug(
794-
f"[Leveling _update_member_profile_data()] attempt"
795-
f" {self.user_points[member.id].leveling_update_attempt} to"
796-
f" update the member profile data in the database for member {member} with"
797-
f" id [{updated_user_id}] {index + 1}/{total_number_of_updates_needed}"
798-
)
799-
user_updated = await self.user_points[member.id].update_leveling_profile_info(
800-
logger, self.guild.id, member, self.levelling_website_avatar_channel,
801-
updated_user_log_id=updated_user_log_id
802-
)
803-
if user_updated:
804-
logger.debug(
805-
f"[Leveling _update_member_profile_data()] updated the member profile data"
806-
f" in the database for member {member} with id [{updated_user_id}] "
807-
f"{index + 1}/{total_number_of_updates_needed}"
808-
)
809-
except Exception as e:
810-
logger.error(
811-
f"[Leveling _update_member_profile_data()] unable to update the member profile"
812-
f" data in the database for member {member} with id [{updated_user_id}]"
813-
f" {index + 1}/{total_number_of_updates_needed} due to error:\n{e}"
818+
user_updated = False
819+
try:
820+
logger.debug(
821+
f"[Leveling _update_member_profile_data()] "
822+
f"attempt {self.user_points[member.id].leveling_update_attempt} to update the member profile"
823+
f"data in the database for member {member} with id [{member.id}], "
824+
f"updated_user_log_id = {updated_user_log_id}, expiry_date of "
825+
f"[{self.user_points[member.id].discord_avatar_link_expiry_date.pst}] and a CDN link of "
826+
f"<{self.user_points[member.id].leveling_message_avatar_url}> "
827+
f"{index + 1}/{total_number_of_updates_needed}"
828+
)
829+
# leveling_update_attempt is reset to 0 in update_leveling_profile_info if member is successfully
830+
# updated THIS time
831+
user_updated = await self.user_points[member.id].update_leveling_profile_info(
832+
logger, self.guild.id, member, self.levelling_website_avatar_channel,
833+
updated_user_log_id=updated_user_log_id
834+
)
835+
if user_updated:
836+
logger.debug(
837+
f"[Leveling _update_member_profile_data()] updated the member profile data"
838+
f" in the database for member {member} with id [{updated_user_id}] "
839+
f"{index + 1}/{total_number_of_updates_needed}"
814840
)
815-
else:
816-
logger.warn(
817-
f"[Leveling _update_member_profile_data()] attempt "
818-
f"{self.user_points[member.id].leveling_update_attempt}: unable to update the member profile data"
819-
f" in the database for member {updated_user_id} {index + 1}/{total_number_of_updates_needed}"
841+
except Exception as e:
842+
logger.error(
843+
f"[Leveling _update_member_profile_data()] unable to update the member profile"
844+
f" data in the database for member {member} with id [{updated_user_id}] and updated_user_log_id"
845+
f" = {updated_user_log_id} {index + 1}/{total_number_of_updates_needed} due to error:\n{e}"
820846
)
821-
await self.user_points[member.id].async_save()
847+
return user_updated
822848

823849
@app_commands.command(name="reset_attempts")
824850
@app_commands.checks.has_any_role("Bot_manager")

0 commit comments

Comments
 (0)