HHH-17005 - Replace monitor with a Java lock to avoid pinning when using virtual threads
This commit replaces a `synchronized` with a ReentrantLock in: - PooledOptimizer - PooledLoOptimizer - LegacyHiLoAlgorithmOptimizer - HiLoOptimizer The other implementations do not use a monitor lock.
This commit is contained in:
parent
d78681eaa2
commit
9056695f70
|
@ -9,6 +9,8 @@ package org.hibernate.id.enhanced;
|
|||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.id.IntegralDataTypeHolder;
|
||||
|
@ -80,29 +82,39 @@ public class HiLoOptimizer extends AbstractOptimizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized Serializable generate(AccessCallback callback) {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
public Serializable generate(AccessCallback callback) {
|
||||
lock.lock();
|
||||
try {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
|
||||
if ( generationState.lastSourceValue == null ) {
|
||||
// first call, so initialize ourselves. we need to read the database
|
||||
// value and set up the 'bucket' boundaries
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
while ( generationState.lastSourceValue.lt( 1 ) ) {
|
||||
if ( generationState.lastSourceValue == null ) {
|
||||
// first call, so initialize ourselves. we need to read the database
|
||||
// value and set up the 'bucket' boundaries
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
while ( generationState.lastSourceValue.lt( 1 ) ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
}
|
||||
// upperLimit defines the upper end of the bucket values
|
||||
generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment();
|
||||
// initialize value to the lower end of the bucket
|
||||
generationState.value = generationState.upperLimit.copy().subtract( incrementSize );
|
||||
}
|
||||
// upperLimit defines the upper end of the bucket values
|
||||
generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment();
|
||||
// initialize value to the lower end of the bucket
|
||||
generationState.value = generationState.upperLimit.copy().subtract( incrementSize );
|
||||
else if ( ! generationState.upperLimit.gt( generationState.value ) ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment();
|
||||
generationState.value = generationState.upperLimit.copy().subtract( incrementSize );
|
||||
}
|
||||
return generationState.value.makeValueThenIncrement();
|
||||
}
|
||||
else if ( ! generationState.upperLimit.gt( generationState.value ) ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment();
|
||||
generationState.value = generationState.upperLimit.copy().subtract( incrementSize );
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return generationState.value.makeValueThenIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a lock instead of the monitor lock to avoid pinning when using virtual threads.
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private GenerationState noTenantState;
|
||||
private Map<String,GenerationState> tenantSpecificState;
|
||||
|
||||
|
@ -139,8 +151,14 @@ public class HiLoOptimizer extends AbstractOptimizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized IntegralDataTypeHolder getLastSourceValue() {
|
||||
return noTenantGenerationState().lastSourceValue;
|
||||
public IntegralDataTypeHolder getLastSourceValue() {
|
||||
lock.lock();
|
||||
try {
|
||||
return noTenantGenerationState().lastSourceValue;
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,8 +173,14 @@ public class HiLoOptimizer extends AbstractOptimizer {
|
|||
*
|
||||
* @return Value for property 'lastValue'.
|
||||
*/
|
||||
public synchronized IntegralDataTypeHolder getLastValue() {
|
||||
return noTenantGenerationState().value.copy().decrement();
|
||||
public IntegralDataTypeHolder getLastValue() {
|
||||
lock.lock();
|
||||
try {
|
||||
return noTenantGenerationState().value.copy().decrement();
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,7 +190,13 @@ public class HiLoOptimizer extends AbstractOptimizer {
|
|||
*
|
||||
* @return Value for property 'upperLimit'.
|
||||
*/
|
||||
public synchronized IntegralDataTypeHolder getHiValue() {
|
||||
return noTenantGenerationState().upperLimit;
|
||||
public IntegralDataTypeHolder getHiValue() {
|
||||
lock.lock();
|
||||
try {
|
||||
return noTenantGenerationState().upperLimit;
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.id.enhanced;
|
|||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.id.IntegralDataTypeHolder;
|
||||
|
@ -53,18 +55,29 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized Serializable generate(AccessCallback callback) {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
public Serializable generate(AccessCallback callback) {
|
||||
lock.lock();
|
||||
try {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
|
||||
if ( generationState.lo > generationState.maxLo ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
generationState.lo = generationState.lastSourceValue.eq( 0 ) ? 1 : 0;
|
||||
generationState.hi = generationState.lastSourceValue.copy().multiplyBy( generationState.maxLo + 1 );
|
||||
if ( generationState.lo > generationState.maxLo ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
generationState.lo = generationState.lastSourceValue.eq( 0 ) ? 1 : 0;
|
||||
generationState.hi = generationState.lastSourceValue.copy().multiplyBy( generationState.maxLo + 1 );
|
||||
}
|
||||
generationState.value = generationState.hi.copy().add( generationState.lo++ );
|
||||
return generationState.value.makeValue();
|
||||
}
|
||||
generationState.value = generationState.hi.copy().add( generationState.lo++ );
|
||||
return generationState.value.makeValue();
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a lock instead of the monitor lock to avoid pinning when using virtual threads.
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private GenerationState noTenantState;
|
||||
private Map<String,GenerationState> tenantSpecificState;
|
||||
|
||||
|
@ -108,8 +121,14 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized IntegralDataTypeHolder getLastSourceValue() {
|
||||
return noTenantGenerationState().lastSourceValue.copy();
|
||||
public IntegralDataTypeHolder getLastSourceValue() {
|
||||
lock.lock();
|
||||
try {
|
||||
return noTenantGenerationState().lastSourceValue.copy();
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -124,7 +143,13 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer {
|
|||
*
|
||||
* @return Value for property 'lastValue'.
|
||||
*/
|
||||
public synchronized IntegralDataTypeHolder getLastValue() {
|
||||
return noTenantGenerationState().value;
|
||||
public IntegralDataTypeHolder getLastValue() {
|
||||
lock.lock();
|
||||
try {
|
||||
return noTenantGenerationState().value;
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.id.enhanced;
|
|||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.id.IntegralDataTypeHolder;
|
||||
|
@ -53,22 +55,32 @@ public class PooledLoOptimizer extends AbstractOptimizer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized Serializable generate(AccessCallback callback) {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
public Serializable generate(AccessCallback callback) {
|
||||
lock.lock();
|
||||
try {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
|
||||
if ( generationState.lastSourceValue == null
|
||||
|| ! generationState.value.lt( generationState.upperLimitValue ) ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
generationState.upperLimitValue = generationState.lastSourceValue.copy().add( incrementSize );
|
||||
generationState.value = generationState.lastSourceValue.copy();
|
||||
// handle cases where initial-value is less that one (hsqldb for instance).
|
||||
while ( generationState.value.lt( 1 ) ) {
|
||||
generationState.value.increment();
|
||||
if ( generationState.lastSourceValue == null
|
||||
|| ! generationState.value.lt( generationState.upperLimitValue ) ) {
|
||||
generationState.lastSourceValue = callback.getNextValue();
|
||||
generationState.upperLimitValue = generationState.lastSourceValue.copy().add( incrementSize );
|
||||
generationState.value = generationState.lastSourceValue.copy();
|
||||
// handle cases where initial-value is less that one (hsqldb for instance).
|
||||
while ( generationState.value.lt( 1 ) ) {
|
||||
generationState.value.increment();
|
||||
}
|
||||
}
|
||||
return generationState.value.makeValueThenIncrement();
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return generationState.value.makeValueThenIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a lock instead of the monitor lock to avoid pinning when using virtual threads.
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private GenerationState noTenantState;
|
||||
private Map<String,GenerationState> tenantSpecificState;
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.id.enhanced;
|
|||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.id.IntegralDataTypeHolder;
|
||||
|
@ -65,36 +67,46 @@ public class PooledOptimizer extends AbstractOptimizer implements InitialValueAw
|
|||
|
||||
|
||||
@Override
|
||||
public synchronized Serializable generate(AccessCallback callback) {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
public Serializable generate(AccessCallback callback) {
|
||||
lock.lock();
|
||||
try {
|
||||
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
|
||||
|
||||
if ( generationState.hiValue == null ) {
|
||||
generationState.hiValue = callback.getNextValue();
|
||||
// unfortunately not really safe to normalize this
|
||||
// to 1 as an initial value like we do for the others
|
||||
// because we would not be able to control this if
|
||||
// we are using a sequence...
|
||||
if ( generationState.hiValue.lt( 1 ) ) {
|
||||
log.pooledOptimizerReportedInitialValue( generationState.hiValue );
|
||||
if ( generationState.hiValue == null ) {
|
||||
generationState.hiValue = callback.getNextValue();
|
||||
// unfortunately not really safe to normalize this
|
||||
// to 1 as an initial value like we do for the others
|
||||
// because we would not be able to control this if
|
||||
// we are using a sequence...
|
||||
if ( generationState.hiValue.lt( 1 ) ) {
|
||||
log.pooledOptimizerReportedInitialValue( generationState.hiValue );
|
||||
}
|
||||
// the call to obtain next-value just gave us the initialValue
|
||||
if ( ( initialValue == -1
|
||||
&& generationState.hiValue.lt( incrementSize ) )
|
||||
|| generationState.hiValue.eq( initialValue ) ) {
|
||||
generationState.value = generationState.hiValue.copy();
|
||||
}
|
||||
else {
|
||||
generationState.value = generationState.hiValue.copy().subtract( incrementSize - 1 );
|
||||
}
|
||||
}
|
||||
// the call to obtain next-value just gave us the initialValue
|
||||
if ( ( initialValue == -1
|
||||
&& generationState.hiValue.lt( incrementSize ) )
|
||||
|| generationState.hiValue.eq( initialValue ) ) {
|
||||
generationState.value = generationState.hiValue.copy();
|
||||
}
|
||||
else {
|
||||
else if ( generationState.value.gt( generationState.hiValue ) ) {
|
||||
generationState.hiValue = callback.getNextValue();
|
||||
generationState.value = generationState.hiValue.copy().subtract( incrementSize - 1 );
|
||||
}
|
||||
}
|
||||
else if ( generationState.value.gt( generationState.hiValue ) ) {
|
||||
generationState.hiValue = callback.getNextValue();
|
||||
generationState.value = generationState.hiValue.copy().subtract( incrementSize - 1 );
|
||||
}
|
||||
|
||||
return generationState.value.makeValueThenIncrement();
|
||||
return generationState.value.makeValueThenIncrement();
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a lock instead of the monitor lock to avoid pinning when using virtual threads.
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private GenerationState noTenantState;
|
||||
private Map<String,GenerationState> tenantSpecificState;
|
||||
|
||||
|
|
Loading…
Reference in New Issue