Работали ли вы с транзакциями в SQL базах данных через Node.js?

Ответ

Да, активно использовал транзакции в production-приложениях на Node.js, особенно в финансовых и e-commerce системах, где критична целостность данных.

PostgreSQL транзакции с изоляцией:

// Использование разных уровней изоляции
const { Pool } = require('pg');
const pool = new Pool();

async function transferFunds(fromId, toId, amount) {
  const client = await pool.connect();

  try {
    await client.query('BEGIN');
    // Уровень изоляции Repeatable Read предотвращает lost updates
    await client.query('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');

    // Проверка баланса и блокировка строк
    const fromResult = await client.query(
      'SELECT balance FROM accounts WHERE id = $1 FOR UPDATE',
      [fromId]
    );

    if (fromResult.rows[0].balance < amount) {
      throw new Error('Insufficient funds');
    }

    // Два обновления в одной транзакции
    await client.query(
      'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
      [amount, fromId]
    );

    await client.query(
      'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
      [amount, toId]
    );

    // Создание audit записи
    await client.query(
      `INSERT INTO transfers (from_account, to_account, amount, status) 
       VALUES ($1, $2, $3, 'completed')`,
      [fromId, toId, amount]
    );

    await client.query('COMMIT');
    return { success: true };

  } catch (error) {
    await client.query('ROLLBACK');
    // Логика компенсирующих действий
    await logFailedTransaction(fromId, toId, amount, error.message);
    throw error;
  } finally {
    client.release();
  }
}

Особенности в Node.js:

  • Использовал connection pooling с отдельными соединениями для длительных транзакций
  • Реализовывал retry логику для транзакций, падающих из-за serialization failures (PostgreSQL)
  • В микросервисной архитектуре применял Saga паттерн с компенсирующими транзакциями

Ответ 18+ 🔞

А, ну так, значит, ты спрашиваешь про транзакции в продакшене на Node.js? Да, блядь, было дело, особенно в этих ебенях финансовых, где если копейку не туда перевести — сразу пиздец, волнение ебать, все, накрылся медным тазом весь сервис.

Вот, смотри, как это с PostgreSQL обычно выглядит, с изоляцией и всем таким:

// Использование разных уровней изоляции
const { Pool } = require('pg');
const pool = new Pool();

async function transferFunds(fromId, toId, amount) {
  const client = await pool.connect();

  try {
    await client.query('BEGIN');
    // Уровень изоляции Repeatable Read предотвращает lost updates
    await client.query('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');

    // Проверка баланса и блокировка строк
    const fromResult = await client.query(
      'SELECT balance FROM accounts WHERE id = $1 FOR UPDATE',
      [fromId]
    );

    if (fromResult.rows[0].balance < amount) {
      throw new Error('Insufficient funds');
    }

    // Два обновления в одной транзакции
    await client.query(
      'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
      [amount, fromId]
    );

    await client.query(
      'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
      [amount, toId]
    );

    // Создание audit записи
    await client.query(
      `INSERT INTO transfers (from_account, to_account, amount, status) 
       VALUES ($1, $2, $3, 'completed')`,
      [fromId, toId, amount]
    );

    await client.query('COMMIT');
    return { success: true };

  } catch (error) {
    await client.query('ROLLBACK');
    // Логика компенсирующих действий
    await logFailedTransaction(fromId, toId, amount, error.message);
    throw error;
  } finally {
    client.release();
  }
}

Ну и, ёпта, особенности-то в Node.js какие были, да? Главное — не накосячить с пулами соединений, а то будет тебе хиросима и нигерсраки в одном флаконе. Использовал отдельные коннекты для долгих транзакций, чтобы всю систему не застопорить, а то доверия ебать ноль будет к такому коду. А ещё эта дичь с serialization failures в PostgreSQL — сам от себя охуевал, когда в первый раз с этим столкнулся. Пришлось retry-логику пихать, чтобы транзакция, которая из-за параллельного доступа отвалилась, сама себе впендюрила ещё пару попыток. В микросервисах, блядь, вообще весело — там одна транзакция на три сервиса размазана, приходилось паттерн этот, Saga, использовать, с компенсирующими действиями. Чувствуешь подвох? Одна операция в одном сервисе прошла, а в другом — хуй с горы, и надо всё откатывать, как будто ничего и не было. Пизда рулю, короче. Но когда всё работает — красота, ебать мои старые костыли.