Merge branch 'master' of github.com:ebean-orm/ebean

This commit is contained in:
Rob Bygrave
2022-03-14 15:37:52 +13:00
170 changed files with 1491 additions and 1155 deletions
@@ -4,17 +4,21 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.MDC;
import io.ebean.config.MdcBackgroundExecutorWrapper;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DefaultBackgroundExecutorTest {
@Test
public void submit_callable() throws Exception {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 2, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 2, "test", null);
final Future<String> future0 = es.submit(() -> "Hello");
final Future<String> future1 = es.submit(() -> "There");
@@ -39,7 +43,7 @@ public class DefaultBackgroundExecutorTest {
public void shutdown_slowCallable_expect_interrupted() throws Exception {
int shutdownWaitSecs = 1;
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, shutdownWaitSecs, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, shutdownWaitSecs, "test", null);
final Future<String> future2 = es.submit(() -> {
try {
@@ -61,7 +65,7 @@ public class DefaultBackgroundExecutorTest {
@Disabled("test takes long time")
public void shutdown_when_running_expect_waitAndNiceShutdown() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 20, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 20, "test", null);
es.execute(new RunFor(3000, "a"));
es.execute(new RunFor(3000, "b"));
@@ -74,7 +78,7 @@ public class DefaultBackgroundExecutorTest {
@Disabled("test takes long time")
public void shutdown_when_rougeRunnable_expect_InterruptedException() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test", null);
es.execute(new RunFor(300000, "a"));
es.execute(new RunFor(3000, "b"));
@@ -85,12 +89,12 @@ public class DefaultBackgroundExecutorTest {
@Test
public void wrapWithNoMDC() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test", null);
assertThat(MDC.getCopyOfContextMap()).isNull();
es.wrapMDC(() -> {
es.wrap(() -> {
assertThat(MDC.getCopyOfContextMap()).isNull();
});
es.wrapMDC(() -> {
es.wrap(() -> {
assertThat(MDC.getCopyOfContextMap()).isNull();
return "Callable";
});
@@ -98,25 +102,44 @@ public class DefaultBackgroundExecutorTest {
}
@Test
public void wrapWithMDC_expect_() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
public void wrapWithMDC_expect_() throws Exception {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test", new MdcBackgroundExecutorWrapper());
// MDC has a copyOnThread map. So we must pass different values to check if the test will work
MDC.clear();
es.submit(()->{
assertThat(MDC.get("hello")).isNull();
}).get();
MDC.put("hello", "there");
es.wrapMDC(() -> {
es.wrap(() -> {
assertThat(MDC.get("hello")).isEqualTo("there");
});
es.wrapMDC(() -> {
}).run(); // will clear the MDC. But this should be OK
MDC.put("hello", "there");
es.wrap(() -> {
assertThat(MDC.get("hello")).isEqualTo("there");
return "Callable";
});
}).call(); // will clear the MDC. But this should be OK
MDC.put("hello", "there");
CountDownLatch latch = new CountDownLatch(1);
es.execute(() -> {
// the assertion is executed async, so it will only logged on console
assertThat(MDC.get("hello")).isEqualTo("there");
latch.countDown();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
es.submit(() -> {
assertThat(MDC.get("hello")).isEqualTo("there");
return "Callable";
});
}).get();
MDC.clear();
es.execute(()->{
assertThat(MDC.get("hello")).isNull();
});
es.shutdown();
}
@@ -0,0 +1,108 @@
package org.tests.cache;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.concurrent.Callable;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.OCachedBean;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.config.BackgroundExecutorWrapper;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.MdcBackgroundExecutorWrapper;
import io.ebeaninternal.server.cache.DefaultServerCachePlugin;
/**
* Test class testing async/background cache updates in a multi-tenant environment.
*/
public class TestBeanCacheAsync extends BaseTestCase {
private final ThreadLocal<String> tenantId = new ThreadLocal<>();
class ThreadLocalTenantProvider implements CurrentTenantProvider {
@Override
public Object currentId() {
return tenantId.get();
}
}
/**
* Copy tenant info to the background thread.
*/
class TenantCopyBackgroundExecutorWrapper implements BackgroundExecutorWrapper {
@Override
public <T> Callable<T> wrap(Callable<T> task) {
String tenant = tenantId.get(); // executed in current thread
return () -> {
tenantId.set(tenant); // executed in other thread
try {
return task.call();
} finally {
tenantId.remove();
}
};
}
@Override
public Runnable wrap(Runnable task) {
String tenant = tenantId.get();
return () -> {
tenantId.set(tenant);
try {
task.run();
} finally {
tenantId.remove();
}
};
}
}
@Test
public void findById_with_tenant() throws InterruptedException {
DatabaseConfig config = new DatabaseConfig();
config.setName(DB.getDefault().name());
config.loadFromProperties();
config.setDataSource(DB.getDefault().dataSource());
config.setReadOnlyDataSource(DB.getDefault().readOnlyDataSource());
config.setDdlExtra(false);
config.setDdlGenerate(false);
config.setDdlRun(false);
config.setDefaultServer(false);
config.setRegister(false);
config.setServerCachePlugin(new DefaultServerCachePlugin()); // disables foreground local caching (as it is done in Hz/Ignite)
config.setCurrentTenantProvider(new ThreadLocalTenantProvider());
config.setBackgroundExecutorWrapper(
new MdcBackgroundExecutorWrapper().with(new TenantCopyBackgroundExecutorWrapper()));
tenantId.set("4711");
Database db = DatabaseFactory.create(config);
try {
OCachedBean bean = new OCachedBean();
bean.setName("findById");
db.save(bean);
OCachedBean bean0 = db.find(OCachedBean.class, bean.getId());
assertNotNull(bean0);
assertThat(bean0.getName()).isEqualTo("findById");
bean0.setName("findById2");
db.save(bean0);
Thread.sleep(100); // TODO: can we block finds on that ID if a pending cache update is present?
bean0 = db.find(OCachedBean.class, bean.getId());
assertNotNull(bean0);
assertThat(bean0.getName()).isEqualTo("findById2");
} finally {
db.shutdown(false, false);
}
}
}