/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* 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.ogm.perftest.mongodb.ogm;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider;
import org.hibernate.ogm.datastore.spi.DatastoreProvider;
import org.hibernate.ogm.perftest.model.AuthorWithSequence;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole;
import com.mongodb.BasicDBObject;
/**
* A JMH benchmark measuring performance of parallel reads and writes using Hibernate OGM.
*
* @author Gunnar Morling
*/
public class HibernateOgmCombinedBenchmark {
public static final int NUMBER_OF_TEST_ENTITIES = 10000;
/**
* The number of operations to be performed with one entity manager. Using an EM only for one op is an anti-pattern,
* but setting the number too high will result in an unrealistic result. Aim for a value to be expected during the
* processing of one web request or similar.
*/
private static final int GETS_PER_INVOCATION = 10;
private static final int FINDS_PER_INVOCATION = 6;
private static final int UPDATES_PER_INVOCATION = 2;
private static final int INSERTS_PER_INVOCATION = 2;
@State(Scope.Benchmark)
public static class TestDataInserter {
private EntityManagerFactoryHolder stateHolder;
@Setup
public void insertTestData(EntityManagerFactoryHolder stateHolder) throws Exception {
this.stateHolder = stateHolder;
EntityManager entityManager = stateHolder.entityManagerFactory.createEntityManager();
for ( int i = 0; i <= NUMBER_OF_TEST_ENTITIES; i++ ) {
if ( i % 1000 == 0 ) {
stateHolder.transactionManager.begin();
entityManager.joinTransaction();
}
AuthorWithSequence author = new AuthorWithSequence();
author.setBio( "This is a decent size bio made of " + stateHolder.rand.nextDouble() + " stuffs" );
author.setDob( new Date() );
author.setFname( "Jessie " + stateHolder.rand.nextInt() );
author.setLname( "Landis " + stateHolder.rand.nextInt() );
author.setMname( "" + stateHolder.rand.nextInt( 26 ) );
entityManager.persist( author );
if ( i % 1000 == 0 ) {
stateHolder.transactionManager.commit();
System.out.println( "Inserted " + i + " entities" );
}
}
entityManager.close();
MongoDBDatastoreProvider datastoreProvider = (MongoDBDatastoreProvider) ( (SessionFactoryImplementor) stateHolder.entityManagerFactory.unwrap( SessionFactory.class ) ).getServiceRegistry().getService( DatastoreProvider.class );
datastoreProvider.getDatabase().getCollection( "AuthorWithSequence" ).createIndex( new BasicDBObject( "mname", 1 ) );
}
}
@Benchmark
@OperationsPerInvocation(GETS_PER_INVOCATION + FINDS_PER_INVOCATION + INSERTS_PER_INVOCATION + UPDATES_PER_INVOCATION)
public void combinedFindAndUpdate(TestDataInserter inserter, Blackhole blackhole) throws Exception {
doCombinedFindAndUpdate( inserter, blackhole );
}
@Benchmark
@OperationsPerInvocation(GETS_PER_INVOCATION + FINDS_PER_INVOCATION + INSERTS_PER_INVOCATION + UPDATES_PER_INVOCATION)
@Threads(25)
public void combinedFindAndUpdateWithThreadCount_025(TestDataInserter inserter, Blackhole blackhole) throws Exception {
doCombinedFindAndUpdate( inserter, blackhole );
}
@Benchmark
@OperationsPerInvocation(GETS_PER_INVOCATION + FINDS_PER_INVOCATION + INSERTS_PER_INVOCATION + UPDATES_PER_INVOCATION)
@Threads(50)
public void combinedFindAndUpdateWithThreadCount_050(TestDataInserter inserter, Blackhole blackhole) throws Exception {
doCombinedFindAndUpdate( inserter, blackhole );
}
@Benchmark
@OperationsPerInvocation(GETS_PER_INVOCATION + FINDS_PER_INVOCATION + INSERTS_PER_INVOCATION + UPDATES_PER_INVOCATION)
@Threads(100)
public void combinedFindAndUpdateWithThreadCount_100(TestDataInserter inserter, Blackhole blackhole) throws Exception {
doCombinedFindAndUpdate( inserter, blackhole );
}
private void doCombinedFindAndUpdate(TestDataInserter inserter, Blackhole blackhole) throws NotSupportedException, SystemException, RollbackException,
HeuristicMixedException, HeuristicRollbackException {
EntityManagerFactoryHolder stateHolder = inserter.stateHolder;
EntityManager entityManager = stateHolder.entityManagerFactory.createEntityManager();
stateHolder.transactionManager.begin();
entityManager.joinTransaction();
AuthorWithSequence author = null;
int updates = 0;
// do GETS_PER_INVOCATION find-by-id
for ( int i = 0; i < GETS_PER_INVOCATION; i++ ) {
long id = stateHolder.rand.nextInt( NUMBER_OF_TEST_ENTITIES - 1 ) + 1;
author = entityManager.find( AuthorWithSequence.class, id );
if ( author == null ) {
throw new IllegalArgumentException( "Couldn't find entry with id " + id );
}
blackhole.consume( author.getFname() );
// do UPDATES_PER_INVOCATION updates
if ( updates < UPDATES_PER_INVOCATION ) {
author.setLname( "Royce" );
updates++;
}
}
// do FINDS_PER_INVOCATION queries
for ( int i = 0; i < FINDS_PER_INVOCATION; i++ ) {
int mName = stateHolder.rand.nextInt( 26 );
TypedQuery<AuthorWithSequence> query = entityManager.createNamedQuery( "author_by_mname", AuthorWithSequence.class );
query.setMaxResults( 50 );
query.setParameter( "mname", "" + mName );
List<AuthorWithSequence> authors = query.getResultList();
for ( AuthorWithSequence foundAuthor : authors ) {
blackhole.consume( foundAuthor.getLname() );
}
}
// do INSERTS_PER_INVOCATION queries
for ( int i = 0; i < INSERTS_PER_INVOCATION; i++ ) {
AuthorWithSequence newAuthor = new AuthorWithSequence();
newAuthor.setBio( "This is a decent size bio made of " + stateHolder.rand.nextDouble() + " stuffs" );
newAuthor.setDob( new Date() );
newAuthor.setFname( "Jessie " + stateHolder.rand.nextInt() );
newAuthor.setLname( "Landis " + stateHolder.rand.nextInt() );
newAuthor.setMname( "" + stateHolder.rand.nextInt( 26 ) );
entityManager.persist( newAuthor );
}
stateHolder.transactionManager.commit();
entityManager.close();
}
/**
* For debugging purposes.
*/
public static void main(String[] args) throws Exception {
EntityManagerFactoryHolder entityManagerFactoryHolder = new EntityManagerFactoryHolder();
entityManagerFactoryHolder.setupEntityManagerFactory();
TestDataInserter inserter = new TestDataInserter();
inserter.insertTestData( entityManagerFactoryHolder );
new HibernateOgmCombinedBenchmark().combinedFindAndUpdate( inserter, null );
}
}