@whom

Django & PostgreSQL MemoryError + не правильная работа скрипта?

Начну с объяснения архитектуры проекта. Есть бот, на aiogram, через которого создаются заказы. Заказы состоят из строк(товара), цены и идентификаторов принадлежности заказа.

Задача:
Скриптом обрабатывать все неоплаченные заказы и в случае, если заказ не оплачен уже больше чем N минут, то строки товара возвращаются в таблицу товара, а заказ удаляется.

Моя реализация:
while True:
            users = MyUser.objects.all().exclude(bot_username='null')
            now = timezone.now()
            for admin_user_obj in users:
                for order in User_purchase.objects.raw('SELECT id, time_created, chat_id, balance_to_return FROM user_purchases WHERE is_paid=FALSE and belongs=%s LIMIT 5', [admin_user_obj.id]):
                    if order.time_created + timezone.timedelta(minutes=admin_user_obj.booking_time) < now:
                        try:
                            bot_user = Bot_user.objects.get(belongs=admin_user_obj.id, chat_id=order.chat_id)
                            if order.balance_to_return:
                                bot_user.balance += order.balance_to_return
                            bot_user.active_orders -= 1
                            bot_user.save()
                            send_order_canceled(order, admin_user_obj)
                            for z in User_purchase.objects.raw('SELECT id, strings, item FROM user_purchases WHERE id=%s', [order.id]):
                                add_strings(z.item, z.strings)
                        except:
                            pass
                        order.delete()

Запросы на SQL, потому что иначе возникают ошибки MemoryError.

После запуска и мониторинга логов, скрипт начинает спамить мне строкой товара:
BFZ9S7F.png

И клиент мне сообщил что его 1 строка товара превратилась в 23 миллиона, а несколько строк в брони превратились в 7 миллионов строк. Сказать что меня это удивило, ничего не сказать.

models.py
class User_purchase(models.Model):
    class Meta:
        db_table = 'user_purchases'
        verbose_name = 'Платеж пользователя в боте'
        verbose_name_plural = 'Платежи пользователей в боте'

    chat_id = models.IntegerField()
    belongs = models.IntegerField()
    title = models.TextField()
    strings = models.TextField()
    amount = models.DecimalField(
        decimal_places=2,
        max_digits=10,
        default=0
    )
    balance_to_return = models.DecimalField(
        decimal_places=2,
        max_digits=10,
        default=0)
    note = models.TextField(null=True, blank=True)
    coupon = models.IntegerField(null=True, blank=True)
    is_paid = models.BooleanField(null=True, blank=True, default=True)
    item = models.IntegerField(null=True, blank=True)
    time_created = models.DateTimeField(default=timezone.now)
    time_of_commission = models.DateTimeField(default=timezone.now, null=True, blank=True)


docker-compose
db:
    image: postgres:12.0-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    env_file:
      - ./.env.prod.db
    command: >
      -c work_mem=512MB
      -c maintenance_work_mem=256MB
      -c max_wal_size=1GB

  premium_daemon:
    build: ./app
    command: python manage.py premium_daemon
    volumes:
      - ./app/:/usr/src/app/
    env_file:
      - ./.env.prod
    depends_on:
      - db
  • Вопрос задан
  • 191 просмотр
Пригласить эксперта
Ответы на вопрос 3
@Norkotik
Реализация понравилась
try:
Вся логика
except:
pass
Если эту гомосятину убрать, то смог бы понять где ошибся в логике, а так чему удивляться?
Ответ написан
Комментировать
shurshur
@shurshur
Сисадмин, просто сисадмин...
Как тут уже правильно заметили, не надо ловить все Exception без разбору и даже не выводить никакой ошибки. Ничего хорошего от этого антипаттерна не получится.

Как минимум следует начать хотя бы с такого:

try:
  ...
except Exception as e:
  print (e)


Сейчас же, возможно, случается Exception, но мы этого даже не знаем.

Далее, не стоит использовать ORM вперемешку с обычными запросами. Это тоже источник потенциальных проблем, ведь ORM может кэшировать данные, в том числе ещё не сохранённые. Возможно, до вызова save случился Exception, данные в кэше отличаются от данных в базе, но при прямом запросе в базу мы получим не то же самое, что получили бы из кэша.

Вот такие мелочи могут создавать проблемы, которые при этом вообще нигде даже не отражается. И это ещё повезло, что эффект такой заметный. А если бы эффект проявлялся в одном случае из тысячи? Как бы его потом ловить и что отвечать пользователям, с которых списали лишних денег?
Ответ написан
Комментировать
ky0
@ky0
Миллиардер, филантроп, патологический лгун
Т - тестирование. Д - дебаггинг.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы