/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.stress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.transaction.TransactionManager;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.util.InfinispanMessageLogger;
import org.hibernate.cfg.Environment;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.test.cache.infinispan.functional.entities.Age;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertFalse;
/**
* A stress test for putFromLoad operations
*
* @author Galder ZamarreƱo
* @since 4.1
*/
@Ignore
public class PutFromLoadStressTestCase {
static final InfinispanMessageLogger log = InfinispanMessageLogger.Provider.getLog(PutFromLoadStressTestCase.class);
static final boolean isTrace = log.isTraceEnabled();
static final int NUM_THREADS = 100;
static final int WARMUP_TIME_SECS = 10;
static final long RUNNING_TIME_SECS = Integer.getInteger("time", 60);
static final long LAUNCH_INTERVAL_MILLIS = 10;
static final int NUM_INSTANCES = 5000;
static SessionFactory sessionFactory;
static TransactionManager tm;
final AtomicBoolean run = new AtomicBoolean(true);
@BeforeClass
public static void beforeClass() {
// Extra options located in src/test/resources/hibernate.properties
StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder()
.applySetting( Environment.USE_SECOND_LEVEL_CACHE, "true" )
.applySetting( Environment.USE_QUERY_CACHE, "true" )
// TODO: Tweak to have a fully local region factory (no transport, cache mode = local, no marshalling, ...etc)
.applySetting(
Environment.CACHE_REGION_FACTORY,
"org.hibernate.cache.infinispan.InfinispanRegionFactory"
)
.applySetting(
Environment.JTA_PLATFORM,
"org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"
)
// Force minimal puts off to simplify stressing putFromLoad logic
.applySetting( Environment.USE_MINIMAL_PUTS, "false" )
.applySetting( Environment.HBM2DDL_AUTO, "create-drop" );
StandardServiceRegistry serviceRegistry = ssrb.build();
MetadataSources metadataSources = new MetadataSources( serviceRegistry )
.addResource( "cache/infinispan/functional/Item.hbm.xml" )
.addResource( "cache/infinispan/functional/Customer.hbm.xml" )
.addResource( "cache/infinispan/functional/Contact.hbm.xml" )
.addAnnotatedClass( Age.class );
Metadata metadata = metadataSources.buildMetadata();
for ( PersistentClass entityBinding : metadata.getEntityBindings() ) {
if ( entityBinding instanceof RootClass ) {
( (RootClass) entityBinding ).setCacheConcurrencyStrategy( "transactional" );
}
}
for ( Collection collectionBinding : metadata.getCollectionBindings() ) {
collectionBinding.setCacheConcurrencyStrategy( "transactional" );
}
sessionFactory = metadata.buildSessionFactory();
tm = com.arjuna.ats.jta.TransactionManager.transactionManager();
}
@AfterClass
public static void afterClass() {
sessionFactory.close();
}
@Test
public void testQueryPerformance() throws Exception {
store();
// doTest(true);
// run.set(true); // Reset run
doTest(false);
}
private void store() throws Exception {
for (int i = 0; i < NUM_INSTANCES; i++) {
final Age age = new Age();
age.setAge(i);
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sessionFactory.openSession();
s.getTransaction().begin();
s.persist(age);
s.getTransaction().commit();
s.close();
return null;
}
});
}
}
private void doTest(boolean warmup) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
try {
CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS + 1);
List<Future<String>> futures = new ArrayList<Future<String>>(NUM_THREADS);
for (int i = 0; i < NUM_THREADS; i++) {
Future<String> future = executor.submit(
new SelectQueryRunner(barrier, warmup, i + 1));
futures.add(future);
Thread.sleep(LAUNCH_INTERVAL_MILLIS);
}
barrier.await(); // wait for all threads to be ready
long timeout = warmup ? WARMUP_TIME_SECS : RUNNING_TIME_SECS;
TimeUnit unit = TimeUnit.SECONDS;
Thread.sleep(unit.toMillis(timeout)); // Wait for the duration of the test
run.set(false); // Instruct tests to stop doing work
barrier.await(2, TimeUnit.MINUTES); // wait for all threads to finish
log.infof("[%s] All threads finished, check for exceptions", title(warmup));
for (Future<String> future : futures) {
String opsPerMS = future.get();
if (!warmup)
log.infof("[%s] Operations/ms: %s", title(warmup), opsPerMS);
}
log.infof("[%s] All future gets checked", title(warmup));
} catch (Exception e) {
log.errorf(e, "Error in one of the execution threads during %s", title(warmup));
throw e;
} finally {
executor.shutdownNow();
}
}
private String title(boolean warmup) {
return warmup ? "warmup" : "stress";
}
public class SelectQueryRunner implements Callable<String> {
final CyclicBarrier barrier;
final boolean warmup;
final Integer customerId;
public SelectQueryRunner(CyclicBarrier barrier, boolean warmup, Integer customerId) {
this.barrier = barrier;
this.warmup = warmup;
this.customerId = customerId;
}
@Override
public String call() throws Exception {
try {
if (isTrace)
log.tracef("[%s] Wait for all executions paths to be ready to perform calls", title(warmup));
barrier.await();
long start = System.nanoTime();
int runs = 0;
if (isTrace) {
log.tracef("[%s] Start time: %d", title(warmup), start);
}
queryItems();
long end = System.nanoTime();
long duration = end - start;
if (isTrace)
log.tracef("[%s] End time: %d, duration: %d, runs: %d",
title(warmup), start, duration, runs);
return opsPerMS(duration, runs);
} finally {
if (isTrace)
log.tracef("[%s] Wait for all execution paths to finish", title(warmup));
barrier.await();
}
}
private void deleteCachedItems() throws Exception {
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
sessionFactory.getCache().evictEntityRegion(Age.class);
return null;
}
});
}
private void queryItems() throws Exception {
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sessionFactory.getCurrentSession();
Query query = s.getNamedQuery(Age.QUERY).setCacheable(true);
List<Age> result = (List<Age>) query.list();
assertFalse(result.isEmpty());
return null;
}
});
}
private String opsPerMS(long nanos, int ops) {
long totalMillis = TimeUnit.NANOSECONDS.toMillis(nanos);
if (totalMillis > 0)
return ops / totalMillis + " ops/ms";
else
return "NAN ops/ms";
}
}
}