简体   繁体   中英

How to handle simultaneous save API requests in laravel?

I am trying to save new booking through API. Below is the sample code of save booking function.

public function save_booking(Request $request){
        $booking = new Booking;
        $booking->booking_number            = generate_booking_id();
        $booking->booking_type              = $request->input('bookingType');
        $booking->booking_date              = $request->input('bookingDate');
        $booking->booking_time              = $request->input('bookingTime');
        $booking->booking_status            = $request->input('bookingStatus');
        $booking->save();
}

Note - generate_booking_id() is a helper function to generate booking like BN-202105-0001 (Last four digits 0001 are calculated by adding total records count + 1 from booking table).

When two or more simultaneous save API requests trying to save new booking, same booking number is assigned to both requests. (Eg Request 1 = BN-202105-0001, Request 2 = BN-202105-0001 and so on.)

I want to assign different booking number to each of simultaneous API request. (Eg There are 4 simultaneous request trying to save new booking then for Request 1 = BN-202105-0001, Request 2 = BN-202105-0002, Request 3 = BN-202105-0003, Request 4 = BN-202105-0004)

How do I solve this concurrent request problem in Laravel?

Thanks in advance!

This is happening because your generate_booking_id() is reading the db simultaneously from two different requests and generating the booking id for you.

You can avoid this, by creating a ON INSERT trigger at the db level like the following to the update the booking_number for you instead of you manually doing it. The trigger will make sure to automatically update the booking_number after the booking has been created and not before it.

Assuming you are using MySQL: (Nevertheless, you could use the same approach in other database management systems)

CREATE TRIGGER 'bookings'.'update_booking_number'
AFTER INSERT ON 'bookings' FOR EACH ROW
BEGIN
    UPDATE bookings b
      SET booking_number = (SELECT CONCAT('BN-202105-000',COUNT(*) + 1) FROM bookings) 
      WHERE b.id = NEW.id;
END;

Alternate solution could be to insert the booking record first into the db and then call the generate_booking_id() function to calculate the booking_number correctly and then update the record into the db:

 public function save_booking(Request $request){
            $booking = new Booking;
            $booking->booking_type              = $request->input('bookingType');
            $booking->booking_date              = $request->input('bookingDate');
            $booking->booking_time              = $request->input('bookingTime');
            $booking->booking_status            = $request->input('bookingStatus');
            $booking->save();

            $booking->booking_number            = generate_booking_id();
            $booking->save();
            
    }

For the sake of simplicity, you could try making the booking_number a property of your model. This property then combines a given prefix with the actual id of the booking. That way you do not need to create your own incremental function, and just assemble what you already got.

Example of the property:

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Booking extends Model
{
    use HasFactory;

    function getBookingNumberAttribute() {
        $date = Carbon::createFromFormat('Y-m-d', $this->attributes['booking_date']);
        $year = $date->year;
        $month = $date->format('m');
        $fourDigitId = sprintf('%04d', $this->attributes['id']);
        return "BN$year$month-$fourDigitId";
   }

    static function findByBookingNumber($booking_number) {
        $splitBookingNumber = explode('-', $booking_number);
        return self::where('id', intval($splitBookingNumber[1]));
   }
}

The function getBookingNumberAttribute functions as an accessor for the property: booking_number , so when you run:

$now = Carbon::now();
$booking = Booking::create([
    'booking_date' => $now->format('Y-m-d'),
    'booking_time' => $now->format('H:i:s'),
    'booking_status' => 'new',
]);
dump($booking->booking_number);

You should always get a new ID, as the logic for creating the unique id is handled by the mysql database.

I've also added a method to show how to find a booking by its booking number.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM