Created
May 7, 2010 14:04
-
-
Save idmillington/393456 to your computer and use it in GitHub Desktop.
A function for returning a UUID (of any kind) in its canonical string form from within the node.js server-side javascript system. It uses a command-line OSSP uuid program to generate the UUIDs and caches them to avoid constantly spawning new processes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Generates a new UUID and passes it to the given callback function. | |
* | |
* Uses the a command-line uuid generator. Caches UUIDs to avoid | |
* unneccessary spawning of new processes. | |
* | |
* You can easily adjust the script used to create the UUID. By | |
* default I am using the OSSP uuid program, which is available in | |
* most linux distro's package managers (e.g. `sudo apt-get install | |
* uuid` on ubuntu). | |
* | |
* Callback signature is function(err, uuid). | |
* | |
* Credits: | |
* Ian Millington | |
* Steve Brewer | |
*/ | |
var createUUID = (function() { | |
// We create and execute this outside function immediately. Doing | |
// this provides us with a secure inner scope to store our uuid | |
// cache, preventing other code from accessing it. The result of | |
// this outmost function call is to return the actual get_uuid() | |
// function which is assigned to the createUUID name. | |
// Adjust these constants to tweak the uuid generation process. | |
// This version uses the OSSP uuid program, but you could also | |
// replace the script with your own uuidgen wrapper as per | |
// http://www.redleopard.com/2010/03/bash-uuid-generator/ | |
var UUID_SCRIPT = "uuid"; | |
var UUID_ARGS = ['-v', 4 /* uuid version */, | |
'-n', 100 /* per call */]; | |
// Alias the spawn function for our cache top-up routine. | |
var spawn = require('child_process').spawn; | |
// The uuid stack. Pop UUIDs from this as they are needed. | |
var uuids = []; | |
// Track if we're currently generating, so we don't spawn new UUID | |
// generators if one is pending. | |
var spooledCallbacks = []; | |
var generating = false; | |
// Handle error notification of the spooled callbacks. | |
var notifyCallbacksOfError = function(error) { | |
while (spooledCallbacks.length > 0) { | |
spooledCallbacks.pop()(error, null); | |
} | |
} | |
// Updates the cache with another batch of UUIDs. | |
var topUpCache = function() { | |
// Create the shell call to uuid. | |
var uuidCall = spawn(UUID_SCRIPT, UUID_ARGS); | |
// When data arrives split it and cache it. | |
uuidCall.stdout.addListener('data', function(data) { | |
var result = data.toString(); | |
// Strip whitespace from start and end of the data. | |
result = result.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); | |
// Assume we got one UUID per line. | |
var uuidsReturned = result.split('\n'); | |
uuids = uuids.concat(uuidsReturned); | |
}); | |
// Pass errors up to the spooled callbacks, these are then | |
// popped, so they receive at most one error. | |
uuidCall.stderr.addListener('data', function(data) { | |
notifyCallbacksOfError(new Error("uuid generation error: "+data)); | |
}); | |
// If we're done, call the callback with the uuid. | |
uuidCall.addListener('exit', function(code) { | |
if (code != 0) { | |
notifyCallbacksOfError(new Error("uuid generation failed")); | |
} else { | |
// Send as many UUIDs as we can. | |
while(spooledCallbacks.length > 0 & uuids.length > 0) { | |
spooledCallbacks.pop()(null, uuids.pop()); | |
} | |
if (spooledCallbacks.length > 0) { | |
// We didn't have enough uuids, so top up again. | |
topUpCache(); | |
} else { | |
// We're done. | |
generating = false; | |
} | |
} | |
}); | |
}; | |
// Calls the given function with a UUID when one is calculated. | |
// Callback signature is function(err, uuid). | |
return function(callback) { | |
if (uuids.length > 0) { | |
// We can immediately send back a UUID. | |
callback(null, uuids.pop()); | |
} else { | |
// We need to top up our cache before notifying the callback. | |
spooledCallbacks.unshift(callback); | |
// Check if we need to start a new cache update. If not, | |
// then just adding the callback to the spooled list will | |
// mean it gets called when UUIDs are available. | |
if (!generating) { | |
generating = true; | |
topUpCache(); | |
} | |
} | |
}; | |
})(); | |
// Delete this line if you just want the function in your own code, | |
// add it if you want to use this file as a module and require() it | |
// into your program. | |
exports.createUUID = createUUID; |
Thanks a lot Steve, yes, in my application UUID requests were rare so I didn't notice this obvious problem. Your solution works well.
Now I need to work out if I can pull your changes back here.
I've merged your changes in and done some additional work on tidying it up. I didn't pull your changes directly, because I wanted to keep the excessive comments in this bit of code, so GIST browsers can see what's going on.
Changed the callback registration to use unshift rather than push, so that we have a FIFO queue for pending uuids.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
forked: http://gist.github.com/394936