Add support for Postgres lock types (no key, share, key share) with FOR UPDATE

This commit is contained in:
rob bygrave committed 2020-11-24 23:36:54 +13:00
1 parent f4ea674d60
commit 5c34b0daa5
16 files changed
+207 -41

No files matched your search

@@ -491,16 +491,31 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.forUpdate();
}
@Override
public Query<T> forUpdate(Query.LockType lockType) {
return query.forUpdate(lockType);
}
@Override
public Query<T> forUpdateNoWait() {
return query.forUpdateNoWait();
}
@Override
public Query<T> forUpdateNoWait(Query.LockType lockType) {
return query.forUpdateNoWait(lockType);
}
@Override
public Query<T> forUpdateSkipLocked() {
return query.forUpdateSkipLocked();
}
@Override
public Query<T> forUpdateSkipLocked(Query.LockType lockType) {
return query.forUpdateSkipLocked(lockType);
}
@Override
public Query<T> select(String fetchProperties) {
return query.select(fetchProperties);
@@ -559,6 +559,21 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> forUpdate(LockType lockType) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> forUpdateNoWait(LockType lockType) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> forUpdateSkipLocked(LockType lockType) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public boolean isForUpdate() {
return false;
@@ -569,6 +584,11 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public LockType getForUpdateLockType() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> alias(String alias) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -236,10 +236,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
*/
private Boolean autoTune;
/**
* For update mode.
*/
private ForUpdate forUpdate;
private LockType lockType;
private boolean singleAttribute;
@@ -967,21 +965,37 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public DefaultOrmQuery<T> forUpdate() {
return setForUpdateWithMode(ForUpdate.BASE);
return setForUpdateWithMode(ForUpdate.BASE, LockType.Default);
}
@Override
public Query<T> forUpdate(LockType lockType) {
return setForUpdateWithMode(ForUpdate.BASE, lockType);
}
@Override
public Query<T> forUpdateNoWait(LockType lockType) {
return setForUpdateWithMode(ForUpdate.NOWAIT, lockType);
}
@Override
public Query<T> forUpdateSkipLocked(LockType lockType) {
return setForUpdateWithMode(ForUpdate.SKIPLOCKED, lockType);
}
@Override
public DefaultOrmQuery<T> forUpdateNoWait() {
return setForUpdateWithMode(ForUpdate.NOWAIT);
return setForUpdateWithMode(ForUpdate.NOWAIT, LockType.Default);
}
@Override
public DefaultOrmQuery<T> forUpdateSkipLocked() {
return setForUpdateWithMode(ForUpdate.SKIPLOCKED);
return setForUpdateWithMode(ForUpdate.SKIPLOCKED, LockType.Default);
}
private DefaultOrmQuery<T> setForUpdateWithMode(ForUpdate mode) {
private DefaultOrmQuery<T> setForUpdateWithMode(ForUpdate mode, LockType lockType) {
this.forUpdate = mode;
this.lockType = lockType;
this.useBeanCache = CacheMode.OFF;
return this;
}
@@ -996,6 +1010,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return forUpdate;
}
@Override
public LockType getForUpdateLockType() {
return lockType;
}
@Override
public ProfilingListener getProfilingListener() {
return profilingListener;
@@ -30,9 +30,24 @@ public class PostgresPlatformTest {
platform.configure(config);
assertThat(config.isLockWithKey()).isTrue();
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED)).isEqualTo("X for update skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT)).isEqualTo("X for update nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE)).isEqualTo("X for update");
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED, Query.LockType.Default)).isEqualTo("X for update skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT, Query.LockType.Default)).isEqualTo("X for update nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE, Query.LockType.Default)).isEqualTo("X for update");
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED, Query.LockType.Update)).isEqualTo("X for update skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED, Query.LockType.NoKeyUpdate)).isEqualTo("X for no key update skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED, Query.LockType.Share)).isEqualTo("X for share skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED, Query.LockType.KeyShare)).isEqualTo("X for key share skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT, Query.LockType.Update)).isEqualTo("X for update nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT, Query.LockType.NoKeyUpdate)).isEqualTo("X for no key update nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT, Query.LockType.Share)).isEqualTo("X for share nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT, Query.LockType.KeyShare)).isEqualTo("X for key share nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE, Query.LockType.Update)).isEqualTo("X for update");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE, Query.LockType.NoKeyUpdate)).isEqualTo("X for no key update");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE, Query.LockType.Share)).isEqualTo("X for share");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE, Query.LockType.KeyShare)).isEqualTo("X for key share");
}
@Test
@@ -45,9 +60,9 @@ public class PostgresPlatformTest {
platform.configure(config);
assertThat(config.isLockWithKey()).isFalse();
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED)).isEqualTo("X for no key update skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT)).isEqualTo("X for no key update nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE)).isEqualTo("X for no key update");
assertThat(platform.withForUpdate("X", Query.ForUpdate.SKIPLOCKED, Query.LockType.Default)).isEqualTo("X for no key update skip locked");
assertThat(platform.withForUpdate("X", Query.ForUpdate.NOWAIT, Query.LockType.Default)).isEqualTo("X for no key update nowait");
assertThat(platform.withForUpdate("X", Query.ForUpdate.BASE, Query.LockType.Default)).isEqualTo("X for no key update");
}
}
@@ -5,7 +5,6 @@ import io.ebean.DB;
import io.ebean.annotation.ForPlatform;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Transactional;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -16,6 +15,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static io.ebean.Query.LockType.NoKeyUpdate;
import static org.assertj.core.api.Assertions.assertThat;
public class TestQueryForUpdatePostgresLock extends BaseTestCase {
@@ -27,7 +27,6 @@ public class TestQueryForUpdatePostgresLock extends BaseTestCase {
private long timePreLock;
private long timePostLock;
@Ignore // restore this test with explicit use of NO KEY lock strength
@Test
@ForPlatform(Platform.POSTGRES)
public void testForUpdatePostgresLock() throws InterruptedException {
@@ -56,7 +55,7 @@ public class TestQueryForUpdatePostgresLock extends BaseTestCase {
private void lockArticle(Integer id) {
timePreLock = System.currentTimeMillis();
log.info("lock start");
DB.find(Article.class).setId(id).forUpdate().findOne();
DB.find(Article.class).setId(id).forUpdate(NoKeyUpdate).findOne();
sleep(1000);
timePostLock = System.currentTimeMillis();
log.info("lock done");