[hibernate-commits] Hibernate SVN: r20892 - in core/branches/Branch_3_2_4_SP1_CP: test/org/hibernate/test/id and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Feb 9 05:54:31 EST 2011


Author: stliu
Date: 2011-02-09 05:54:31 -0500 (Wed, 09 Feb 2011)
New Revision: 20892

Added:
   core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/id/enhanced/
   core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/id/enhanced/OptimizerUnitTest.java
Modified:
   core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/OptimizerFactory.java
   core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java
   core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/TableGenerator.java
Log:
JBPAPP-5762 HHH-5217 Minimize double sequence value reads in PooledOptimizer

Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/OptimizerFactory.java	2011-02-09 04:51:08 UTC (rev 20891)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/OptimizerFactory.java	2011-02-09 10:54:31 UTC (rev 20892)
@@ -23,7 +23,25 @@
 	public static final String POOL = "pooled";
 
 	private static Class[] CTOR_SIG = new Class[] { Class.class, int.class };
-
+	
+	/**
+	 * Marker interface for optimizer which wish to know the user-specified initial value.
+	 * <p/>
+	 * Used instead of constructor injection since that is already a public understanding and
+	 * because not all optimizers care.
+	 */
+	public static interface InitialValueAwareOptimizer {
+		/**
+		 * Reports the user specified initial value to the optimizer.
+		 * <p/>
+		 * <tt>-1</tt> is used to indicate that the user did not specify.
+		 *
+		 * @param initialValue The initial value specified by the user, or <tt>-1</tt> to indicate that the
+		 * user did not specify.
+		 */
+		public void injectInitialValue(long initialValue);
+	}
+	
 	public static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize) {
 		String optimizerClassName;
 		if ( NONE.equals( type ) ) {
@@ -51,6 +69,25 @@
 		// the default...
 		return new NoopOptimizer( returnClass, incrementSize );
 	}
+	
+	/**
+	 * Builds an optimizer
+	 *
+	 * @param type The optimizer type, either a short-hand name or the {@link Optimizer} class name.
+	 * @param returnClass The generated value java type
+	 * @param incrementSize The increment size.
+	 * @param explicitInitialValue The user supplied initial-value (-1 indicates the user did not specify).
+	 *
+	 * @return The built optimizer
+	 */
+	@SuppressWarnings({ "UnnecessaryBoxing", "deprecation" })
+	public static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize, long explicitInitialValue) {
+		final Optimizer optimizer = buildOptimizer( type, returnClass, incrementSize );
+		if ( InitialValueAwareOptimizer.class.isInstance( optimizer ) ) {
+			( (InitialValueAwareOptimizer) optimizer ).injectInitialValue( explicitInitialValue );
+		}
+		return optimizer;
+	}
 
 	public static abstract class OptimizerSupport implements Optimizer {
 		protected final Class returnClass;
@@ -154,10 +191,14 @@
 		}
 	}
 
-	public static class PooledOptimizer extends OptimizerSupport {
+	/**
+	 * Optimizer which uses a pool of values, storing the next low value of the
+	 * range in the database.
+	 */
+	public static class PooledOptimizer extends OptimizerSupport implements InitialValueAwareOptimizer{
 		private long value;
 		private long hiValue = -1;
-
+		private long initialValue = -1;
 		public PooledOptimizer(Class returnClass, int incrementSize) {
 			super( returnClass, incrementSize );
 			if ( incrementSize < 1 ) {
@@ -168,7 +209,10 @@
 			}
 		}
 
-		public Serializable generate(AccessCallback callback) {
+		/**
+		 * {@inheritDoc}
+		 */
+		public synchronized Serializable generate(AccessCallback callback) {
 			if ( hiValue < 0 ) {
 				value = callback.getNextValue();
 				if ( value < 1 ) {
@@ -178,7 +222,14 @@
 					// we are using a sequence...
 					log.info( "pooled optimizer source reported [" + value + "] as the initial value; use of 1 or greater highly recommended" );
 				}
-				hiValue = callback.getNextValue();
+				if ( ( initialValue == -1 && value < incrementSize ) || ( value == initialValue ) ) {
+					// the call to obtain next-value just gave us the initialValue
+					hiValue = callback.getNextValue();
+				}
+				else {
+					hiValue = value;
+					value = hiValue - incrementSize;
+				}
 			}
 			else if ( value >= hiValue ) {
 				hiValue = callback.getNextValue();
@@ -187,16 +238,31 @@
 			return make( value++ );
 		}
 
+		/**
+		 * {@inheritDoc}
+		 */
 		public long getLastSourceValue() {
 			return hiValue;
 		}
 
+		/**
+		 * {@inheritDoc}
+		 */
 		public boolean applyIncrementSizeToSourceValues() {
 			return true;
 		}
 
+		/**
+		 * Getter for property 'lastValue'.
+		 *
+		 * @return Value for property 'lastValue'.
+		 */
 		public long getLastValue() {
 			return value - 1;
 		}
+
+		public void injectInitialValue( long initialValue ) {
+			this.initialValue = initialValue;
+		}
 	}
 }

Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java	2011-02-09 04:51:08 UTC (rev 20891)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java	2011-02-09 10:54:31 UTC (rev 20892)
@@ -146,7 +146,7 @@
 			databaseStructure = new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
 		}
 
-		optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+		optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize, -1 );
 		databaseStructure.prepare( optimizer );
 	}
 

Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/TableGenerator.java	2011-02-09 04:51:08 UTC (rev 20891)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/id/enhanced/TableGenerator.java	2011-02-09 10:54:31 UTC (rev 20892)
@@ -272,7 +272,7 @@
 
 		String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
 		String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
