Не могу понять почему накопительно увеличивается потребление оперативной памяти
Каковы примерные причины накопительного увеличения оперативной памяти сервера?
код использую для бота дискорд, бот изначально запускается, и хост показывает потребление памяти в пределах 300-350 мб, но спустя 1-2 часа, это значение доходит до 1.5гб.
Попробовал использовать gc.collect(), но это результата не дает. то, что это та часть кода, которая засоряет оперативную память, выяснил путем комментирования разных команд, и эмпирическим путем получил то, что при комментировании этой части кода оперативная память стабильно стоит в 145-150 на протяжении более 12 часов.
def load_last_row_count():
try:
with open('monitor_count.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
return 0
def save_last_row_count(last_row_count):
with open('monitor_count.json', 'w') as f:
json.dump(last_row_count, f)
last_row_count = load_last_row_count()
@tasks.loop(seconds=10)
async def monitor_sheet():
global last_row_count
sheet_id = sheet_URL
range_name = sheet_range_name
try:
values = get_sheet_values(sheet_id, range_name, SERVICE_ACCOUNT_FILE, SCOPES)
current_row_count = len(values)
if last_row_count == current_row_count:
return
for i in range(last_row_count, current_row_count):
new_entry = values[i]
if new_entry[5] == "HiTech":
message = format_message(new_entry)
await send_discord_notification(bot, MONITOR_CHANNEL_ID, message)
last_row_count = current_row_count
save_last_row_count(last_row_count)
except Exception as e:
print(f"Ошибка при мониторинге таблицы: {e}")
await send_discord_notification(bot, MONITOR_CHANNEL_ID, f"Ошибка при мониторинге таблицы: {e}")
@bot.event
async def on_ready():
current_time = datetime.now().strftime("%H:%M:%S")
print(f'Подключение успешно {bot.user} в {current_time}')
await send_discord_notification(bot, MONITOR_CHANNEL_ID, f"Подключено в {current_time}")
monitor_sheet.start()
объяснение кода:
бот обращается каждые 10 секунд к гугл таблице, и сверяет количество строк в момент обращения с количеством строк в прошлое обращение, если есть разница между количеством, то он отправляет эти строки мне в дискорд, с учетом условия, в 6 столбце должно быть написано слово HiTech
.
также в момент нового запроса он записывает количество строк в .json
файл, чтобы сверить его в следующем запросе.
на всякий случай прикреплю еще все функции, которые используются:
def get_sheet_values(sheet_id, range_name, service_account_file, scopes):
creds = service_account.Credentials.from_service_account_file(service_account_file, scopes=scopes)
service = build('sheets', 'v4', credentials=creds)
sheet = service.spreadsheets()
result = sheet.values().get(spreadsheetId=sheet_id, range=range_name).execute()
values = result.get('values', [])
return values
def format_message(entry):
fields = ["Дата и время заполнения заявки", "e-mail", "Игровой ник", "Имя", "Для связи", "Сервер", "Номер сервера",
"Опыт", "Время от МСК", "Сколько готов играть", "Знание модов", "О себе"]
values = [entry[i].strip() if i < len(entry) and entry[i].strip() else "*данные не заполнены*" for i in
range(len(fields))]
formatted_message = (
f"# Вот это заявочка ? \n"
f"{fields[2]}: **{values[2]}**\n"
f"{fields[3]}: **{values[3]}**\n"
f"{fields[0]}: **{values[0]}**\n"
f"{fields[1]}: **{values[1]}**\n"
f"{fields[4]}: **{values[4]}**\n"
f"{fields[5]}: **{values[5]}**\n"
f"{fields[6]}: **{values[6]}**\n"
f"{fields[7]}: **{values[7]}**\n"
f"{fields[8]}: **{values[8]}**\n"
f"{fields[9]}: **{values[9]}**\n"
f"{fields[10]}: **{values[10]}**\n"
f"{fields[11]}: **{values[11]}**"
)
return formatted_message
async def send_discord_notification(bot: commands.Bot, channel_id: int, message):
channel = bot.get_channel(channel_id)
if channel:
try:
await channel.send(message)
except Exception as e:
print(f"Ошибка при отправке сообщения в канал: {e}")
else:
print("Не удалось найти канал для отправки уведомления.")
Если есть вопрос а зачем а для чего тебе строки таблицы в дискорд. Вот мой ответ, в эту таблицу строки добавляются из гугл формы, в нее не так часто смотрят, и иногда пропускают заявки от людей, дабы это избежать, хочу, чтобы все новые заявки автоматически отправлялись в дискорд.
Ответы (3 шт):
Попробуйте отказаться от объявления global
в async
которую будете изменять.
def load_last_row_count():
try:
with open('monitor_count.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
return 0
def save_last_row_count(last_row_count):
with open('monitor_count.json', 'w') as f:
json.dump(last_row_count, f)
@tasks.loop(seconds=10)
async def monitor_sheet():
last_row_count = load_last_row_count()
global
- первое что бросается в глаза.
Вот в этом обсуждении подобной утечки советуют создавать sheets
один раз и тогда утечка уходит или становится практически незаметной.
То есть вам нужно вынести этот кусок кода в отдельную функцию, которую вызывать в самом начале один раз, а в остальные функции передать уже готовую переменную sheets
, которой дальше и пользоваться:
def GetSheet():
creds = service_account.Credentials.from_service_account_file(service_account_file, scopes=scopes)
service = build('sheets', 'v4', credentials=creds)
sheet = service.spreadsheets()
return sheet
А вот здесь пишут, что проблема скорее всего в вызове build
, то есть, возможно, достаточно один раз создать service
, а не sheet
.
Помогло создание класса и правильный вызов. Утечка прекращена:
class GoogleSheetAPI:
def __init__(self, service_account_file, scopes):
self.creds = service_account.Credentials.from_service_account_file(service_account_file, scopes=scopes)
self.service = build('sheets', 'v4', credentials=self.creds)
self.sheet = self.service.spreadsheets()
def get_sheet_values(self, sheet_id, range_name):
result = self.sheet.values().get(spreadsheetId=sheet_id, range=range_name).execute()
values = result.get('values', [])
return values
ну и использование:
google_sheet_api = GoogleSheetAPI(service_account_file, scopes)
values = google_sheet_api.get_sheet_values(sheet_id, range_name)
Вот это нужно вызывать один раз:
google_sheet_api = GoogleSheetAPI(service_account_file, scopes)
А дальше каждый раз, соответственно:
values = google_sheet_api.get_sheet_values(sheet_id, range_name)