Does PESSIMISTIC_WRITE lock the whole table? - java

Just to be sure that I correctly understand how things work.
If I do em.lock(employee, LockModeType.PESSIMISTIC_WRITE); - will it block only this entity (employee) or the whole table Employees?
If it matters, I am talking about PostgreSQL.

It should block only the entity.
PostgreSQL hibernate dialect adds for update in case of write locks:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java#L549
(newer versions just use the same implementation)
for update is treated row-wise by PostgreSQL:
https://www.postgresql.org/docs/9.5/static/explicit-locking.html
FOR UPDATE causes the rows retrieved by the SELECT statement to be
locked as though for update. This prevents them from being locked,
modified or deleted by other transactions until the current
transaction ends. That is, other transactions that attempt UPDATE,
DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE
or SELECT FOR KEY SHARE of these rows will be blocked until the
current transaction ends; conversely, SELECT FOR UPDATE will wait for
a concurrent transaction that has run any of those commands on the
same row, and will then lock and return the updated row (or no row, if
the row was deleted).

Related

Locking table rows in a distributed application, entire table is being locked when running select..for update in mysql 5.6 [duplicate]

I have a user table with field lastusedecnumber.
I need to access and increment lastusedecnumber.
During that accessing time I need to lock that particular user row (not the entire table).
How do I do this?
The table type is MyISAM.
MySQL uses only table-level locking from MyISAM tables. If you can, switch to InnoDB for row-level locking.
Here's a link to the MySQL site describing Locks set by SQL Statements for InnoDB tables.
http://dev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html
Kind of late, but hope it will help someone:
UPDATE user SET lastusedecnumber = LAST_INSERT_ID(lastusedecnumber + 1);
SELECT LAST_INSERT_ID();
Will give you atomic increment of lastusedecnumber and ability to read new value of lastusedecnumber field (after increment) using SELECT LAST_INSERT_ID().
As a workaround you could add a column to your table, like locked TINYINT(1) - whenever you want the row to be locked you set it to 1. When you now try to access this row, the first thing you do is check if the locked fields is set.
To unlock the row, simply set it to 0 again. Not nice but a really simple workaround.
I didn't feel like converting my whole database from myisam. So I simply try to create a new table named based on the id of the record I want to lock. If create table is successful, do my work and delete the table at the end. If create table not successful, stop.
A better workaround is to create a column containting a timestamp. Whenever you want to lock the row you update it to the current time. To unlock update to a time at least x minutes in the past. Then to check if its locked check that the time stamp is at least x minutes old.
This way if the process crashes (or the user never completes their operation) the lock effectively expires after x minutes.

(UPDLOCK, ROWLOCK) locks whole table even tough only 1 row is selected

Inside our Java application we are using a SQL Server statement to pause some processes.
This is the SQL statement:
SELECT * FROM MESSAGES WITH (UPDLOCK, ROWLOCK)
WHERE MESSAGES.INTERNAL_ID IN ('6f53448f-1c47-4a58-8839-e126e81130f0');
The UUIDs in the IN clause changes of course from run to run.
This the Java code we use for locking:
entityManager.createNativeQuery(sqlString).getResultList()
The above SQL statement returns only one row. Unfortunately it seems that the whole table gets locked. The result is that all processes are locked even though none or only some should be blocked.
Why is the whole table locked even though I specify UPDLOCK?
Additional information:
MESSAGES.INTERNAL_ID is NVARCHAR(255) which is not nullable.
Otherwise there is no constraint on the column.
The isolation level is READ_COMMITTED.
This is because your MESSAGES.INTERNAL_ID is not a key. Once row is locked you cannot read it and check it's value. Try to create a primary key on this column.
If it's impossible, create INDEX on it and rewrite your query:
SELECT MESSAGES.INTERNAL_ID FROM MESSAGES WITH (UPDLOCK, ROWLOCK)
WHERE MESSAGES.INTERNAL_ID IN ('6f53448f-1c47-4a58-8839-e126e81130f0');
MSDN says:
Lock hints ROWLOCK, UPDLOCK, AND XLOCK that acquire row-level locks
may place locks on index keys rather than the actual data rows. For
example, if a table has a nonclustered index, and a SELECT statement
using a lock hint is handled by a covering index, a lock is acquired
on the index key in the covering index rather than on the data row in
the base table.

How long hibernate locks table during select?

Suppose a Criteria is created and then method Criteria.list() is called. It returns N results from table MyTalbe (there is not joins for simplicity). So how long hibernate lock records in MyTable? Does it lock all selected rows until all iterated? Or it lock rows untill session is opened (if yes when it s closed if I do not invoke close on session)?
Note
Database is MySql.
Hibernate does not lock the tables after a select. If you need to block for a particular reason you need to do explicitly.

