Reputation: 181
I have a database table that logs the changes occur in other tables. table structure for my log table is as following.
Log_Table(id, table_name, operation, flag)
values (1, Customer_Table, 1, 1);
values (2, Customer_Table, 2, 1);
values (3, Customer_Table, 1, 1);
values (4, Customer_Table, 2, 1);
values (5, Customer_Table, 1, 1);
I update this table against a button on a web page by doing following operations:
/* first */
public List<Long> select_Changes()
{
select id from Log_Table where flag =1;
}
/* (wait for user input) */
/* second */
public void update_table(List<Long> ids)
{
update Log_Table set flag =0 where id in( ids)
}
Problem is that between first and second operation its up to the user to perform the operation. Meanwhile another user at same time does the same operations. I don't want rows already selected by the first user to be selected by the second user; that is, when the second user runs the first step (assuming two more rows have been added since the first user ran it), the result should be:
values(6,Customer,2,1);
values(7,Customer,1,1);
Please suggest what I should do? I need to lock the rows for any kind of operation after rows are get selected. I tried select for update clause but it did not solve the problem. It's in a web application.
Upvotes: 1
Views: 105
Reputation: 19471
It is almost never a good idea to hold a database transaction open while waiting for user input. What if the user goes to lunch while the transaction is pending, or their network connection goes down and isn't restored for days?
Also, you don't say what database product you are using. Depending on the product, its configuration, and the transaction isolation level, the results of the concurrent attempt what the transaction is pending, so if you want portable behavior you can't rely on the behavior of SELECT FOR UPDATE
or even more standardized features.
My recommendation would be to provide a way in the row to identify pending rows, which are waiting for user confirmation. You could use three states for the flag
column, to represent something like available
, pending
, and taken
; however, you probably want to have some way to recognize rows which were presented to a user but for which the user never clicked "OK" or "Cancel" (or whatever the options are). If you add a timestamp column for that purpose, you could stay with two states for the flag
column and use something like this (assuming that you are using databases which support the RETURNING
clause) for the first step:
public List<Long> select_Changes()
{
UPDATE Log_Table
SET when_presented = CURRENT_TIMESTAMP
WHERE flag = 1
AND when_presented = NULL
RETURNING id, when_presented;
}
The second step would be changed to:
public void update_table(List<Long> ids)
{
UPDATE Log_Table
SET flag = 0
WHERE id IN (ids)
AND when_presented = time_claimed;
}
The second step would not necessarily need to change, but with the change above, you could use another RETURNING
clause to confirm which id
values this user actually claimed, closing a race condition otherwise present if a maintenance process set when_presented
back to NULL
on an apparently abandoned set of rows which were then presented to another user right before the first user belatedly tried to claim them.
Upvotes: 1
Reputation: 181
I have added a timestamps column and an extra table with the structure
Last_Oeration_Time(id number, last_op_time timestamp(6))
When the first user clicks the button i run an sql insert query on Last_Oeration_Time like insert int Last_Oeration_Time(id,last_op_time) values(seq_lasst_op_time_id.nextval,sysdate)
now when the second user runs the first step (assuming two more rows have been added since the first user ran it), the result is desired result. is this ok?
Upvotes: 0