views:

191

answers:

4

What could possibly go wrong with the following transaction if executed by concurrent users in the default isolation level of READ COMMITTED?

BEGIN TRANSACTION

SELECT * FROM t WHERE pid = 10 and r between 40 and 60
-- ... this returns tid = 1, 3, 5
-- ... process returned data ...
DELETE FROM t WHERE tid in (1, 3, 5)
INSERT INTO t (tid, pid, r) VALUES (77, 10, 35)
INSERT INTO t (tid, pid, r) VALUES (78, 10, 37)
INSERT INTO t (tid, pid, r) VALUES (79, 10, 39)

COMMIT
A: 

You really should mention if you're using oracle or postgres. Also, you should explicitly specify your locking and not rely on default behavior. They may change with other databases or database versions.

Jay
I'm using Oracle as the DB
Bradford
+1  A: 

Thew whole thing is strange to me. What is the purpose of the select? It accomplishes nothing. Write a delete to select the records you want. What would strike me a problem of concurrent users is that they will be trying to insert the same records since you hard-coded the values and thus would probably run into the unique constraints that you probably have on tid or the tid, pid combo.

Honestly what are you trying to accomplish here? This looks like an ad hoc query that was meant for one-time usage that you are trying to run multiple times. It is almost always a bad idea to hard-code like this.

HLGEM
The select is there because the insert statements, which aren't really hard-coded like that, depend on the data returned from the select. Here's what's going on: I have a table with tid, product_id, start_date, end_date, and discount. When a user inserts a date range and discount for a product, I select all overlapping dates, delete these, and then insert the new records according to some algorithm that depends on the records returned in the overlap select. I just want to prevent overlaps from occuring in the DB w/o using an INSERT AFTER trigger.
Bradford
+2  A: 

You could having serious performance problems from dead locks

The SELECT will obtain a shared lock on a page and then the DELETE would attempt to upgrade those locks to exclusive locks.

If another user was executing the same query, it might obtain shared locks on the same page at the same time another user does. Then when one tries to upgrade to an exclusive lock, it will wait for all other shared locks to be released. The other will also be waiting for all shared locks to be released. Both will have a shared lock and waiting for the other to release that shared lock so itself can obtain an exclusive lock. Other queries will pile up trying to do the same, and soon the deadlocks will begin to be detected and the queries will begin to get cancelled and rolled back. Depending on the frequency of the queries the dead lock detection of the DB engine may not be killing off queries as fast as new ones are coming in, meaning none of the queries will succeed.

You would need to add something like a hint in the select to request that an exclusive lock be obtained from the get-go. Or you could move the select outside of the transaction and use concurrency conflict detection in your other statement's where criteria.

AaronLS
Thanks. This is the kind of information I was looking for.
Bradford
A: 

You don't use a lock for the SELECT, so everybody will get the same results, everybody will see records tid 1, 3 and 5. Everybody will process these records and everybody will try to delete these records. And that's not going to work, the delete operation will place a lock. Only one transaction can lock these records, all other transactions have to wait for commit of the first transaction. This transaction will insert the new records and commit, all others will delete nothing (can't find records, no problem) and insert the new records as well. These records have the same data, is that a problem?

Maybe you want a SELECT ... FROM ... FOR UPDATE; to lock the records you want to process. http://www.postgresql.org/docs/8.4/interactive/sql-select.html

Frank Heikens
The INSERT INTO statements are an example of what will be inserted. They will be dynamic, dependent on tid and pid -- same as the delete. Basically, I want everything to fail if another transaction happens to operate on the same tid and pid at the same time. So, I guess I want to "lock" at the time of the select, but only on the rows where pid = 10, so that no other transactions will be removing or inserting data for this criteria at the same time. I just don't understand locking. I have high reads a low number of writes. If poss. I don't want a read to hang or fail because of an insert.
Bradford
I meant, I don't won't a normal select statement outside of these transactions to fail because this type of transaction is running.
Bradford