I have an environment with (for simplicity) two tables A and B. Table B has a foreign key constraint on Table A's primary key. I now have several forked processes each performing a transaction like the following:
- Load data into local memory (lets say user input)
- Select all rows (~1000) from table A
- Compute both set differences of received data and data from local memory as well as set intersection with local memory.
- Delete references from table B if corresponding row from table A is not present in local memory
- Delete rows from table A if they are no longer referenced from table B and not present in local memory
- Update rows from tables A and B present in memory with data from memory
- Insert new rows into tables A and B for data only present locally
As mentioned table A has roughly 1000 rows and table B 5000. Each transaction touches around 30% of the entries.
The reason I calculate the set differences myself is that I cannot use a MERGE or UPSERT since I don't know which DBMS I will be talking to (abstracted away by company's DB layer), Oracle or PostreSQL.
The problem I see when two such transactions run in parallel is:
- Both read the current state, see that an entry with primary key X does not yet exist in Table A and both try to insert it. Who ever commits last will fail.
- Both read the current state and delete a reference from Table B leaving a row in Table A without any reference left. Thus it would need to be deleted. But since both transactions still see the reference of the other the row in Table A would never be deleted, leaving a dead row.
- Both read the current state. Transaction 1 sees a row in Table A without any reference and deletes it. Transaction 2 sees the same row as already present and adds just a reference to it. Transaction 1 commits. Commit of transaction2 fails due to FK violation.
I think the only isolation level that would help me is SERIALIZABLE but I cannot handle any serialization error since the company's DB layer does not provide proper error information on why a transaction failed. But I don't see that any table locking would help me because my problem is the (inconsistent) read of Table A at the beginning of each transaction and reads are never blocked.
How can I best solve my problem?