Blog

How-To: Generating Universally Unique Identifiers (UUIDs) at the Edge

September 9, 2019 · by Daniel Kenzelmann ·

As a developer or admin, there are certain situations when you need to generate a unique identifier for an object. Of course, you could just create a random string to do this, but the better and more popular way is to assign a universally unique identifier (UUID) to the object. (Note that UUIDs are also sometimes known as GUIDs or globally unique identifiers.)

Generating and assigning UUIDs can be done at the origin, but this approach unfortunately doesn’t work if the objects you need to ID are served from cache at the Edge. In these instances, you can instead generate and assign UUIDs at the Edge — specifically, we’re referencing Akamai’s 240,000-server intelligent edge platform — and by doing so you’ll improve performance and improve your end-user experience.

In this blog post, we’ll help you do that by covering these topics: 

  • The two main UUID formats: UUIDv4 and UUIDv5
  • The basics of UUIDv4
  • The basics of UUIDv5
    • How to generate UUIDv5s with Akamai’s Property Manager
    • How to implement UUIDv5s with Property Manager
    • How to implement UUIDv5s using the Property Manager API (PAPI)

The two main UUID formats: UUIDv4 and UUIDv5

When you adhere to UUID specs defined in RFC 4122, you can use convenient and standardized tools and methods that are designed for handling UUIDs, and that lets you work with both random-number UUIDs (these are the version 4 format, or UUIDv4)  and namespace-based UUIDs (these are the version 5 format, or UUIDv5).

Per RFC 4122 section 4.4, UUIDv4 is meant for generating UUIDs from truly random or pseudo-random numbers, while UUIDv5 IDs are derived from a namespace (for example, a URL) as per RFC 4122 section 4.3.

Now let’s take a deeper look at each of these two formats, including when and how to use them.

The basics of UUIDv4 

If you’ve looked at a UUID/GUID, you probably know the basic format: it’s five segments of seemingly random hex data, beginning with eight hex characters, followed by three four-character strings, then 12 characters at the end. These segments are separated by a “-”.

Throughout this post, we’ll refer to these as segment 1, segment 2, segment 3, segment 4, and segment 5.

Here’s what the five segments look like in a sample UUID:

segments

In a random string such as this, we would have 32 hex characters, each with four bits of possibilities, for a total of 128 bits of random data. Then we could create and format this “pseudo UUID” (it’s “pseudo” because it is not a "real" UUID as defined in the RFC 4122 specs) using the “Hex Random” generator function in Property Manager’s “Set Variable” behavior menu:

set variable

However, RFC 4122 specifies fixed values of six bits: Specifically, the first four bits of the third segment and the first two bits of the fourth segment, so we are left with only 122 random bits. So, as you can see, generating a purely random string will not create an RFC 4122-compliant UUID; we’ll have to specifically create a UUIDv4 or UUIDv5.

How to generate UUIDv4s with Akamai’s Property Manager

First, let’s look at the obvious way to generate UUIDv4s with Akamai’s Property Manager.

Note: In the later section about namespace-based UUIDs (UUIDv5), we’ll use some less-obvious techniques to simplify the UUID-generation process with Property Manager, and it will then be easy for you to see how to use those same techniques for UUIDv4.

Okay, here we go. The first 12 hex characters (Segments 1 and 2) and the last 12 hex characters (Segment 5) are very simple: all of those characters should be random data so, just like in the example above, we can use the “Hex Random” generator function in Property Manager’s “Set Variable” behavior. 

So that will generate characters for Segments 1, 2, and 5. But what about Segments 3 and 4? (As noted above, RFC4122 specifies fixed values for the first four bits of Segment 3 and the first two bits of Segment 4, so the “Hex Random” function won't work with these segments.

The answer is to simply create the necessary random data for the individual non-fixed segments (i.e., Segments 1, 2, and 5) of the UUID, then add the fixed bits from Segments 3 and 4 to it.

So, then, what are the values of the four fixed bits in Segment 3 of the UUID? The four fixed bits correspond to the UUID version number “4” (binary value 0100). If we look at Segment 3 in binary (i.e., 0100 0000 0000 0000, with the fixed bits noted via bold italic), we can see what value corresponds here when looking at the whole segment:

binary hex

What we would do next is generate the green parts as random bits, then just add the red number. Fortunately, Property Manager can return a random value within a specific range: in this case, from 0000 0000 0000 000 to 0000 1111 1111 1111, or in decimal from 0 to 4095.

So the three-step Property Manager formula for Segment 3 of the UUID is: 

  1. Create a random number from 0 to 4095
  2. Add 16384
  3. Convert to hexadecimal

The same principle applies to Segment 4, but here, only two bits are fixed (1000 0000 0000 0000, with the fixed bits noted via bold italic), resulting in a fixed value of 32768 and a random number between 0 and 16383. Adding both numbers (i.e., the fixed value of 32768 plus the random number between 0 and 16383) and converting the sum to hexadecimal gives us the final value for Segment 4.

Finally, we concatenate all five segments, separate them with dashes, and store the resulting string in a local variable for further use.

How to implement UUIDv4s with Property Manager

Now that you’ve generated a random-number-based UUIDv4 via the steps noted above, let’s go over how to implement that UUID using Property Manager.

First, create (temporary) variables for the five segments:

temp variables

Then, using the “Set Variable” behavior in Property Manager that was shown above, create the individual parts (note how Segments 3 and 4 differ from Segments 1, 2, and 5 for the reasons noted above):

Segment 1:

segment 1

Segment 2:

segment 2

Segment 3:

segment 3

Segment 4:

segment 4

Segment 5:

segment 5

Finally, combine the segments:

combined segments

Now you can use the PMUSER_UUID variable to implement your UUID throughout the rest of the property.

How to implement UUIDv4s using the Property Manager API (PAPI)

If you have more than a handful of properties in your account, using APIs gives you easier management and control of them. If you’re already familiar with using APIs, you can use the code block below in PAPI to achieve the same functionality you’d get with Property Manager.

Note: If you’re not familiar with Akamai APIs (such as PAPI), check out Akamai’s full API catalog and how to get started with Akamai APIs.

To implement UUIDv4s with PAPI, insert this code block:

{

"name": "Create UUID",

"children": [],

"behaviors": [

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART1",

"valueSource": "GENERATE",

"transform": "NONE",

"generator": "HEXRAND",

"numberOfBytes": 4

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART2",

"valueSource": "GENERATE",

"transform": "NONE",

"generator": "HEXRAND",

"numberOfBytes": 2

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART3",

"valueSource": "GENERATE",

"transform": "ADD",

"generator": "RAND",

"minRandomNumber": "0",

"maxRandomNumber": "4095",

"operandOne": "16384"

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART3",

"valueSource": "EXPRESSION",

"transform": "DECIMAL_TO_HEX",

"variableValue": "{{user.PMUSER_UUID_PART3}}"

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART4",

"valueSource": "GENERATE",

"transform": "ADD",

"generator": "RAND",

"minRandomNumber": "0",

"maxRandomNumber": "16383",

"operandOne": "32768"

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART4",

"valueSource": "EXPRESSION",

"transform": "DECIMAL_TO_HEX",

"variableValue": "{{user.PMUSER_UUID_PART4}}"

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID_PART5",

"valueSource": "GENERATE",

"transform": "NONE",

"generator": "HEXRAND",

"numberOfBytes": 6

}

},

{

"name": "setVariable",

"options": {

"variableName": "PMUSER_UUID",

"valueSource": "EXPRESSION",

"transform": "NONE",

"variableValue": "{{user.PMUSER_UUID_PART1}}-{{user.PMUSER_UUID_PART2}}-{{user.PMUSER_UUID_PART3}}-{{user.PMUSER_UUID_PART4}}-{{user.PMUSER_UUID_PART5}}"

}

}

],

"criteria": [],

"criteriaMustSatisfy": "all",

"comments": "This code creates a random UUID and stores it on a predefined user variable called PMUSER_UUID. Use the value on this variable for any logic needed.  For example, inject it in a cookie or a header. Note: First 4 bits of segment 3 and first 2 bits of segment 4 are fixed  per RFC 4122"

The basics of UUIDv5

Now that you know how a UUIDv4 is generated, let’s look at namespace-based UUIDs (v5). UUIDv5s are basically a hashing of the name that the UUID is based on (in this case the URL). This is what makes UUIDv5 different from UUIDv4, which, as we have seen, is based on random numbers rather than namespace.

How to generate UUIDv5s with Akamai’s Property Manager

Namespace-based UUIDs are generated via the following logic in Property Manager:

  • First, take the namespace ID as defined in the RFC. For example, per RFC, the namespace ID for a URL is “6ba7b811-9dad-11d1-80b4-00c04fd430c8”.
  • Then, append the URL to the namespace ID. For example, you would append “https://www.example.com/test/” to the above namespace ID to get this string: “6ba7b811-9dad-11d1-80b4-00c04fd430c8https://www.example.com/test/
  • Then, you’d hash this string using SHA-1 which would give you a string that is already nearly complete; just replace the character at position 13 with a “5” (the UUID version number) and set the first two bits of the character at position 17 (more on this below). Once that’s done, you’ll have a complete v5 UUID.

Of course, for this last step you could work with the bitwiseAND and OR operations, or you could do some modulo math (to get the lower bits and then add the higher ones) to set the correct bits there. But you can also just replace the necessary characters!

Now, let’s look at how to set the first two bits of the character at position 17. First, we’ll examine the 16 possible bit values for the 4 bits of the single character at position 17, and how you would have to alter them to have the first two bits as “10” (noted in bold italic) and keep the value of the second two bits:

0000 = 0   ➝   1000 = 8

0001 = 1   ➝   1001 = 9

0010 = 2   ➝   1010 = a

0011 = 3   ➝   1011 = b

