optimizers for enhanced id generators should be synchronized against multi-threaded access

Description

Currently the optimizers do not serialize access to their internal state when applying their optimizations. This only affects the pooled and hilo optimizers.

Environment

None

Activity

Show:
MaciejM
December 12, 2007, 8:57 PM

Here is improved version of the NoopOptimizerTest (previous one was a little be unstable..)

MaciejM
December 12, 2007, 9:05 PM

Here is a fixed version of NoopOptimizer generate() method. As you may notice the trick is to use a local method variable. This is better then adding the synchronized clause for the whole method - as this would cause scalability problems in the case when there is latency on database connection.

public Serializable generate(AccessCallback callback) {

//copy the value to local variable to avoid race condition
long localLastSourceValue = lastSourceValue;

if (localLastSourceValue == -1) {
while (localLastSourceValue <= 0) {
localLastSourceValue = callback.getNextValue();
}
} else {
localLastSourceValue = callback.getNextValue();
}

lastSourceValue = localLastSourceValue;

return make(localLastSourceValue);
}

Sorry that it is not a formal patch but I am not sure what version of the OptimizerFactory it should apply to

John S. Adair
May 28, 2008, 12:29 AM

I notice the checked in fix synchronizes the two methods. We're on 3.2.5 and can't take the newest drop yet, so I made the change and built hibernate locally to see if it solved our problems. It did solve the duplicate key problems but led to an apparent deadlock. I'm not filing a new ticket because I can't swear this isn't a problem on our end and don't have a simple reproducible test case, but here are the stack traces for what it's worth:

"pool-1-thread-8" prio=6 tid=0x0337a008 nid=0x1738 waiting for monitor entry [0x04f1e000..0x04f1fc68]
at org.hibernate.id.enhanced.OptimizerFactory$PooledOptimizer.generate(OptimizerFactory.java:172)

  • waiting to lock <0x23d46dd0> (a org.hibernate.id.enhanced.OptimizerFactory$PooledOptimizer)
    at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:157)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:99)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
    at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:269)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:217)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:170)
    at org.hibernate.engine.Cascade.cascade(Cascade.java:131)
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:431)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:265)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
    ...

"pool-1-thread-7" prio=6 tid=0x02d8f008 nid=0x668 in Object.wait() [0x04ede000..0x04edfce8]
at java.lang.Object.wait(Native Method)

  • waiting on <0x23d34768> (a org.apache.commons.pool.impl.GenericObjectPool)
    at java.lang.Object.wait(Object.java:474)
    at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:748)
    - locked <0x23d34768> (a org.apache.commons.pool.impl.GenericObjectPool)
    at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:95)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:540)
    at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:81)
    at org.hibernate.jdbc.AbstractBatcher.openConnection(AbstractBatcher.java:558)
    at org.hibernate.engine.transaction.Isolater$JdbcDelegate.delegateWork(Isolater.java:178)
    at org.hibernate.engine.transaction.Isolater.doIsolatedWork(Isolater.java:43)
    at org.hibernate.engine.TransactionHelper.doWorkInNewTransaction(TransactionHelper.java:51)
    at org.hibernate.id.enhanced.TableStructure$1.getNextValue(TableStructure.java:72)
    at org.hibernate.id.enhanced.OptimizerFactory$PooledOptimizer.generate(OptimizerFactory.java:173)
    - locked <0x23d46dd0> (a org.hibernate.id.enhanced.OptimizerFactory$PooledOptimizer)
    at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:157)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:99)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
    at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:269)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:217)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:170)
    at org.hibernate.engine.Cascade.cascade(Cascade.java:131)
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:431)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:265)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
    ...

Laszlo Sas
February 22, 2010, 1:37 PM

Optimizers are not thread-safe. (Used version core 3.3.2)

NoopOptimizer:

1) public Serializable generate(AccessCallback callback) must be synchronized.

Fix: public synchronized Serializable generate(AccessCallback callback)

2) public long getLastSourceValue()

Fix: public synchronized long getLastSourceValue()

HiLoOptimizer:

1) public long getLastSourceValue()

Fix: public synchronized long getLastSourceValue()

2) public long getLastValue()

Fix: public synchronized long getLastValue()

3) public long getHiValue()

Fix: public synchronized long getHiValue()

PooledOptimizer:

1) public long getLastSourceValue()

Fix: public synchronized long getLastSourceValue()

2) public long getLastValue()

Fix: public synchronized long getLastValue()

Would you be so kind as to apply these modifications?

Thanks,

Laszlo

Steve Ebersole
March 21, 2011, 7:08 PM

Bulk closing stale resolved issues

Assignee

Steve Ebersole

Reporter

Steve Ebersole

Fix versions

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure