We have several Application Servers (=JVMs) running each of them using Hibernate-Objects with the SequenceStyleGenerator+pooled configured. In unpredictable time intervals it happens that hibernate assigns the same ID to two completely different objects which results in a UniqueConstraintViolation exception from the database. Here an example with a description where hibernate fails:
DB-Sequence setup:
start=0
increment=2
PooledOptimizer.generate() with 2 threads (first assignment of hiValue/value):
JVM-1 JVM-2
value=0=callback.nextval
value=2=callback.nextval
hiValue=4=callback.nextval
hiValue=6=callback.nextval
The problem's cause is in the PooledOptimizer.generate: when it initializes
the value+hiValue for the first time it invokes callback.nextValue() twice which
may provide values that do not belong to each other. The reason is that
between the assignment of "value" and "hiValue" another JVM can retrieve a
DB sequence value from the callback which leads to an inconsistent "value" and "hiValue"
relation (see example above).
A fix that works for multiple JVMs would be to invoke the "callback.getNextValue()" maximum once
per "optimizer.generate()" call:
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue < 0 ) {
value = callback.getNextValue();
hiValue = value + incrementSize;
}
else if ( value >= hiValue ) {
value = callback.getNextValue();
hiValue = value + incrementSize;
}
return make(value++);
}
I attached a testcase that prooves the described problem (you can see that the IDs "2" and "3" are assigned two times).
I would be very thankful if this problem could be fixed very soon since it is a showstopper which
occurs very unpredictably.
Hibernate 3.2.6, Oracle (any version)
Also I have studied some JBoss logs that suggest that optimizers are not just created during startup of the server. From what I have seen optimizers are can also be created after some error resulting in an exception. Now I could be wrong about this so please verify yourself, but if so this means that the current code is riskier that one might first think particularity in i clustered environment.
Bjorn, the optimizer is created when the generator it is part of is "configured". That process happens once per generator instance. The double call happens the first time the pooled optimizer is used, which is the first time the generator is called. Unless the error somehow rebuilds the SessionFactory I don't see how what you say happens happens.
See "fixed by" links
That code is much better, but still assumes two values retrieved from the sequence to be consecutive. This only happens initially though so it is unlikely to a problem, at least not for long.
As I said I am ok with a potential for a "split read". We have minimized the likelihood of these "split reads" by minimizing when/where the double read occurs to a very narrow circumstance. Am I totally fine with that. If you are not, then
use the new strategy I just introduced (HHH-5218) to interpret the sequence value as the range lower bound, or
use a custom optimizer that does what you want, or
trick it by starting your sequence off at something greater-than-or-equal-to the increment size.