mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#2089 - Postgres - Use NO KEY with FOR UPDATE clauses with Postgres
This commit is contained in:
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestQueryForUpdate extends BaseTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
@ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB})
|
||||
public void testForUpdate() {
|
||||
@@ -35,6 +34,8 @@ public class TestQueryForUpdate extends BaseTestCase {
|
||||
query.findList();
|
||||
if (isSqlServer()) {
|
||||
assertThat(sqlOf(query)).contains("with (updlock)");
|
||||
} else if (isPostgres()) {
|
||||
assertThat(sqlOf(query)).contains("for no key update");
|
||||
} else {
|
||||
assertThat(sqlOf(query)).contains("for update");
|
||||
}
|
||||
@@ -67,7 +68,11 @@ public class TestQueryForUpdate extends BaseTestCase {
|
||||
if (isH2() || isPostgres()) {
|
||||
assertSql(sql.get(0)).contains("from e_basic t0 where t0.id =");
|
||||
assertSql(sql.get(1)).contains("from e_basic t0 where t0.id =");
|
||||
assertSql(sql.get(1)).contains("for update");
|
||||
if (isPostgres()) {
|
||||
assertSql(sql.get(1)).contains("for no key update");
|
||||
} else {
|
||||
assertSql(sql.get(1)).contains("for update");
|
||||
}
|
||||
}
|
||||
|
||||
transaction.end();
|
||||
@@ -91,6 +96,8 @@ public class TestQueryForUpdate extends BaseTestCase {
|
||||
assertThat(sqlOf(query)).contains("for update");
|
||||
} else if (isSqlServer()) {
|
||||
assertThat(sqlOf(query)).contains("with (updlock,nowait)");
|
||||
} else if (isPostgres()) {
|
||||
assertThat(sqlOf(query)).contains("for no key update nowait");
|
||||
} else {
|
||||
assertThat(sqlOf(query)).contains("for update nowait");
|
||||
}
|
||||
@@ -116,6 +123,8 @@ public class TestQueryForUpdate extends BaseTestCase {
|
||||
assertThat(sqlOf(query)).contains("with (updlock,nowait)");
|
||||
} else if (isH2()) {
|
||||
assertThat(sqlOf(query)).contains("for update");
|
||||
} else if (isPostgres()) {
|
||||
assertThat(sqlOf(query)).contains("for no key update nowait");
|
||||
} else {
|
||||
assertThat(sqlOf(query)).contains("for update nowait");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.tests.basic;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.annotation.Transactional;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tests.model.basic.Article;
|
||||
import org.tests.model.basic.Section;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestQueryForUpdatePostgresLock extends BaseTestCase {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TestQueryForUpdatePostgresLock.class);
|
||||
|
||||
private long timePreInsert;
|
||||
private long timePostInsert;
|
||||
private long timePreLock;
|
||||
private long timePostLock;
|
||||
|
||||
@Test
|
||||
@ForPlatform(Platform.POSTGRES)
|
||||
public void testForUpdatePostgresLock() throws InterruptedException {
|
||||
|
||||
Article article = new Article("lockTest", "auth");
|
||||
DB.save(article);
|
||||
final Integer id = article.getId();
|
||||
|
||||
ExecutorService exec = Executors.newFixedThreadPool(2);
|
||||
exec.submit(() -> lockArticle(id));
|
||||
exec.submit(() -> insertSection(id));
|
||||
exec.awaitTermination(2, TimeUnit.SECONDS);
|
||||
exec.shutdown();
|
||||
|
||||
// assert the lock was obtained before the insert was attempted
|
||||
assertThat(timePreLock).isLessThan(timePreInsert);
|
||||
// assert that the insert wasn't waiting on the lock to complete
|
||||
assertThat(timePostInsert).isLessThan(timePostLock);
|
||||
}
|
||||
|
||||
/**
|
||||
* This holds row lock on article for 1 second.
|
||||
* With FOR NO KEY UPDATE this does not block the insert.
|
||||
*/
|
||||
@Transactional
|
||||
private void lockArticle(Integer id) {
|
||||
timePreLock = System.currentTimeMillis();
|
||||
log.info("lock start");
|
||||
DB.find(Article.class).setId(id).forUpdate().findOne();
|
||||
sleep(1000);
|
||||
timePostLock = System.currentTimeMillis();
|
||||
log.info("lock done");
|
||||
}
|
||||
|
||||
/**
|
||||
* This inserts with FK to the article that is locked.
|
||||
* With FOR NO KEY UPDATE this insert does not wait on the lock.
|
||||
*/
|
||||
@Transactional
|
||||
private void insertSection(Integer id) {
|
||||
sleep(100);
|
||||
log.info("insert start");
|
||||
timePreInsert = System.currentTimeMillis();
|
||||
Section section = new Section();
|
||||
section.setArticle(DB.getReference(Article.class, id));
|
||||
DB.save(section);
|
||||
timePostInsert = System.currentTimeMillis();
|
||||
log.info("inserted");
|
||||
}
|
||||
|
||||
private void sleep(int millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user