![]() | |
![]() |
| | Thread Tools | Display Modes |
#1
| |||
| |||
|
|
Terry Lee Tucker <terry (AT) esc1 (DOT) com> writes: I would like to be able to provide feedback to the user when they select a row for update (using SELECT FOR UPDATE). At present, if the row is being accessed (with SELECT FOR UPDATE) by another user, the application just sits there waiting. To me, this says that you're already off on the wrong foot. You don't ever want your client application holding locks while a human user edits text, drinks coffee, goes out to lunch, or whatever. A better design is to fetch the data without locking it, allow the user to edit as he sees fit, and then when he clicks "save" you do something like begin; select row for update; if [ row has not changed since you originally pulled it ] then update row with changed values; commit; else abort; notify user of conflicts let user edit new data to resolve conflicts and try again fi In this design the row lock is only held for milliseconds. You need to provide some code to let the user merge what he did with the prior changes, so that he doesn't have to start over from scratch in the failure case. What "merge" means requires some business-logic knowledge so I can't help you there, but this way you are spending your effort on something that actually helps the user, rather than just tells him he has to wait. Performance will be much better too --- long-lasting transactions are nasty for all sorts of reasons. BTW, a handy proxy for "row has not changed" is to see if its XMIN system column is still the same as before. If so, no transaction has committed an update to it. (This may or may not help much, since you're probably going to end up groveling over all the fields anyway in the "notify user" part, but it's a cool hack if you can use it.) regards, tom lane ---------------------------(end of broadcast)--------------------------- TIP 7: don't forget to increase your free space map settings |
#2
| |||
| |||
|
|
A better design is to fetch the data without locking it, allow the user to edit as he sees fit, and then when he clicks "save" you do something like begin; select row for update; if [ row has not changed since you originally pulled it ] then update row with changed values; commit; else abort; notify user of conflicts let user edit new data to resolve conflicts and try again fi In this design the row lock is only held for milliseconds. You need to provide some code to let the user merge what he did with the prior changes, so that he doesn't have to start over from scratch in the failure case. BTW, a handy proxy for "row has not changed" is to see if its XMIN system column is still the same as before. If so, no transaction has committed an update to it. (This may or may not help much, since you're probably going to end up groveling over all the fields anyway in the "notify user" part, but it's a cool hack if you can use it.) |
#3
| |||
| |||
|
|
BTW, a handy proxy for "row has not changed" is to see if its XMIN system column is still the same as before. Considering that my business objects remember XMIN from when |
#4
| |||
| |||
|
|
Just so that I am not getting this wrong: BTW, a handy proxy for "row has not changed" is to see if its XMIN system column is still the same as before. Considering that my business objects remember XMIN from when they first got the row would the following sequence make sure I am in good shape ? begin; select ... for update; update ... set ... where my_pk=<my_pk_value AND xmin=<the_old_xmin This should either update 1 row in which case I can commit or zero rows in which case I need to rollback and handle the merge conflict. The reasoning would be that the condition my_pk=my_pk_value would select the row I am interested in while xmin=the_old_xmin would ensure that row hasn't been modified. Am I right or is there a flaw in my thinking ? |
#5
| |||
| |||
|
|
begin; select ... for update; update ... set ... where my_pk=<my_pk_value AND xmin=<the_old_xmin I think you can skip the SELECT FOR UPDATE altogether if you do it that way. Otherwise it looks fine. Except that there will be other clients accessing those rows, |
#6
| |||
| |||
|
|
The docs say that XMIN is the transaction ID of the *inserting* transaction for this row version. IOW updates will change XMIN. Will XMIN also be changed by a *deleting* transaction ? I guess it depends on how deletion is handled: Either the *unchanged* row version is marked as deleted (hence XMIN would not change) OR a new row version is created and marked deleted (which would indeed change xmin). |
|
IOW, can I also detect my row being *deleted* from under me by another transation by way of checking XMIN ? Else I would likely need to check XMAX, too. |
|
Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a tool for doing 5% of the work and then sitting around waiting for someone else to do the other 95% so you can sue them. |
#7
| |||
| |||
|
|
You need to look at it (XMIN) in conjunction with XMAX. A newly insert row has XMIN set and XMAX null. When a row is updated the XMAX of the old row is set and a new row is created with an XMIN. When you delete a row it just sets the XMAX. But, as you say below, it also "disappears"... :-) |
|
IOW, can I also detect my row being *deleted* from under me by another transaction by way of checking XMIN ? Else I would likely need to check XMAX, too. Easy, look for it. If you can't find it, it got deleted... Doh, of course you are right. I was thinking of doing this: |
![]() |
| Thread Tools | |
| Display Modes | |
| |