Index range locks are more performant than Predicate Locks
Predicate locks can completely eliminate write skews bringing true serializability to databases. Unfortunately, they do not perform well because every query needs to be checked against existing locks. If there are many locks by active transactions, this check becomes time-consuming.
Most databases implement index-range locking (also known as next-key locking), a simplified approximation to predicate locking.
Index-range locking works on the premise that it is safe to simplify a predicate to match a wider set of objects. In the booking room example, instead of checking for bookings for a particular room in a specific timeslot, you can approximate it by locking all bookings to the room at any time or locking all rooms for the timeslot.
The database attaches an approximation of the search condition to one of the indexes - either the room index or the time-based index depending on whichever is in use. When another transaction wants to insert, update, or delete a booking for the room and timeslot, it will need to update the same part of the index. Consequently, it will encounter the shared lock, and it will be forced to wait until the lock is released.
While index range locks are approximations and not precise, they have much lower overheads, so they are a good compromise.
It is essential to understand that the database can fall back to a shared lock on the entire table in the absence of suitable indexes. It is a safe fallback position, but it is not ideal for performance since it will stop all other transactions from writing to the table.