Properly handle exceptions in periodic loops
This commit is contained in:
		@@ -104,4 +104,7 @@ class invalidUserException(Exception):
 | 
				
			|||||||
	pass
 | 
						pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class wrongChannelException(Exception):
 | 
					class wrongChannelException(Exception):
 | 
				
			||||||
 | 
						pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class periodicLoopException(Exception):
 | 
				
			||||||
	pass
 | 
						pass
 | 
				
			||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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):
 | 
				
			||||||
		"""
 | 
							"""
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user