title: Handling simultaneous reservation requests in restaurant scenario
body: <p>I am working on a REST API project for a restaurant reservation system with Spring boot and HSQLDB using Spring data JPA. The scenario is users can call booking service to book a table at a particular time. Only one booking must be allowed at a given time slot.</p>
  
  <p>My question is how do i handle the scenario where the REST API is called simultaneously by 2 or more different users to book the same table at the same time.</p>
  
  <p>I searched through this and found optimistic locking can be used for this but i am not sure how it would help in the scenario unless I make any updates to Restaurant Table record.</p>
  
  <p>Below is my project structure.</p>
  
  <p>Booking DAO</p>
  
  <pre><code>@Entity
  public class Booking {
  
      @Id
      @GeneratedValue
      private Long id;
      private String bookingId;
      @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
      private LocalDateTime bookingStart;
      @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
      private LocalDateTime bookingEnd;
      @OneToOne
      private RestaurantTable table;
      private String customerName;
  
      ..Setter/Getter
  }
  </code></pre>
  
  <p>Restaurant DAO</p>
  
  <pre><code>@Entity
  public class RestaurantTable {
  
      @Id
      @GeneratedValue
      private Long id;
      private Integer tableId;
      private Integer capacity;
      private String description;
  
      //Setter/Getter
  }
  </code></pre>
  
  <p>Booking Create service.</p>
  
  <pre><code>@Service
  public class BookingService{
  
      @Transactional
      public Booking addBooking(final BookingDTO bookingRequest) {
          LOG.debug("Creating new booking for request: {}", bookingRequest);
          validateBookingData(bookingRequest);
          return bookingCreateService.createBooking(bookingRequest);
      }
  }
  
  @Service
  public class BookingCreateService {
  
      @Autowired
      private BookingRepository bookingRepository;
      @Autowired
      private TableRepository tableRepository;
  
      public Booking createBooking(final BookingDTO bookingRequest) {
          Booking booking = null;
          final LocalDateTime bookingStart = bookingRequest.getBookingTime();
          final LocalDateTime bookingEnd = bookingRequest.getBookingTime().plusHours(1);
          RestaurantTable table = tableRepository
                  .findAvailableTableWithAdequateSeatingCapacity(bookingStart, bookingEnd, bookingRequest.getCustomers())
                  .stream().findFirst().orElse(null);
  
          if (table != null) {
              booking = saveBooking(table, bookingStart, bookingEnd, bookingRequest.getCustomerName());
          } else {
              throwErrorWithRecomemdedTime(bookingRequest.getCustomers(), bookingStart);
          }
          return booking;
      }
  
      private Booking saveBooking(final RestaurantTable table, final LocalDateTime bookingStart,
              final LocalDateTime bookingEnd, final String customerName) {
          Booking booking = new Booking();
          booking.setBookingStart(bookingStart);
          booking.setBookingEnd(bookingEnd);
          booking.setTable(table);
          booking.setBookingId(RandomStringUtils.randomAlphanumeric(7));
          booking.setCustomerName(customerName);
          return bookingRepository.save(booking);
      }
  
      private void throwErrorWithRecomemdedTime(final Integer customerCount, final LocalDateTime bookingStart) {
          Booking closestBookingWithFreeSlot = bookingRepository.findClosestBookingToInputTime(bookingStart);
          StringBuilder errorMsg = new StringBuilder();
          errorMsg.append("Table not available for capacity: ").append(customerCount).append(".");
          if (closestBookingWithFreeSlot != null) {
              errorMsg.append(" Table available after " + closestBookingWithFreeSlot.getBookingEnd() + ".");
          }
          throw new DataValidationException(errorMsg.toString());
      }
  }
  </code></pre>
  
  <p>If i hold the <code>BookingService</code> from completion using <code>Thread.sleep</code> and create another requets for booking same table, it would create two bookings for same table with same time. How can I avoid this? Should I make this synchronized? Will synchronised cause performance issue?</p>