Re: Need help writing exclusion constraint - Mailing list pgsql-general

From Jeff Davis
Subject Re: Need help writing exclusion constraint
Date
Msg-id 1295459395.11513.83.camel@jdavis
Whole thread Raw
In response to Re: Need help writing exclusion constraint  (Daniel Popowich <danielpopowich@gmail.com>)
List pgsql-general
On Wed, 2011-01-19 at 10:15 -0500, Daniel Popowich wrote:
> Anyway...Jeff, all your answers depend on using new features in 9.0.
> What would you recommend for folk still using 8.4?  Without 9.0
> exclusion constraints, what else can you do besides using functions in
> check constraints (or triggers) with appropriate locking (at some
> level of the overall application architecture).

There are several approaches, but all of them leave something to be
desired, of course.

I break the alternative solutions into 4 categories:

1. Full table lock -- instead of using a CHECK constraint, use a trigger
and acquire a full table lock. The obvious problem here is the
contention over that lock, so transactions will need to be kept short.
Performance with this approach will not be very good, but perhaps that's
OK in some situations.

2. What I call "quantization". That is, choose a size, say one hour, and
assume that "1:00" really means "1:00 - 2:00". Then you can use a UNIQUE
index. You have to align everything on the hour exactly (you can't do
1:30-2:30, for instance), and longer reservations require multiple
entries. Choosing an appropriate chunk size is difficult, because if
it's too big then it makes the application overly strict (and imposes
inconveniences on your organization); but if you choose a size that is
too small, it requires many entries for a single reservation (if you
choose one minute, then a one-hour reservation requires 60 rows). These
drawbacks are acceptable for some organizations, but not all.

3. Complex procedural code can be used. For instance, you might have a
separate table (call it "my_locks") of rows that exist just for
row-level locks. One row would represent 1:00 - 2:00, another 2:00 -
3:00, etc. And when you go to insert into the main table (call it
"reservations"), you take a row-level lock on every row in my_locks that
overlaps with the time you are inserting. So, if you are inserting 10:30
- 11:30 into the reservations table, you would take a lock on the rows
10:00 - 11:00 and 11:00 - 12:00 in your my_locks table. This effectively
partitions your lock space, so that a reservation for 1:30 - 2:30 won't
have to wait for a reservation between 10:30 and 11:30. There are other
ideas along these lines as well, this is just an example of how adding
complexity can help. Be careful though, the complexity explodes pretty
quickly, and there are a lot of hidden performance traps.

4. You can work outside of the transactional system. Record both
reservations, and check later for conflicts. The problem here is what to
do when you find one. If you want to undo one reservation, and it was
part of a larger transaction, you have to figure out how to undo the
whole transaction. And you need to keep a log of which transactions need
to be checked, so that if there is a crash you don't lose track and
leave the conflicting reservations in there.

As you can see, none of these are ideal. But, if you run into a specific
problem, you can usually pick one of these approaches and make it work
with careful determination. Exclusion constraints are much easier,
however ;)

Regards,
    Jeff Davis


pgsql-general by date:

Previous
From: Ahmed Ossama
Date:
Subject: Re: Error during a dump (ts_selectivity, not found)
Next
From: Tom Lane
Date:
Subject: Re: Some problem with the NOTIFY/LISTEN