Jeff Davis
UNIQUE indexes have long held a unique position among constraints: they are the only way to express a constraint that two tuples in a table conflict without resorting to triggers and locks (which severely impact performance). But what if you want to impose the constraint that one person can't be in two places at the same time? In other words, you have a schedule, and you want to be sure that two periods of time for the same person do not overlap. This is nearly impossible to do efficiently with the current version of PostgreSQL -- and most other database systems. I will be presenting Exclusion Constraints, which has been accepted for the next PostgreSQL release, along with the PERIOD data type (available now from PgFoundry). I will show how these can, together, offer a fast, scalable, and highly concurrent solution to a very common business requirement. A business requirement is still a requirement even if your current database system can't do it!
2. Schedule Conflict
● One person can't be in two places at the
same time
● Two people can't use an exclusive
resource at the same time
● Business constraint
● Just one instance of an interesting class
of problems
2
3. PERIOD Type
● Need a concept of a period of time,
otherwise the system doesn't know what
a “schedule conflict” is
● PERIOD data type:
– http://pgfoundry.org/projects/temporal
● Definite beginning and end time, e.g., the
period of time during which a professor
is teaching in a classroom
3
4. Constraints
● Foreign Key
– Requires another tuple to exist
● CHECK Constraint
– Only looks inside one tuple, can't compare
two tuples
– You can subvert the system by using a
UDF
● Race conditions
4
5. Why is UNIQUE so unique?
● Only constraint where two tuples can
conflict with eachother
– If x = 5 in one tuple, there can be no other
tuple where x = 5
● Effectively a predicate lock on a very
simple predicate
– Transactions wait on other transactions
trying to insert the same value
● Special code path inside Btree to enforce
uniqueness (doesn't work for GiST, etc.) 5
6. Almost...
● UNIQUE is almost what we need, but not
quite.
● Need something that can understand
“overlaps”
6
7. What's the Point?
● Very common business constraint known
as a “schedule conflict”
● Claims:
– Existing solutions in SQL are bad...
7
8. What's the Point?
● Very common business constraint known
as a “schedule conflict”
● Claims:
– Existing solutions in SQL are bad...
– ...except Exclusion Constraints in
PostgreSQL 9.0.
8
9. NonOverlapping Constraint
● Let's look at existing solutions for
schedule conflicts
● How do you specify a nonoverlapping
constraint in an SQL system?
– Any SQL system?
● Ideas?
9
10. Idea 1: Serialize
● Only one writer
– Exclusive lock
● Before updating any reservation, search
all existing reservations for conflicts
● Horrible performance, availability
problems
10
11. Idea 2: Quantize
● Break into time slices, e.g. 1 hour.
● Use UNIQUE constraint on beginning
● Imposes unnecessary business constraint
– Nobody can reserve 1:30pm – 2:30pm
● Code is not reusable for other businesses
– Hotels reserve by day
● Not useful when quantum is too small
– Security, scientific apps, audit logs, etc.
11
12. Idea 3: Procedural Code
● Triggers
● Perhaps use dummy rows that exist only
for rowlevel locks
● Perhaps application code
● Probably will not perform well
● Very businessspecific, not reusable
● Error prone
● Good luck...
12
13. Idea 4: Delayed Check
● Record timestamp when reservation was
recorded
● Make extra process check for conflicts
and notify victims asynchronously
● Unhappy customers
● Adds uncertainty after “commit”
● Cascading problem
13
14. Back to the Example
● If the constraint is not enforced by the
DBMS...
● ...then it will be enforced when two
professors each believe they have
reserved the same room
● A duel?
● Probably a less desirable constraint
enforcement mechanism than a friendly
error from the DBMS
14
15. Exclusion Constraints
● New feature in 9.0
● Not constraint_exclusion!
● Offers constraint enforcement mechanism
more general than UNIQUE
● Declarative
● Scalable
– Performance comparable to UNIQUE
15
20. That was Easy
● 3 lines
● Scalable
● Flexible
● Declarative
● Robust
● Safe during concurrent INSERTs and
UPDATEs
20
21. Operator Detects Conflicts
● The operator is used to search for
conflicts
● Should return TRUE when two values
conflict
● For example, the “overlaps” operator
(“&&”) would be used to enforce the
constraint that no two tuples contain
overlapping values
21
22. Back to UNIQUE
● If you specify all operators as “=”, the
semantics are identical to UNIQUE
● Performance slightly worse, because one
additional index search is required
● Can be used with GiST or Hash indexes
22
23. UNUNIQUE
● If you specify the operator as “<>”, then
constraint is the opposite of UNIQUE: all
values must be the same!
● Use case: At the zoo, if you've already put
zebras in the cage, you can put more
zebras in but don't put lions in.
23
26. Extra Capabilities
● Support for predicates (WHERE)
● Constraint on a subset of the table
● Support for arbitrary expressions:
... EXCLUDE ((t::circle)
WITH &&) ...
● Deferrable
● Doesn't work with GIN, yet.
26
27. Conclusion
● Constraints are always enforced
● Sometimes by the DBMS (cheap),
sometimes by real life (expensive)
● The very simple, very common “schedule
conflict” constraint is almost impossible
to enforce with most DBMSs
● Let's make it easy, scalable, and flexible.
● “Exclusion Constraints” in 9.0!
27