Транзакція відкочує записи в базу - і більше нічого. Усе, що встигло вийти за її межі, лишається зробленим.
П'ять місць, де це видно:
1. Побічні ефекти не відкочуються. Лист, відправлений усередині транзакції, піде навіть після rollback - база відкотиться, а користувач уже отримав «Замовлення оформлено».
2. Завдання стартує раніше за commit. Воркер може взяти завдання до завершення транзакції й не знайти запису. Лікується afterCommit() на завданні або ShouldDispatchAfterCommit на події:
ProcessOrder::dispatch($order)->afterCommit();
3. Вкладені транзакції - не транзакції. Друга DB::transaction() усередині першої не створює окрему: використовуються savepoint-и, і зовнішній rollback скасує все одно все.
4. DDL не відкочується в MySQL. Schema::create() усередині транзакції робить неявний commit - міграції на MySQL атомарними не бувають, на відміну від PostgreSQL.
5. Довга транзакція тримає блокування. Звернення до зовнішнього API всередині транзакції означає, що рядки заблоковані на весь час мережевого очікування.
Практичне правило: усередині транзакції - лише запити до бази. Листи, черги, HTTP - після неї, за фактом успіху.