0100 = 4   ➝   1000 = 8

0101 = 5   ➝   1001 = 9

0110 = 6   ➝   1010 = a

0111 = 7    ➝    1011 = b

1000 = 8    ➝    1000 = 8

1001 = 9    ➝    1001 = 9

1010 = a    ➝    1010 = a

1011 = b    ➝    1011 = b

1100 = c    ➝    1000 = 8

1101 = d    ➝    1001 = 9

1110 = e    ➝    1010 = a

1111 = f    ➝    1011 = b

By replacing any item on the left (e.g., 0000 = 0) with its corresponding item on the right (e.g., 1000 = 8), we are setting the fixed bits.

But how can you do such a replacement without adding 16 separate behaviors with matches in Property Manager? Here’s how: we’ll use a Property Manager function that is normally used to extract values for specific parameters (for example, the request parameters of the calling URL). To use this function, we simply set a fixed list of parameter names with values that correspond to the table above, then instruct Property Manager to extract the value based on the parameter name (the parameter name being the original value).

How to implement UUIDv5s with Property Manager

Now that we’ve discussed how to generate a UUIDv5 with Property Manager, let’s look at six steps to implement a UUIDv5 with Property Manager.

  1. Set some variables, including the fixed initial value for the URL namespace ID:set variables

     

  2. Concatenate the namespace ID with the URL, and create a SHA-1 hash over it.
  3. Put the character at position 17 into its own variable.
  4. Use the technique mentioned above to extract a value based on the current value of the character.
  5. Select the correct characters from the SHA-1 hash, add the version number “5” and the newly calculated character, and you’ve created the UUID.
  6. Finally, make the UUID all lower case (the SHA-1 function in the first behavior creates upper case characters).

variable nameset variable 2set variable 3

How to implement UUIDv5s using the Property Manager API (PAPI)

In addition to implementing UUIDv5s with Property Manager, you can also implement them with PAPI by inserting this code block:

 

  {

                    "name": "Create UUID v5",

                    "children": [],

                    "behaviors": [

{
                        "name": "setVariable",
                        "options": {
                            "variableName": "PMUSER_UUID",
                            "valueSource": "EXPRESSION",
                            "transform": "SHA_1",
                            "variableValue": "{{user.PMUSER_UUID_NS_URL}}{{builtin.AK_URL}}"
                        }
                    },
                    {
                        "name": "setVariable",
                        "options": {
                            "variableName": "PMUSER_UUID_FIXED",
                            "valueSource": "EXPRESSION",
                            "transform": "SUBSTRING",
                            "variableValue": "{{user.PMUSER_UUID}}",
                            "startIndex": "16",
                            "endIndex": "17"
                        }
                    },
                    {
                        "name": "setVariable",
                        "options": {
                            "variableName": "PMUSER_UUID_FIXED",
                            "valueSource": "EXPRESSION",
                            "transform": "EXTRACT_PARAM",
                            "variableValue": "0=8/1=9/2=A/3=B/4=8/5=9/6=A/7=B/8=8/9=9/A=A/B=B/C=8/D=9/E=A/F=B",
                            "paramName": "{{user.PMUSER_UUID_FIXED}}",
                            "separator": "/",
                            "caseSensitive": true
                        }
                    },
                    {
                        "name": "setVariable",
                        "options": {
                            "variableName": "PMUSER_UUID",
                            "valueSource": "EXPRESSION",
                            "transform": "SUBSTITUTE",
                            "variableValue": "{{user.PMUSER_UUID}}",
                            "regex": "(.{8})(.{4}).(.{3}).(.{3})(.{12}).*",
                            "replacement": "$1-$2-5$3-{{user.PMUSER_UUID_FIXED}}$4-$5",
                            "caseSensitive": true,
                            "globalSubstitution": false
                        }
                    },
                    {
                        "name": "setVariable",
                        "options": {
                            "variableName": "PMUSER_UUID",
                            "valueSource": "EXPRESSION",
                            "transform": "LOWER",
                            "variableValue": "{{user.PMUSER_UUID}}"
                        }
                    }

                    ],

"criteria": [],

 "criteriaMustSatisfy": "all",

 "comments": "This code creates a name based UUID (v5) and stores it on a predefined user variable called PMUSER_UUID. Use the value on this variable for any logic needed like for example inject it in a cookie or a header. Note: First 4 bits of part 3 and first 2 bits of part 4 are fixed as per RFC 4122"

}

 

Conclusion

In general, any activity you do at the Edge can ease the load on your origin and deliver a faster and better experience for your end users. Generating and assigning UUIDs in this way is just one more excellent example of the impressive power of the Edge.

Daniel Kenzelmann is a technical project manager at Akamai Technologies.

Acknowledgments to Rory Hewitt (senior solutions architect at Akamai) for providing the regex code that generates the pseudo-UUID and the idea for how to create the v5 UUID, and to Javier Garza (developer evangelist at Akamai) for providing the JSON code shown in this blog post.