为什么在 Sequelize 中,锁定行会导致同一事务中的更新卡住?

问题描述 投票:0回答:1

我使用node.js作为预订机票项目的后端,并且我还使用Sequelize在数据库(PostgreSQL)上进行操作。该数据库在线,托管在 Clever Cloud 上。我有两个控制器,名为 Seats.js 和 Bookings.js,第一个控制器用于检查我必须预订的座位并锁定它们以防止更新期间出现竞争情况,第二个控制器用于有效预订座位和门票。所有这些都包含在一个事务中,但是由于未知的原因,当席位被锁定时,我无法更新它们(虽然它们都在同一个事务中)并且更新被卡住了。

提前感谢您的回答。

我期望运行时不会卡住,但事实并非如此,这就是 Node.js 给我的日志:

Executing (bd3d1a7b-5e49-49f9-92db-6cf50afd2a1b): START TRANSACTION; 
Executing (bd3d1a7b-5e49-49f9-92db-6cf50afd2a1b): SET TRANSACTION ISOLATION LEVEL READ COMMITTED; Executing (bd3d1a7b-5e49-49f9-92db-6cf50afd2a1b): SELECT "seat_number", "flight_number", "price", "is_booked", "version" FROM "Seats" AS "Seats" WHERE "Seats"."seat_number" = 'B3' AND "Seats"."flight_number" = 'U2 4833' FOR UPDATE; 
Executing (default): SELECT "flight_number", "fk_IATA_from", "fk_IATA_to", "departure", "arrival", "price", "fk_airline" FROM "Flights" AS "Flights" WHERE "Flights"."flight_number" = 'U2 4833'; 
Executing (default): SELECT "email", "password", "name", "surname" FROM "Users" AS "Users" WHERE "Users"."email" = '[email protected]'; 
Executing (default): SELECT "seat_number", "flight_number", "price", "is_booked", "version" FROM "Seats" AS "Seats" WHERE "Seats"."flight_number" = 'U2 4833' AND "Seats"."seat_number" = 'B3' AND "Seats"."is_booked" = true; 
Executing (default): UPDATE "Seats" SET "is_booked"=$1,"version"=version + 1 WHERE "seat_number" = $2 AND "flight_number" = $3 AND "version" = $4  

最后一行(UPDATE)被卡住,无法前进到其他查询。

启动 SELECT 查询的代码是:

(Seats.js)

const checkSeatForBooking = async (req, transaction, res, next) => {
  const { seatNumber, flightNumber } = req;
  
  try {
    const check = await Seats.findOne({
      where: {
        seat_number: seatNumber,
        flight_number: flightNumber
      },
      lock: transaction.LOCK.UPDATE,
      transaction
    });

    if (!check) {
      return {
        success: false,
        seat_number: seatNumber,
        message: `Seat ${seatNumber} doesn't exist`,
      };
    } else if (check.isBooked) {
      return {
        success: false,
        seat_number: check.seat_number,
        message: `Seat ${check.seat_number} booked previously`,
      };
    }

    return check;
  } catch(error) {
    return {
      success: false,
      message: "Failed checking of seat",
      error: error.message,
    };
  }
}

首先启动 checkSeatForBooking 然后启动 UPDATE 查询的代码是:

(Bookings.js)

const insertBookings = async (req, res, next) => {
    const transaction = await instanceSequelize.transaction({
      isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED
    });
    
    try {
        const flightState = req.body.flightState;
        const seatsFlightsDeparture = flightState.seatsFlightsDeparture;
        const departureTicketsToBook = flightState.seatsFlightsDeparture;
        const seatsFlightsReturning = flightState.seatsFlightsReturning;
        const returningTicketsToBook = flightState.seatsFlightsReturning;
    
        //Check seats
        for (const flight of seatsFlightsDeparture) {
          for (const seat of flight) {
            const seatCheckResult = await checkSeatForBooking(seat, transaction);
        
            if (!seatCheckResult) {
              await transaction.rollback();
              return res.status(400).json(seatCheckResult);
            }
        
            seat.seatNumber = seatCheckResult.seat_number;
            seat.seatPrice = seatCheckResult.price;
            seat.version = seatCheckResult.version;
          }
        }
        
        //Other code not useful for this problem
        
        //UPDATE SEATS
        for (const flight  of seatsFlightsDeparture) {
          for (const seat of flight) {
            const existingBookedSeat = await Seats.findOne({
              where: {
                flight_number: seat.flightNumber,
                seat_number: seat.seatNumber,
                is_booked: true
              },
            });
        
            if (existingBookedSeat) {
              await transaction.rollback();
              return res.status(400).json({
                success: false,
                message: "Seat already booked",
              });
            }
        
            const version = seat.version;
        
            const seatBooking = await Seats.update(
              {
                is_booked: true,
                version: instanceSequelize.literal('version + 1')
              },
              {
                where: {
                  seat_number: seat.seatNumber,
                  flight_number: seat.flightNumber,
                  version
                }
              },
              transaction
            );
        
            if (!seatBooking) {
              await transaction.rollback();
              return res.status(400).json({
                success: false,
                message: "Cannot book departure seats",
              });
            }
          }
        }
    
        //Other code not useful for this problem
    
        await transaction.commit();
    
        res.status(200).send({
            success: true,
            message: "Departure booking inserted successfully",
            booking
        });
    
      } catch (error) {
        await transaction.rollback();
        console.error(error);
        res.status(500).json({
          success: false,
          message: "Can not insert booking, insert operation failed",
          error: error.message,
        });
      }
}
node.js postgresql transactions sequelize.js sql-update
1个回答
0
投票

我建议将事务正确传递给 Node.js 应用程序中的所有 Sequelize 查询,特别是

UPDATE
查询,如下所示。

const seatBooking = await Seats.update(
  {
    is_booked: true,
    version: instanceSequelize.literal('version + 1')
  },
  {
    where: {
      seat_number: seat.seatNumber,
      flight_number: seat.flightNumber,
      version
    },
    transaction: transaction // Make sure transaction is passed here
  }
);
© www.soinside.com 2019 - 2024. All rights reserved.