Skip to main content
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Incorporated comments
Source Link
Paul White
  • 90.2k
  • 30
  • 423
  • 663

Suppose I have a system for reserving vacation properties. One of the tables is reservations. Each reservation has a property_id, a start_date, and an end_date.

I want to prevent conflicting reservations. Eg, if a property is reserved from June 15-20, nobody can reserve it on June 17, or from June 19-22, or any other overlapping period.

I can see two ways to guarantee this:

Option 1: Lock the table for writes, run a SELECT and check the existing reservations in application code, and if it will not create a conflict, INSERT a reservation or UPDATE the dates of an existing one.

Option 2: Use an EXCLUDE constraint.

ALTER TABLE reservations ADD CONSTRAINT no_overlapping_rentals
EXCLUDE USING gist (property_id WITH =,
daterange("start_date", "end_date", '[]') WITH &&);

I'm fairly sure that the second option is more performant overall, because PostgreSQL doesn't have to wait while the application code examines the existing reservations.

But at the database level, what's the performance difference between these two options? And are there any other factors I should consider?


There actually is a third way - use transactions with serializable isolation, and within the transactions, SELECT for conflicting reservations, and if you find none, INSERT your new one. But 1) that also requires the db waiting for the application code to check the results and 2) it can give false positives - see https://gist.github.com/nathanl/f98450014f62dcaf0405394a0955e18e for a simple demo (about uniqueness, not overlap, but the same concept applies)

Oh, and another thing about serializable isolation - I think the fact that you have to retry on a false positive means that the time to insert becomes unbounded, as you may have to keep getting in the back of the retry line.

Suppose I have a system for reserving vacation properties. One of the tables is reservations. Each reservation has a property_id, a start_date, and an end_date.

I want to prevent conflicting reservations. Eg, if a property is reserved from June 15-20, nobody can reserve it on June 17, or from June 19-22, or any other overlapping period.

I can see two ways to guarantee this:

Option 1: Lock the table for writes, run a SELECT and check the existing reservations in application code, and if it will not create a conflict, INSERT a reservation or UPDATE the dates of an existing one.

Option 2: Use an EXCLUDE constraint.

ALTER TABLE reservations ADD CONSTRAINT no_overlapping_rentals
EXCLUDE USING gist (property_id WITH =,
daterange("start_date", "end_date", '[]') WITH &&);

I'm fairly sure that the second option is more performant overall, because PostgreSQL doesn't have to wait while the application code examines the existing reservations.

But at the database level, what's the performance difference between these two options? And are there any other factors I should consider?

Suppose I have a system for reserving vacation properties. One of the tables is reservations. Each reservation has a property_id, a start_date, and an end_date.

I want to prevent conflicting reservations. Eg, if a property is reserved from June 15-20, nobody can reserve it on June 17, or from June 19-22, or any other overlapping period.

I can see two ways to guarantee this:

Option 1: Lock the table for writes, run a SELECT and check the existing reservations in application code, and if it will not create a conflict, INSERT a reservation or UPDATE the dates of an existing one.

Option 2: Use an EXCLUDE constraint.

ALTER TABLE reservations ADD CONSTRAINT no_overlapping_rentals
EXCLUDE USING gist (property_id WITH =,
daterange("start_date", "end_date", '[]') WITH &&);

I'm fairly sure that the second option is more performant overall, because PostgreSQL doesn't have to wait while the application code examines the existing reservations.

But at the database level, what's the performance difference between these two options? And are there any other factors I should consider?


There actually is a third way - use transactions with serializable isolation, and within the transactions, SELECT for conflicting reservations, and if you find none, INSERT your new one. But 1) that also requires the db waiting for the application code to check the results and 2) it can give false positives - see https://gist.github.com/nathanl/f98450014f62dcaf0405394a0955e18e for a simple demo (about uniqueness, not overlap, but the same concept applies)

Oh, and another thing about serializable isolation - I think the fact that you have to retry on a false positive means that the time to insert becomes unbounded, as you may have to keep getting in the back of the retry line.

Source Link
Nathan Long
  • 945
  • 2
  • 10
  • 21

Performance of table locks vs exclude constraints

Suppose I have a system for reserving vacation properties. One of the tables is reservations. Each reservation has a property_id, a start_date, and an end_date.

I want to prevent conflicting reservations. Eg, if a property is reserved from June 15-20, nobody can reserve it on June 17, or from June 19-22, or any other overlapping period.

I can see two ways to guarantee this:

Option 1: Lock the table for writes, run a SELECT and check the existing reservations in application code, and if it will not create a conflict, INSERT a reservation or UPDATE the dates of an existing one.

Option 2: Use an EXCLUDE constraint.

ALTER TABLE reservations ADD CONSTRAINT no_overlapping_rentals
EXCLUDE USING gist (property_id WITH =,
daterange("start_date", "end_date", '[]') WITH &&);

I'm fairly sure that the second option is more performant overall, because PostgreSQL doesn't have to wait while the application code examines the existing reservations.

But at the database level, what's the performance difference between these two options? And are there any other factors I should consider?