Sobes.tech
Back to questions
Senior
5

// book(seat_id, user_id) PostgreSQL Begin Transaction(read_commited) seat = select * from seats where id = seat_id for update if(seat == null seat.status == "booked") rollback return error("Seat isnt available") insert into bookings (user_id, seat_id, status, created_at) values (used_id, ...) update seats set status = 'booked' where id = seat_id commit return success()

Companies where asked
2gis2gis

Answer from AI

sobes.tech AI

Данный код демонстрирует транзакционную блокировку и бронирование места (seat) в PostgreSQL с уровнем изоляции read_committed.

Основная идея:

  • Начинается транзакция.
  • Выполняется выборка места с блокировкой FOR UPDATE, чтобы предотвратить одновременное бронирование одним и тем же местом.
  • Если место не найдено или уже забронировано, транзакция откатывается и возвращается ошибка.
  • Если место доступно, создаётся запись бронирования и обновляется статус места на "booked".
  • Транзакция коммитится.

Такой подход предотвращает состояние гонки (race condition) при параллельных попытках бронирования одного и того же места.

Пример на Kotlin с использованием JDBC:

fun bookSeat(seatId: Int, userId: Int): Boolean {
    val connection = dataSource.connection
    try {
        connection.autoCommit = false
        connection.transactionIsolation = Connection.TRANSACTION_READ_COMMITTED

        val seatStmt = connection.prepareStatement("SELECT status FROM seats WHERE id = ? FOR UPDATE")
        seatStmt.setInt(1, seatId)
        val rs = seatStmt.executeQuery()

        if (!rs.next() || rs.getString("status") == "booked") {
            connection.rollback()
            return false // Место недоступно
        }

        val insertBooking = connection.prepareStatement(
            "INSERT INTO bookings (user_id, seat_id, status, created_at) VALUES (?, ?, 'booked', NOW())"
        )
        insertBooking.setInt(1, userId)
        insertBooking.setInt(2, seatId)
        insertBooking.executeUpdate()

        val updateSeat = connection.prepareStatement("UPDATE seats SET status = 'booked' WHERE id = ?")
        updateSeat.setInt(1, seatId)
        updateSeat.executeUpdate()

        connection.commit()
        return true
    } catch (e: SQLException) {
        connection.rollback()
        throw e
    } finally {
        connection.close()
    }
}

Такой код гарантирует, что два пользователя не смогут одновременно забронировать одно и то же место.