I am inserting rows in a table, based on a value and on the existence of a row, which I get from SELECT
statements. This function can be run concurrently, so I need to do some extra obviously. The basic steps are (pseudocode)
function doStuff(int someID){
found = `SELECT someIDFROM targettable`
if(found){
/*we have done this allready, so skip.*/
}else{
value =' SELECT value FROM statetable`
//insert value;
`INSERT INTO targettable
VALUES (someID,value)'
}
}
The problem is ofcourse that if process1 reads uniqueID, figures it should insert the value, and process2 does the same before the write is finished, I get 2 lines in my targettable
. This is possible (someID is not unique for that table), but in this case unwanted because it was done on wrong assumptions because of the concurrency.
Now my assumption/feeling is that a transaction alone is not enough for this. The second process will have consistent reads I think, but that does not help prevent the second INSERT
, does it? so this will not do (I think, please advice me on this. This could be where I'm going wrong). (I'm quite sure at the moment this is not enough to ensure correct behaviour, because of testing)
function doStuff(int someID){
'START TRANSACTION HERE'
found = `SELECT someIDFROM targettable where someid = someID `
if(found){
/*we have done this allready, so skip.*/
}else{
value =' SELECT value FROM statetable`
//insert value;
`INSERT INTO targettable
VALUES (someID,value)'
}
COMMIT TRANSACTION
}
From what I read about InnoDB locking reads it could be that I should use those, but I'm not sure as I have never worked with them. It could also be the transaction is enough, and I'm just begin foolish.
My current idea is to use SELECT someID FROM targettable FOR UPDATE
, which I figure will give me a lock. But I'm not sure what lock, since it depends on isolation level. This would look like so:
function doStuff(int someID){
found = `SELECT someIDFROM targettable where someid = someID FOR UPDATE`
if(found){
/*we have done this allready, so skip.*/
}else{
value =' SELECT value FROM statetable`
//insert value;
`INSERT INTO targettable
VALUES (someID,value)'
}
}
I'm not sure how to cope with the value
I get from another table, but that might be another thing.
Summarizing: Can I get away with just using a transaction, do I need to use a locking-read (if so, how?) or is there another option I missed?
Update: If I do something like
START TRANSACTION;
SELECT * FROM targettable WHERE someid = 3 FOR UPDATE
And query that select
in another thread, I get no result (locked?) until this transaction is finished. But if I put an INDEX
on that someid, I DO! I'm probably missing something, but I wasn't expecting that.
How can I be sure I don't insert this row twice?