Skip to content

Instantly share code, notes, and snippets.

@mflorida
Forked from mikelehen/generate-pushid.js
Last active January 1, 2025 05:26
Show Gist options
  • Save mflorida/479c6d19f8f0808fd27072ac9dadfb0c to your computer and use it in GitHub Desktop.
Save mflorida/479c6d19f8f0808fd27072ac9dadfb0c to your computer and use it in GitHub Desktop.
JavaScript code for generating Firebase Push IDs
/**
* Fancy ID generator that creates 20-character string identifiers with the following properties:
*
* 1. They're based on timestamp so that they sort *after* any existing ids.
* 2. They contain 72-bits of random data after the timestamp so that IDs won't collide with other clients' IDs.
* 3. They sort *lexicographically* (so the timestamp is converted to characters that will sort properly).
* 4. They're monotonically increasing. Even if you generate more than one in the same timestamp, the
* latter ones will sort after the former ones. We do this by using the previous random bits
* but "incrementing" them by 1 (only in the case of a timestamp collision).
*/
generatePushID = (function() {
// Modeled after base64 web-safe chars, but ordered by ASCII.
let PUSH_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
let lastPushTime = 0;
// We generate 72-bits of randomness which get turned into 12 characters and appended to the
// timestamp to prevent collisions with other clients. We store the last characters we
// generated because in the event of a collision, we'll use those same characters except
// "incremented" by one.
let lastRandChars = [];
return function() {
let now = new Date().getTime();
let duplicateTime = (now === lastPushTime);
lastPushTime = now;
let timeStampChars = new Array(8);
for (let i = 7; i >= 0; i--) {
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
// NOTE: Can't use << here because javascript will convert to int and lose the upper bits.
now = Math.floor(now / 64);
}
if (now !== 0) {
throw new Error('We should have converted the entire timestamp.');
}
let i = 0;
let id = timeStampChars.join('');
if (!duplicateTime) {
for (i = 0; i < 12; i++) {
lastRandChars[i] = Math.floor(Math.random() * 64);
}
} else {
// If the timestamp hasn't changed since last push, use the same random number, except incremented by 1.
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
lastRandChars[i] = 0;
}
lastRandChars[i]++;
}
for (i = 0; i < 12; i++) {
id += PUSH_CHARS.charAt(lastRandChars[i]);
}
if (id.length !== 20) {
let suffix = 0;
// increment last few characters
// if total length is less than 20
// this should allow use of ids with
// only alphanumeric values
while (id.length < 20) {
id += String(++suffix);
}
// throw new Error('Length should be 20.');
}
return 'i' + id;
};
})();
// console.log(generatePushID())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment