Wednesday, August 25, 2010

System.currentTimeMillis();

I ran across an interesting issue today involving JVM internals, and how they behave differently based on the host OS.

The issue was with the following code snippet:

MyEntity a = new MyEntity();
a.setUid("foo" + System.currentTimeMillis());
a.setName("my entity");
entityService.save(a);

This was part of some object creation code I was using in a unit test, which ran successfully every time on my macbook but was failing on other's windows machines. When I found out the reason behind this, I was definitely surprised.

Believe it or not, it is more or less a known issue that the resolution of currentTimeMillis() on windows machines hovers around 10-15ms (linux and mac have a resolution of around 1ms). This was causing duplicate foreign key violations to occur.

So, as a general rule of thumb, it is a bad idea to rely on currentTimeMillis() to have a millisecond-resolution on windows, especially if you are using it to generate unique things like file names or db IDs. Looks like System.nanoTime() might tick faster.

Here are some links I found on StackOverflow about this:

On Windows, you'll more or less be limited to 10 ms resolution at best. Here's a bit from the Inside Windows NT High Resolution Timers article from TechNet:

Windows NT bases all of its timer support off of one system clock interrupt, which by default runs at a 10 millisecond granularity. This is therefore the resolution of standard Windows timers.

In my experience, using System.currentTimeMillis method gives about 15-16 ms resolution on Windows. Getting better time than the operating system timer will probably require more exotic methods.

Here's the StackOverflow question which helped me solve this issue:
http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime

At the end of the day, I decided it was simpler for my test to use a static synchronized counter variable rather than depending upon currentTimeMillis() for generating my UIDs. Another possible solution could be to use UUID.randomUUID().toString().

No comments: