Телеграм-бот @Donate1024Bot щоранку розсилає всім підписникам новий збір. Щоб уникнути випадкового подвійного спрацювання, цикл розсилки схований всередину select for update
.
Коли робив інтеграцію з Bluesky, то додав код, який також публікує збір у Bluesky. Звичайно, я не перевіряв E2E виклик, і просто скопіпастив рядок з реплу. Виглядало це якось так:
Lock.find_by(key: "daily_post").with_lock do
random_post = Post.random_post
TgUser.pluck(:tg_id).each do |tg_id|
SendDailyPostToUserJob.perform_later(tg_id, random_post.id)
end
random_post.update(posted_count: random_post.posted_count + 1)
SendDailyPostToBskyJob.perform_later(post_id)
end
Запушив код опівночі та й ліг спати.
І звичайно що я побачив наступного ранку? Вірно, 6 однакових постів у себе.
Справдився мій найбільший страх — зациклити відправку однакових повідомлень клієнтам.
Тесту на цей код у мене теж не було, бо ліньки було возитися з моканням та стабанням і перевіркою асинхронних штук. На тому й погорів.
Уважний читач звісно помітить тривіальну помилку.
Також я не мав перевірок на те, що збір вже було відправлено конкретному підписнику. Тому створювалась пачка джоб які асинхронно відправляли його в окремих транзакціях, а транзакція, що мала б проставити посту прапорець «опубліковано» не комітилась, джоба ретраїлась, і все йшло по колу.
Добре що я помітив це достатньо швидко та мав можливість виправити, погано що недостатньо швидко і всім таки прийшло шість однакових повідомлень.
Ймовірно через це декілька людей відписались та не задонатили гроші, і тепер сума зборів впала на 3-5 тисяч гривень😔
Зараз я також ще раз подивився на цей код та знайшов там декілька способів хороших оптимізацій, але вже боюсь його пушити, бо немає тестів 🤦♂️️. Схоже що настав час таки їх зробити.
🥺Вибачте всі кому прийшли зайві повідомлення. Я знаю що тих хто досі читає бот, а не закинув його в архів, лише кілька десятків, але мені дуже цінна підтримка кожного з вас.
P.S.: в мене була купа тестів на те що правильно обчислюється щоденний збір — що він у коректному статусі, не дублює попередні, публікується у правильному порядку і так далі. Але це все були юніти, а такого, щоб перевірив повністю як відпрацював лок та джоби та ще у декількох потоках — ні. Бо складно та й так все два з половиною роки працювало як годинник.
P.P.S.: можете порадувати ріжка та підписатися на бот @Donate1024Bot і донатити щоденно хоча б по сотні гривень — то вже буде досить потужно🥺
update: після того, як я написав пост, натрапив на таку штуку в Rails як запуск джоб після коміту транзакції🤯. Хтось уже не раз зіткнувся з проблемою як у мене і написав для неї коробочне рішення. Читайте доки!
Сподобалось? Долучайтеся до мого телеграм каналу: https://t.me/full_of_hatred