Назад к вопросам
2gis
Senior
6
// 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()
Компании, где спрашивали
Ответ от нейросети
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()
}
}
Такой код гарантирует, что два пользователя не смогут одновременно забронировать одно и то же место.