Article 4T9WJ CodeSOD: To Be Random Enough

CodeSOD: To Be Random Enough

by
Remy Porter
from The Daily WTF on (#4T9WJ)

A long time ago, when I was first learning about databases, one of the points brought up was the difference between a "natural key" and a "surrogate key". A natural key was a unique identifier which already existed in your dataset, and surrogate keys were those you made up- UUIDs or sequences or what have you.

As a best practice, even if you have a viable natural key, you should still use a surrogate key. There are exceptions, but it's usually preferable to employ a database key which you control to provide identity, especially one which has no meaning- because that means it'll never need to change values.

Adam H's co-worker never got this memo.

They needed to store data about vehicles. The particular data being stored, in this case, was also time series data. So they decided that their key would be a mashup of the timestamp and the vehicle's 6-digit license plate number.

There were a number of problems with this: not every record entered into this table was actually tied directly to a vehicle, so sometimes the license plate number was blank. The developer needed to avoid any key collisions, so they decided to randomly generate a value, which was hopefully "random enough".

public final class TransformationUtil { private TransformationUtil(){} //snip private static final Random rand = new Random(); public static String setUniqKey(LocationHistory locationHistory){ SimpleDateFormat date = new SimpleDateFormat("yyyyMMddhhmmssSSSSSS"); Calendar c = Calendar.getInstance(); String str = date.format(c.getTime()); if (locationHistory.getVehicleNumber() != null) { str = (str).trim().concat(locationHistory.getVehicleNumber().trim()); } else { String value = uniqKey(); str = (str).trim().concat(value.trim()); } if (str.length() > 30) { locationHistory.setUniqKey(str.substring(0, 30)); } else { locationHistory.setUniqKey(str); } } public static String uniqKey() String randomNumber; ArrayList numbers = new ArrayList(); for(int i = 0; i < 999999; i++) { numbers.add(i + 1); } Collections.shuffle(numbers); String randomNum2 = numbers.get(rand.nextInt(numbers.size())).toString(); if(randomNum2.length() ==2){randomNumber = "0000" + randomNum2;}else if(randomNum2.length() ==1){randomNumber = "00000" + randomNum2 ;}else if(randomNum2.length() ==3){randomNumber = "000" + randomNum2 ;}else if(randomNum2.length() ==4){randomNumber = "00" + randomNum2 ;} else if(randomNum2.length() ==5){randomNumber = "0" + randomNum2;}else{randomNumber = randomNum2;} return randomNumber; }}

setUniqKey is almost not terrible. Get a date, format the timestamp. If you have a vehicle number, whack it onto the end. If the result is longer than 30 characters (because not every vehicle number is 6 characters), just chop off the excess and hope that it's unique enough.

But if you don't have a vehicle number, you need to invent one randomly. There are a lot of ways to randomly generate numbers, and this program certainly uses one of them.

The uniqKey method fills an array with every number from 1 to 999999. It then shuffles that array. Then, it randomly picks a position out of the shuffled array. Finally, it pads the array using that delightful chain of if-statements to fill the length. In a choice explicitly made to drive me nuts, the ==1 condition comes after the ==2 condition, because of course it does.

A fair number of records getting inserted needed a randomly generated key. Given that this is an incredibly inefficient way to generate random numbers, this whole block didn't perform very well. The input data was coming from a JMS queue, and any time there was a run of no-vehicle-ID records, the queue would back up and cause a series of cascading failures in other components. Each time that happened, the ops team would "solve" it by spinning up a new VM with the component running on it. More and more instances of just this component appeared, trying to keep up with the load.

Adam suggested, "If you're not going to just use a UUID for a key, why not just switch to Random.nextInt?"

"That won't be random enough," the developer replied.

proget-icon.png [Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how! TheDailyWtf?d=yIl2AUoC8zAS2IlrbWdHzs
External Content
Source RSS or Atom Feed
Feed Location http://syndication.thedailywtf.com/TheDailyWtf
Feed Title The Daily WTF
Feed Link http://thedailywtf.com/
Reply 0 comments