JPA LockModeType.PESSIMISTIC_WRITE doesn't work as expected. App without keys, reads snapshot of database at locking

心已入冬 提交于 2019-12-13 02:40:03

问题


I have two apps running identical code, querying the same Oracle database with the help of hibernate.
There is a table that keeps emails to be sent.
Both apps run a scheduler that query a column on this table for "QUEUED" string (which marks emails to be sent), create and send emails and at the end, update, "QUEUED" to "SENT" so these emails won't be sent again.
In theory, I want one app to read some rows, lock them from reading and writing, update them and unlocking for the other app to use.
For this reason I am using below query:

String jpql = "SELECT m FROM Email m WHERE m.status = :status";
return em.createQuery(jpql, Email.class)
        .setParameter("status", "QUEUED")
        .setLockMode(LockModeType.PESSIMISTIC_WRITE)
        .getResultList();  

which is used by both apps and according to documents, LockModeType.PESSIMISTIC_WRITE is the equivalent of "SELECT FOR UPDATE".
Instead, below behavior happens:

13:06:02,160 | MailQueueMonitor_1| Found 0 email(s) to be sent. // No rows returned from app1  

13:06:03,813 | MailQueueMonitor_2| Found 0 email(s) to be sent. // No rows returned from app2  

13:06:12,180 | MailQueueMonitor_1| Found 1 email(s) to be sent. // 1 mail returned from app1  

13:06:12,190 | MailQueueMonitor_1| Mailer will sleep for 30s    // App1 will sleep for 30s   

// At this point, app2 tries to execute query but freezes as app1 has the keys to the rows  

13:06:42,191 | MailQueueMonitor_1| Mailer woke up and will try to send mails    // App1 wakes up  

13:06:46,796 | MailQueueMonitor_1| Mailer sent mail     // App1 sent mail  

13:06:46,798 | MailQueueMonitor_1| Mailer changed mail status to SENT // App1 update status from QUEUED to SENT  

// At this point, app1 releases the locks and app2 unfreezes and executes query looking for QUEUED rows which should not exist at this point since they where updated to SENT.  

13:06:46,809 | MailQueueMonitor_2| Found 1 email(s) to be sent. // App2 queries and finds 1 row! It is like it queried a snapshot of the database before app2 updated all rows.  

13:06:46,836 | MailQueueMonitor_2| Mailer will sleep for 30s    // App2 will sleep for 30s  

13:07:16,836 | MailQueueMonitor_2| Mailer woke up and will try to send mails    // App2 wakes up  

13:07:21,457 | MailQueueMonitor_2| Mailer sent mails    // App2 sent mail. This is re-senting above email occuring to duplicate emails.  

13:07:21,458 | MailQueueMonitor_2| Mailer changed mail status SENT  // App2 update status from QUEUED to SENT, again!

The questions are, why doesn't app2 read updated rows even though, query is executed after lock release. Why isn't an exception thrown when app2 tries to query locked rows? How should I lock the rows from being read or updated and after lock released, the next app which will query the db will see the updated data?

Some notes:
1. If I run the query twice on two consecutive lines, when the locks are released, the previously locked app will execute first query (which was the one locked) will return non-updated data but the second will return updated data by app which previously had the locks.
2. If I manually run above procedure through two ORACLE SQL DEVELOPER instances, the behavior is as expected, meaning:

SQL_DEV_1: SELECT * FROM T_MAIL WHERE STATUS = 'QUEUED' FOR UPDATE; // Returns 1 row, locks the row
SQL_DEV_2: SELECT * FROM T_MAIL WHERE STATUS = 'QUEUED' FOR UPDATE; // Doesn't return anything but keeps waiting for locks to be released
SQL_DEV_1: UPDATE T_MAIL SET STATUS = 'SENT'; // Returns 1 row, locks the row
SQL_DEV_1: COMMIT; // Commit update, locks are released
SQL_DEV_2: // waiting query is executed, returns no rows since one and only row was update to SENT

回答1:


I think I've found solution (or workaround). When you are using PESSIMISTIC_WRITE second server has no idea about fact that first one is changing data. Terotically he can know about this when LockModeType.PESSIMISTIC_READ is used, but in Oracle LockModeType.PESSIMISTIC_READ is implemented using LockModeType.PESSIMISTIC_WRITE. So the workaround decision for you is to add version field and setting mode to LockModeType.PESSIMISTIC_FORCE_INCREMENT



来源:https://stackoverflow.com/questions/37119874/jpa-lockmodetype-pessimistic-write-doesnt-work-as-expected-app-without-keys-r

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!