-		optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+		optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize, -1 );
 	}
 	
 	/**

Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/id/enhanced/OptimizerUnitTest.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/id/enhanced/OptimizerUnitTest.java	                        (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/id/enhanced/OptimizerUnitTest.java	2011-02-09 10:54:31 UTC (rev 20892)
@@ -0,0 +1,338 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.id.enhanced;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.hibernate.id.enhanced.AccessCallback;
+import org.hibernate.id.enhanced.Optimizer;
+import org.hibernate.id.enhanced.OptimizerFactory;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+ at SuppressWarnings({ "deprecation" })
+public class OptimizerUnitTest extends TestCase {
+	public OptimizerUnitTest(String string) {
+		super( string );
+	}
+
+	public static Test suite() {
+		return new TestSuite( OptimizerUnitTest.class );
+	}
+
+	public void testBasicNoOptimizerUsage() {
+		// test historic sequence behavior, where the initial values start at 1...
+		SourceMock sequence = new SourceMock( 1 );
+		Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
+		for ( int i = 1; i < 11; i++ ) {
+			final Long next = ( Long ) optimizer.generate( sequence );
+			assertEquals( i, next.intValue() );
+		}
+		assertEquals( 10, sequence.getTimesCalled() );
+		assertEquals( 10, sequence.getCurrentValue() );
+
+		// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
+		sequence = new SourceMock( 0 );
+		optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
+		for ( int i = 1; i < 11; i++ ) {
+			final Long next = ( Long ) optimizer.generate( sequence );
+			assertEquals( i, next.intValue() );
+		}
+		assertEquals( 11, sequence.getTimesCalled() ); // an extra time to get to 1 initially
+		assertEquals( 10, sequence.getCurrentValue() );
+	}
+
+	public void testBasicHiLoOptimizerUsage() {
+		int increment = 10;
+		Long next;
+
+		// test historic sequence behavior, where the initial values start at 1...
+		SourceMock sequence = new SourceMock( 1 );
+		Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
+		for ( int i = 1; i <= increment; i++ ) {
+			next = ( Long ) optimizer.generate( sequence );
+			assertEquals( i, next.intValue() );
+		}
+		assertEquals( 1, sequence.getTimesCalled() ); // once to initialze state
+		assertEquals( 1, sequence.getCurrentValue() );
+		// force a "clock over"
+		next = ( Long ) optimizer.generate( sequence );
+		assertEquals( 11, next.intValue() );
+		assertEquals( 2, sequence.getTimesCalled() );
+		assertEquals( 2, sequence.getCurrentValue() );
+
+		// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
+		sequence = new SourceMock( 0 );
+		optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
+		for ( int i = 1; i <= increment; i++ ) {
+			next = ( Long ) optimizer.generate( sequence );
+			assertEquals( i, next.intValue() );
+		}
+		assertEquals( 2, sequence.getTimesCalled() ); // here have have an extra call to get to 1 initially
+		assertEquals( 1, sequence.getCurrentValue() );
+		// force a "clock over"
+		next = ( Long ) optimizer.generate( sequence );
+		assertEquals( 11, next.intValue() );
+		assertEquals( 3, sequence.getTimesCalled() );
+		assertEquals( 2, sequence.getCurrentValue() );
+	}
+
+	public void testBasicPooledOptimizerUsage() {
+		Long next;
+		// test historic sequence behavior, where the initial values start at 1...
+		SourceMock sequence = new SourceMock( 1, 10 );
+		Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 10 );
+		for ( int i = 1; i < 11; i++ ) {
+			next = ( Long ) optimizer.generate( sequence );
+			assertEquals( i, next.intValue() );
+		}
+		assertEquals( 2, sequence.getTimesCalled() ); // twice to initialize state
+		assertEquals( 11, sequence.getCurrentValue() );
+		// force a "clock over"
+		next = ( Long ) optimizer.generate( sequence );
+		assertEquals( 11, next.intValue() );
+		assertEquals( 3, sequence.getTimesCalled() );
+		assertEquals( 21, sequence.getCurrentValue() );
+	}
+
+	public void testSubsequentPooledOptimizerUsage() {
+		// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
+		//		cheat by telling the sequence to start with 1000
+		final SourceMock sequence = new SourceMock( 1001, 3, 5 );
+		//		but tell the optimizer the start-with is 1
+		final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
+
+		assertEquals( 5, sequence.getTimesCalled() );
+		assertEquals( 1001, sequence.getCurrentValue() );
+
+		Long next = (Long) optimizer.generate( sequence );
+		assertEquals( 1001, next.intValue() );
+		assertEquals( (5+1), sequence.getTimesCalled() );
+		assertEquals( (1001+3), sequence.getCurrentValue() );
+
+		next = (Long) optimizer.generate( sequence );
+		assertEquals( (1001+1), next.intValue() );
+		assertEquals( (5+1), sequence.getTimesCalled() );
+		assertEquals( (1001+3), sequence.getCurrentValue() );
+
+		next = (Long) optimizer.generate( sequence );
+		assertEquals( (1001+2), next.intValue() );
+		assertEquals( (5+1), sequence.getTimesCalled() );
+		assertEquals( (1001+3), sequence.getCurrentValue() );
+
+		// force a "clock over"
+		next = (Long) optimizer.generate( sequence );
+		assertEquals( (1001+3), next.intValue() );
+		assertEquals( (5+2), sequence.getTimesCalled() );
+		assertEquals( (1001+6), sequence.getCurrentValue() );
+	}
+
+//	public void testBasicPooledLoOptimizerUsage() {
+//		final SourceMock sequence = new SourceMock( 1, 3 );
+//		final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3 );
+//
+//		assertEquals( 0, sequence.getTimesCalled() );
+//		assertEquals( -1, sequence.getCurrentValue() );
+//
+//		Long next = ( Long ) optimizer.generate( sequence );
+//		assertEquals( 1, next.intValue() );
+//		assertEquals( 1, sequence.getTimesCalled() );
+//		assertEquals( 1, sequence.getCurrentValue() );
+//
+//		next = ( Long ) optimizer.generate( sequence );
+//		assertEquals( 2, next.intValue() );
+//		assertEquals( 1, sequence.getTimesCalled() );
+//		assertEquals( 1, sequence.getCurrentValue() );
+//
+//		next = ( Long ) optimizer.generate( sequence );
+//		assertEquals( 3, next.intValue() );
+//		assertEquals( 1, sequence.getTimesCalled() );
+//		assertEquals( 1, sequence.getCurrentValue() );
+//
+////		// force a "clock over"
+//		next = ( Long ) optimizer.generate( sequence );
+//		assertEquals( 4, next.intValue() );
+//		assertEquals( 2, sequence.getTimesCalled() );
+//		assertEquals( (1+3), sequence.getCurrentValue() );
+//	}
+
+	public void testSubsequentPooledLoOptimizerUsage() {
+		// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
+		//		cheat by telling the sequence to start with 1000
+		final SourceMock sequence = new SourceMock( 1001, 3, 5 );
+		//		but tell the optimizer the start-with is 1
+		final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
+
+		assertEquals( 5, sequence.getTimesCalled() );
+		assertEquals( 1001, sequence.getCurrentValue() );
+
+		Long next = ( Long ) optimizer.generate( sequence );
+		assertEquals( (1001), next.intValue() );
+		assertEquals( (5+1), sequence.getTimesCalled() );
+		assertEquals( (1001+3), sequence.getCurrentValue() );
+
+		next = ( Long ) optimizer.generate( sequence );
+		assertEquals( (1001+1), next.intValue() );
+		assertEquals( (5+1), sequence.getTimesCalled() );
+		assertEquals( (1001+3), sequence.getCurrentValue() );
+
+		next = ( Long ) optimizer.generate( sequence );
+		assertEquals( (1001+2), next.intValue() );
+		assertEquals( (5+1), sequence.getTimesCalled() );
+		assertEquals( (1001+3), sequence.getCurrentValue() );
+
+//		// force a "clock over"
+		next = ( Long ) optimizer.generate( sequence );
+		assertEquals( (1001+3), next.intValue() );
+		assertEquals( (5+2), sequence.getTimesCalled() );
+		assertEquals( (1001+6), sequence.getCurrentValue() );
+	}
+
+	public void testRecoveredPooledOptimizerUsage() {
+		final SourceMock sequence = new SourceMock( 1, 3 );
+		final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
+
+		assertEquals( 0, sequence.getTimesCalled() );
+		assertEquals( -1, sequence.getCurrentValue() );
+
+		Long next = ( Long ) optimizer.generate( sequence );
+		assertEquals( 1, next.intValue() );
+		assertEquals( 2, sequence.getTimesCalled() );
+		assertEquals( 4, sequence.getCurrentValue() );
+
+		// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
+		final Optimizer optimizer2 = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
+		next = ( Long ) optimizer2.generate( sequence );
+		assertEquals( 4, next.intValue() );
+		assertEquals( 3, sequence.getTimesCalled() );
+		assertEquals( 7, sequence.getCurrentValue() );
+	}
+
+//	public void testRecoveredPooledLoOptimizerUsage() {
+//		final SourceMock sequence = new SourceMock( 1, 3 );
+//		final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3, 1 );
+//
+//		assertEquals( 0, sequence.getTimesCalled() );
+//		assertEquals( -1, sequence.getCurrentValue() );
+//
+//		Long next = ( Long ) optimizer.generate( sequence );
+//		assertEquals( 1, next.intValue() );
+//		assertEquals( 1, sequence.getTimesCalled() );
+//		assertEquals( 1, sequence.getCurrentValue() );
+//
+//		// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
+//		final Optimizer optimizer2 = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3, 1 );
+//		next = ( Long ) optimizer2.generate( sequence );
+//		assertEquals( 4, next.intValue() );
+//		assertEquals( 2, sequence.getTimesCalled() );
+//		assertEquals( 4, sequence.getCurrentValue() );
+//	}
+
+	private static class SourceMock implements AccessCallback {
+		private long value;
+		private long initialValue;
+		private int increment;
+		private int timesCalled = 0;
+
+		public SourceMock(long initialValue) {
+			this( initialValue, 1 );
+		}
+
+		public SourceMock(long initialValue, int increment) {
+			this( initialValue, increment, 0 );
+		}
+
+		public SourceMock(long initialValue, int increment, int timesCalled) {
+			this.increment = increment;
+			this.timesCalled = timesCalled;
+			if ( timesCalled != 0 ) {
+				this.value=initialValue;
+				this.initialValue = 1;
+			}
+			else {
+				this.value=-1;
+				this.initialValue = initialValue;
+			}
+		}
+
+		public long getNextValue() {
+			try {
+				if ( timesCalled == 0 ) {
+					initValue();
+					return value;
+				}
+				else {
+					return value+=increment;
+				}
+			}
+			finally {
+				timesCalled++;
+			}
+		}
+
+		private void initValue() {
+			this.value = initialValue;
+		}
+
+		public int getTimesCalled() {
+			return timesCalled;
+		}
+
+		public long getCurrentValue() {
+			return value;
+		}
+	}
+
+//	public void testNoopDumping() {
+//		SourceMock sequence = new SourceMock( 1 );
+//		Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
+//		for ( int i = 1; i <= 41; i++ ) {
+//			System.out.println( i + " => " + optimizer.generate( sequence ) + " (" + sequence.getCurrentValue() + ")" );
+//		}
+//	}
+//
+//	public void testHiLoDumping() {
+//		int increment = 10;
+//		SourceMock sequence = new SourceMock( 1 );
+//		Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
+//		for ( int i = 1; i <= 41; i++ ) {
+//			System.out.println( i + " => " + optimizer.generate( sequence ) + " (" + sequence.getCurrentValue() + ")" );
+//		}
+//	}
+//
+//	public void testPooledDumping() {
+//		int increment = 10;
+//		SourceMock sequence = new SourceMock( 1, increment );
+//		Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, increment );
+//		for ( int i = 1; i <= 41; i++ ) {
+//			System.out.println( i + " => " + optimizer.generate( sequence ) + " (" + sequence.getCurrentValue() + ")" );
+//		}
+//	}
+
+}



More information about the hibernate-commits mailing list