Hibernation generating duplicate UUIDs
We have 6 node Red Hat 4.4.7 / Linux 2.6.32, each node runs a Java application that creates records in the central Oracle database using Hibernate 3.3.2.GA.
We ran into an issue where Hibernate generates duplicate UUIDs.
The Java class in question is defined as follows:
@Entity
@Table(name = "X_Y")
@GenericGenerator(name = "x-y-uuid", strategy = "uuid")
public class XY implements ... {
@Id
@Column(name = "X_Y_ID")
@GeneratedValue(generator = "x-y-uuid")
private String id;
...
}
Using this definition, which we have used successfully for a while, we ran into duplicate X_Y_ID keys. We disabled the unique limit on X_Y_ID and restarted the process. In the meantime, we started digging into possible errors in our code, as well as in Hibernate code. Reading Hibernate UUIDHexGenerator
it would appear that the first 8 characters of the UUID are based on the IP address of the machine, while the second 8 characters are the JVM start time.
After completing the process with the unique restriction on X_Y_ID disabled, we performed some analysis of the received UUIDs. We found that there were actually 59 identical X_Y_ID values. To our surprise, the request:
select SUBSTR(X_Y_ID,1,8), COUNT(*)
from X_Y
group by SUBSTR(X_Y_ID,1,8)
it is indicated that all 6 cars have the same first 8 characters. Request:
select SUBSTR(X_Y_ID,9,8), COUNT(*)
from X_Y
group by SUBSTR(X_Y_ID,9,8)
gave
"49d99de6" 2148309
"49d99e3c" 2044966
"49d99def" 2228095
"49d99df2" 2091068
"49d99dee" 4110661
As you can see, there are 5 lines with the last one, which is about twice the number of lines. This in itself is not surprising. (All this means that the JVM on two different machines starts up within 256ms of each other).
Further investigation revealed that the value generated for the first eight characters ff808081
corresponds to IP address 127.0.0.1, localhost.
Running ifconfig
on one of these machines gives (as an example):
eth0 Link encap:Ethernet HWaddr 00:50:56:81:2C:20
inet addr:10.191.8.50 Bcast:10.191.63.255 Mask:255.255.192.0
inet6 addr: fe80::250:56ff:fe81:2c20/64 Scope:Link
...
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
...
My questions:
- How is it possible that the IP address seen by Hibernate is 127.0.0.1 and not 10.191.8.50?
- What can we do to prevent this on a deployed system?
source to share
As noted in the comments @thatotherguy, Hibernate implementation AbstractUUIDGenerator
and UUIDHexGenerator
quite far from RFC-4122 compliance. I never really realized how bad the implementation was until I looked more closely.
As an aside, taking its implementation for what it is, the root cause of your problem here comes down to UUIDHexGenerator
s use InetAddress.getLocalHost()
(via AbstractUUIDGenerator
) to get a "unique" value. If a lookup for your hostname name results in 127.0.0.1 (for example its in your file /etc/hosts
), or if the hostname is "local", that's what it will use.
You have several options:
-
You can update
/etc/hosts
to contain the LAN IP for your hostname if this is an option. However, you will not be using the correct UUIDs (same caveat as the last part of the next point). -
If the Hibernate algorithm is not sufficient, you can define a custom one
IdentifierGenerator
and provide a better UUID generation algorithm that is more suitable for your task. I would base this on embedded JavaUUID
that is compliant. However, you could hack it by extendingUUIDHexGenerator
and redefiningprotected int getIP()
it to return the exact IP address. This is a hack due toAbstractUUIDGenerator
implementation (yoursgetIP()
will no longer return the value of its instance fieldIP
) and therefore it will still not be the correct UUID. This may be sufficient, but I do not recommend it. -
Instead of using a generator, specify the ID assignment manually and generate the UUID yourself. Again Java
UUID
might work for you here. -
There is a new UUID generator strategy, "uuid2", which uses
UUIDGenerator
. This was new in 3.6, not available in 3.3.2. Source for is available . I have not used this strategy before and cannot speak for it; however, as Andrew Stein pointed out in the comments below, checking the source shows that it provides a strategy built around JavaUUID
, making it likely to be a good bet and certainly better than the older optionsAbstractUUIDGenerator
.
Option 1 is the simplest quick fix if it works for you but may have maintenance / deployment issues and, again, doesn't actually generate well-formed UUIDs. Ultimately, option 2 using UUID
(or option 4 with an appropriate strategy) is probably the most correct.
There is an article describing various strategies for assigning UUIDs for Hibernate , which may contain more useful information and examples.
source to share