Question

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
Was it helpful?

Solution

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.

OTHER TIPS

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.

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top