Properly handle exceptions in periodic loops

This commit is contained in:
Giuseppe Guerra 2018-08-12 17:18:12 +02:00
parent f11708d463
commit 4e97c717f5
3 changed files with 77 additions and 39 deletions

View File

@ -104,4 +104,7 @@ class invalidUserException(Exception):
pass pass
class wrongChannelException(Exception): class wrongChannelException(Exception):
pass
class periodicLoopException(Exception):
pass pass

View File

@ -1,6 +1,8 @@
import threading import threading
import time import time
from common.sentry import sentry
from constants.exceptions import periodicLoopException
from objects import match from objects import match
from objects import glob from objects import glob
from constants import serverPackets from constants import serverPackets
@ -67,6 +69,7 @@ class matchList:
del self.matches[matchID] del self.matches[matchID]
log.info("MPROOM{}: Room disposed manually".format(_match.matchID)) log.info("MPROOM{}: Room disposed manually".format(_match.matchID))
@sentry.capture()
def cleanupLoop(self): def cleanupLoop(self):
""" """
Start match cleanup loop. Start match cleanup loop.
@ -76,21 +79,34 @@ class matchList:
This method starts an infinite loop, call it only once! This method starts an infinite loop, call it only once!
:return: :return:
""" """
log.debug("Checking empty matches") try:
t = int(time.time()) log.debug("Checking empty matches")
emptyMatches = [] t = int(time.time())
emptyMatches = []
exceptions = []
# Collect all empty matches # Collect all empty matches
for key, m in self.matches.items(): for key, m in self.matches.items():
if [x for x in m.slots if x.user is not None]: if [x for x in m.slots if x.user is not None]:
continue continue
if t - m.createTime >= 120: if t - m.createTime >= 120:
log.debug("Match #{} marked for cleanup".format(m.matchID)) log.debug("Match #{} marked for cleanup".format(m.matchID))
emptyMatches.append(m.matchID) emptyMatches.append(m.matchID)
# Dispose all empty matches # Dispose all empty matches
for matchID in emptyMatches: for matchID in emptyMatches:
self.disposeMatch(matchID) try:
self.disposeMatch(matchID)
except Exception as e:
exceptions.append(e)
log.error(
"Something wrong happened while disposing a timed out match. Reporting to Sentry when "
"the loop ends."
)
# Schedule a new check (endless loop) # Re-raise exception if needed
threading.Timer(30, self.cleanupLoop).start() if exceptions:
raise periodicLoopException(exceptions)
finally:
# Schedule a new check (endless loop)
threading.Timer(30, self.cleanupLoop).start()

View File

@ -5,11 +5,14 @@ import redis
from common.ripple import userUtils from common.ripple import userUtils
from common.log import logUtils as log from common.log import logUtils as log
from common.sentry import sentry
from constants import serverPackets from constants import serverPackets
from constants.exceptions import periodicLoopException
from events import logoutEvent from events import logoutEvent
from objects import glob from objects import glob
from objects import osuToken from objects import osuToken
class tokenList: class tokenList:
def __init__(self): def __init__(self):
self.tokens = {} self.tokens = {}
@ -171,6 +174,7 @@ class tokenList:
for _, value in self.tokens.items(): for _, value in self.tokens.items():
value.enqueue(packet) value.enqueue(packet)
@sentry.capture()
def usersTimeoutCheckLoop(self): def usersTimeoutCheckLoop(self):
""" """
Start timed out users disconnect loop. Start timed out users disconnect loop.
@ -178,27 +182,41 @@ class tokenList:
CALL THIS FUNCTION ONLY ONCE! CALL THIS FUNCTION ONLY ONCE!
:return: :return:
""" """
log.debug("Checking timed out clients") try:
timedOutTokens = [] # timed out users log.debug("Checking timed out clients")
timeoutLimit = int(time.time()) - 100 exceptions = []
for key, value in self.tokens.items(): timedOutTokens = [] # timed out users
# Check timeout (fokabot is ignored) timeoutLimit = int(time.time()) - 100
if value.pingTime < timeoutLimit and value.userID != 999 and not value.irc and not value.tournament: for key, value in self.tokens.items():
# That user has timed out, add to disconnected tokens # Check timeout (fokabot is ignored)
# We can't delete it while iterating or items() throws an error if value.pingTime < timeoutLimit and value.userID != 999 and not value.irc and not value.tournament:
timedOutTokens.append(key) # That user has timed out, add to disconnected tokens
# We can't delete it while iterating or items() throws an error
timedOutTokens.append(key)
# Delete timed out users from self.tokens # Delete timed out users from self.tokens
# i is token string (dictionary key) # i is token string (dictionary key)
for i in timedOutTokens: for i in timedOutTokens:
log.debug("{} timed out!!".format(self.tokens[i].username)) log.debug("{} timed out!!".format(self.tokens[i].username))
self.tokens[i].enqueue(serverPackets.notification("Your connection to the server timed out.")) self.tokens[i].enqueue(serverPackets.notification("Your connection to the server timed out."))
logoutEvent.handle(self.tokens[i], None) try:
del timedOutTokens logoutEvent.handle(self.tokens[i], None)
except Exception as e:
exceptions.append(e)
log.error(
"Something wrong happened while disconnecting a timed out client. Reporting to Sentry "
"when the loop ends."
)
del timedOutTokens
# Schedule a new check (endless loop) # Re-raise exceptions if needed
threading.Timer(100, self.usersTimeoutCheckLoop).start() if exceptions:
raise periodicLoopException(exceptions)
finally:
# Schedule a new check (endless loop)
threading.Timer(100, self.usersTimeoutCheckLoop).start()
@sentry.capture()
def spamProtectionResetLoop(self): def spamProtectionResetLoop(self):
""" """
Start spam protection reset loop. Start spam protection reset loop.
@ -207,12 +225,13 @@ class tokenList:
:return: :return:
""" """
# Reset spamRate for every token try:
for _, value in self.tokens.items(): # Reset spamRate for every token
value.spamRate = 0 for _, value in self.tokens.items():
value.spamRate = 0
# Schedule a new check (endless loop) finally:
threading.Timer(10, self.spamProtectionResetLoop).start() # Schedule a new check (endless loop)
threading.Timer(10, self.spamProtectionResetLoop).start()
def deleteBanchoSessions(self): def deleteBanchoSessions(self):
""" """