Вопрос проверяет понимание того, как выполнение синхронных операций (например, HTTP-запросов или чтения файлов) внутри транзакции базы данных может нарушить атомарность и изоляцию, приводя к неконсистентности данных.
Транзакция в базе данных — это последовательность операций, которая должна удовлетворять свойствам ACID: атомарность, согласованность, изоляция и долговечность. Ключевая идея в том, что все изменения внутри транзакции либо применяются целиком, либо откатываются полностью, обеспечивая целостность данных.
Синхронный вызов — это операция, которая блокирует выполнение кода до своего завершения. Примеры: HTTP-запрос к другому сервису, чтение файла с диска, вызов удалённого API. Когда такой вызов выполняется внутри транзакции, возникают следующие риски:
// Псевдокод, иллюстрирующий антипаттерн
beginTransaction();
try {
// 1. Операция с базой данных
updateAccountBalance(userId, -amount);
// 2. СИНХРОННЫЙ вызов к внешнему платежному шлюзу
let paymentResult = synchronousHttpCall('https://payment-gateway/charge', { amount });
if (!paymentResult.success) {
throw new Error('Payment failed');
}
// 3. Еще одна операция с БД
logTransaction(userId, amount);
commitTransaction();
} catch (error) {
rollbackTransaction(); // Откатывает шаги 1 и 3, но не шаг 2!
// Платеж уже списан у шлюза, а баланс пользователя восстановлен — неконсистентность.
}Основной подход — выносить взаимодействие с внешними системами за пределы транзакции базы данных. Можно использовать паттерны:
Вывод: Синхронные вызовы внутри транзакции — это архитектурный антипаттерн, который ведёт к хрупкости системы и неконсистентности данных. Для интеграции с внешними сервисами следует использовать асинхронные, идемпотентные и компенсируемые паттерны, сохраняя транзакции БД короткими и быстрыми.