UUIDs
are nice in that they're globally unique. They have problems when it comes to storage (take up > 64 bits)
and performance (id space is random, leading to poor usage of B-trees).
See performance analysis here: https://www.percona.com/blog/2019/11/22/uuids-are-popular-but-bad-for-performance-lets-discuss/
We can, however, generate UUIDs that fit into the unsigned 64 bit integer
space and are prefixed
by timestamp to keep queries on recent data performant.
Below is a method to generate short UUIDs in the browser.
Note: we save the id as a hex string on the client. This is because a JavaScript integer cannot represent a full 64 bit unsigned value so we resort to strings on the client.
- The server would need to convert the hex to a uint_64 before saving records received from the client.
- The server would also need to convert uint_64 to a hex string when sending to JavaScript clients.
Both of the above should be relatively easy to do in GraphQL API handlers when coercing ID types.
We make use of a deviceId
below. This could be sent in a cookie from the server or it could be a browser fingerprint.
See fingerprintjs.
The implementation is based on these proposals: https://dev.mysql.com/doc/refman/5.6/en/miscellaneous-functions.html#function_uuid-short https://www.percona.com/blog/2019/11/22/uuids-are-popular-but-bad-for-performance-lets-discuss/#crayon-604006e1ec367649398457
export type SID_of<T> = string;
export type Hex = string;
let randomVariable = Math.floor(Number.MAX_SAFE_INTEGER * Math.random());
export default function sid<T>(
deviceId: Hex,
): SID_of<T> {
// 32 bits, hex
const hi32 = Math.floor(Date.now() / 1000).toString(16);
// low 16 bits of device, in hex
const partialDevice = deviceId.substr(-4);
// low 16 bits of the random variable, in hex
const random = (++randomVariable & 0xFFFF).toString(16);
const low32 = partialDevice + random;
// return the ID in hex.
return hi32 + low32;
}
example usage:
import sid from 'sid';
const new_user_id = sid<User>(deviceId);