
Комментарии 4
ORM с сущностями в целом часто скрывает подобные проблемы. Работать просто с SQL в таких сценариях выходит проще - запрос пишется точечно под сценарий, данные простые на входе и на выходе.
Согласен, по итогу мы примерно туда и пришли: выборка нужных колонок, значения в кортежах, bulk UPDATE. Фактически тот же SQL, просто через Core.
Мне кажется, водораздел проходит не между ORM и SQL, а между типами кода. В обычных ручках API объекты удобнее: транзакция короткая, сущность одна, читается лучше. А в фоновом проходе по всей базе, где чтение и запись вперемешку, ORM прячет цену. Причём прячет так, что заметно её только счётчиком запросов.
Интересный кейс, особенно момент с реальным подсчётом запросов. Только при общем UPDATE после рассылки может появиться другая проблема: если процесс отправит половину уведомлений и упадёт, при следующем запуске они уйдут повторно. У вас отправка идемпотентная или такие повторы допустимы?
Да, всё так, это осознанный at-least-once.
Окно, правда, узкое. Флаг ставится только если отправка реально прошла: вернулась ошибка, id в пачку не попал и уйдёт следующим тиком. И коммит не в конце всего прохода, а после каждого тренера. То есть при падении процесса теряется максимум пачка одного человека, обычно одно-три напоминания, а не половина рассылки.
Цена дубля тут небольшая, лишнее «через час тренировка». Пережить можно, поэтому сознательно живём с повтором.
А вот где нельзя, это деньги. Там механизм другой: атомарный UPDATE … SET applied = true WHERE applied = false и проверка, сколько строк реально обновилось. Начисляем, только если строка досталась нам. Повторная доставка вебхука на этом и гасится.
Предзагрузил пачкой — получил N² запросов. Как expire_on_commit превращает оптимизацию в квадрат