Как стать автором
Поиск
Написать публикацию
Обновить

«Мой код не работает!» или как я превратила баг в фичу (почти случайно)

Время на прочтение2 мин
Количество просмотров2.4K

Всем привет.

Я — разработчица, которая пришла в IT не из классического CS-бэкграунда, а из гуманитарной сферы. Python покорил меня своей читаемостью, и вот уже третий год я работаю в продакшене, где сталкиваюсь с самым сложным противником — багами. Особенно теми, которые я сама случайно создаю.

Сегодня расскажу историю одного бага, который чуть не уложил наш сервис. А потом неожиданно превратился в полезную фичу.

Пролог: когда автоочистка пошла не по плану

Контекст: внутренняя система аналитики, где мы собираем пользовательские действия из мобильного приложения. Это большое хранилище событий с ретеншеном 90 дней и еженедельной агрегацией. Всё как обычно.

Моя задача — написать крон-задачу по очистке старых логов. Если событие старше 90 дней, оно должно быть удалено. Логика максимально простая. Код — тоже:

pythonКопироватьРедактироватьcutoff = datetime.now() - timedelta(days=90)
db.delete().where(Event.timestamp < cutoff)

cutoff = datetime.now() - timedelta(days=90) db.delete().where(Event.timestamp < cutoff)

Проверила на тестовой базе — всё красиво. Выкатила.
А в понедельник утром Team Lead открыл отчёты и сказал:

— Ребята, у нас всё исчезло. Пустые графики. Где пользователи?

Баг, который не выглядит как баг

База на месте, запросы работают. Но в отчётах — ноль данных.

Начали копать. Оказалось, что в условии фильтрации по дате я сравнивала локальное datetime.now() с полем Event.timestamp, которое в базе было в UTC. Поскольку крон запускался в 23:30 по Москве, cutoff "съезжал" на час вперёд, и под условие попадали события, которые на самом деле ещё не были "просрочены".

В итоге система удаляла практически все данные за последние 90 дней.

Как баг стал фичей

После восстановления из бэкапа (слава DBA) и краткой паузы на панику, я предложила:
— А что если не удалять, а переносить старые события в архив? Мы ведь часто сталкиваемся с запросами на аналитику за более ранние периоды, но данных уже нет.

Решили попробовать. Вместо удаления стали копировать старые записи в отдельный shard, и только потом — удалять. Плюс появилась система флагов на "мягкое удаление". Примерно так:

pythonКопироватьРедактироватьold_events = session.query(Event).filter(Event.timestamp < cutoff).all()
archive.insert_many(old_events)
# Позже, по расписанию — удаление из основной базы

old_events = session.query(Event).filter(Event.timestamp < cutoff).all() archive.insert_many(old_events) # Позже, по расписанию — удаление из основной базы

Архив оказался полезен буквально через месяц, когда один из руководителей спросил:

— А вы случайно не сохранили данные за прошлый квартал? Хотелось бы сделать сравнение по ретеншену.

Да,мы «случайно» сохранили.

Что я вынесла из этой истории

  1. Ошибки в логике могут быть незаметными. Особенно, когда дело касается времени и часовых поясов.

  2. UTC и локальное время — это разные вселенные. Даже если вы уверены, что всё учли.

  3. Если баг что-то нарушил — посмотрите, можно ли извлечь из этого пользу. Иногда он подсказывает, чего не хватает системе.

  4. Открытая команда важнее, чем безошибочный код. Возможность признать ошибку и вместе найти решение — намного ценнее идеального тест-кейса.

Теги:
Хабы:
-20
Комментарии20

Публикации

Ближайшие события