Table level Lock in Hibernate

I use Hibernate version 4. We have a problem in batch process. Our system works as below
Select set of records which are in 'PENDING' state
Update immediately to 'IN PROGRESS' state
Process it and update to 'COMPLETED' state
The problem when we have two servers and executing at same time, we fear of having concurrency issue. So we would like to implement DB Lock for first two steps. We used query.setLockOptions(), but it seems not working. Is there any other to have table level lock or Row level lock till it completes select and update. Both are in same session.
We have options in JDBC that LOCK TABLE <TABLE_NAME> WRITE. But how do we implement in hibernate or is it possible to implement select..for update in hibernate?
"Select ... for update" is supported in Hibernate via LockMode.UPGRADE which you can set in, for example, a NamedQuery.
But using application/manual table-row locking has several drawbacks (especially when a database connection gets broken half-way a transaction) and your update-procedure can do without it:
Start transaction.
update table set state='PENDING', server_id=1 where state='IN PROGRESS';
Commit transaction
select from table where state='PENDING' and server_id=1;
[process records]
Each server must have a unique number for this to work, but it will be less error-prone and you let the DBMS do what it is supposed to be good at: isolation (see ACID).

Handling the concurrent request while persisting in oracle database?

I have this scenario ,on a airline website (using Java) two separate customers send two requests at same time to book a same seat in same airline
from New York to Chicago. I am using the oracle database and isolation level is read committed.My question here is that does oracle database provide
any solution to deal with this kind of concurrent scenario? what I know is when first transaction DML statement is fired it will get a lock on affected
rows and will release when transaction completes i.e on issuing rollback or commit.But as soon as commit is done and second request will proceed as soon as
first is completed and will override the first one.So it does not help?
Yes in Java I can deal with making my db class as singleton and using synchronized keyword on method which is doing update. But want to know is there
anyway we can this kind of issue at database level itself?Probably isolation level as serializable can help. But not sure?
It will only over write if you allow it. You can try something like
UPDATE seatTable
SET seatTaken = true
WHERE .. find the seat, flight etc.. AND seatTaken = false
This will return 1 row updated the first time and 0 rows updated after that.
As you mention, transanction settings will help you achieving one operation. The best way to enforce this kind of restrictions it to ensure that your relational model is constrained not to accept the 2nd operation once the 1st one succeeds.
Instead of having to do an update on a row, say update .... seat = "taken", create a reservation table (customer, flight, seat) which has a constrain (column:seat = unique) (lookup ora docs to learn the syntax for that on table creation). That way your reservation process becomes an insert in the reservation table and you can rely on the RDBMS to enforce your relational constrains to keep your business model valid.
e.g. Let t1 be the earlier operation time, you'll have:
t1=> insert into reservations(customer1,flight-x,seat-y) // succeeds. Customer 1 reserved the seat-y
t2=> insert into reservations(customer2,flight-x,seat-y) // fails with RDBMS unique constrain violated.
The only way to reserve seat-y again is to first remove the previous reservation, which is probably what your business process wants to achieve.
To handle concurrency in a web site a common practice it to have a column on each record that allows you to check it has not been updated since you got it. Either last update date or a sequential version number (auto incremented by a trigger).
Typically you will read the data (plus the concurrency column)
SELECT seat,etc,version_no
FROM t1
WHERE column = a_value
Then when the user eventually gets round to booking the seat the update will work unless there has been an update.
(the version number or update date will change after every update)
BEGIN
UPDATE t1
SET seatTaken = true
WHERE seatid = .....
AND version_no = p_version
RETURNING version_no INTO p_version;
EXCEPTION WHEN NOT_FOUND THEN
--Generate a custom exception
--concurrency viloation the record has been updated already
END;
the trigger to auto update the version number would look a little like this
CREATE OR REPLACE TRIGGER t1_version
AFTER INSERT OR UPDATE ON t1
FOR EACH ROW
BEGIN
IF :new.version_no IS NULL THEN
:new.version_no := 0;
ELSE
:new.version_no := :old.version_no + 1;
END IF;
END;
Aside from doing everything in a single UPDATE by carefully crafting WHERE clause, you can do this:
Transaction 1:
SELECT ... FOR UPDATE exclusively locks the row for the duration of the transaction.
Check if the returned status of the row is "booked" and exit (or retry another row) if it is.
UPDATE the row and set its "status" to "booked" - it is guaranteed nobody else updated it in the meantime.
Commit. This removes the exclusive lock.
Transaction 2:
SELECT ... FOR UPDATE blocks until Transaction 1 finishes, then exclusively locks the row.
The returned status of the row is "booked" (since Transaction 1 marked it that way), so exit (or possibly retry another row).

Categories

Resources