Snowflake
GameVox возвращает ID в формате 64-битных snowflake Discord везде, где это сделал бы API Discord. Раскладка битов, порядок сортировки и формулы извлечения временной метки идентичны, так что ваш существующий код разбора snowflake работает без изменений.
Формат
Такой же, как в Discord: 42-битная временная метка + 5-битный worker + 5-битный process + 12-битный инкремент, big-endian внутри 64-битного беззнакового целого.
┌─ 42 bits ─────────────────┬─ 5 ─┬─ 5 ─┬─ 12 ────────┐
│ мс от эпохи GameVox │ wkr │ prc │ increment │
└───────────────────────────┴─────┴─────┴─────────────┘
63 22 21 17 16 12 11 0 Каждый сегмент совпадает с Discord:
- Временная метка (42 бита): миллисекунды от эпохи GameVox, см. ниже.
- Worker ID (5 бит): назначается каждой запущенной задаче api при старте через атомарный счётчик в Redis.
- Process ID (5 бит): в паре с worker образует уникальный пул генерации.
- Инкремент (12 бит): последовательность на каждую миллисекунду для каждой пары (worker, process).
Эпоха
GameVox использует ту же эпоху, что и Discord: 1420070400000 (2015-01-01 00:00:00 UTC). Парсер snowflake, написанный для Discord, работает с ID GameVox без изменений: никакую константу менять не нужно.
const SNOWFLAKE_EPOCH = 1420070400000n; Формат передачи
Каждый snowflake в REST и шлюзе сериализуется как строка JSON, а не число. JavaScript теряет точность выше Number.MAX_SAFE_INTEGER (253-1), а snowflake регулярно превышает это значение. Если нужны вычисления, всегда разбирайте через BigInt, а в остальных местах храните как строку.
// Правильно
const id = BigInt(message.id);
// Неправильно: тихая потеря точности
const id = Number(message.id); Извлечение временной метки
JavaScript
function snowflakeToDate(snowflake) {
const EPOCH = 1420070400000n; // как в Discord
const ms = Number((BigInt(snowflake) >> 22n) + EPOCH);
return new Date(ms);
}
console.log(snowflakeToDate('182955831900192769'));
// → объект Date: когда был создан этот snowflake Python
from datetime import datetime, timezone
SNOWFLAKE_EPOCH = 1420070400000
def snowflake_to_dt(snowflake):
ms = (int(snowflake) >> 22) + SNOWFLAKE_EPOCH
return datetime.fromtimestamp(ms / 1000, tz=timezone.utc) Java
long SNOWFLAKE_EPOCH = 1420070400000L;
long ms = (Long.parseUnsignedLong(snowflake) >>> 22) + SNOWFLAKE_EPOCH;
Instant when = Instant.ofEpochMilli(ms); Порядок сортировки
Поскольку временная метка находится в старших битах, лексикографическое сравнение двух snowflake как строк упорядочивает их хронологически, как и в Discord. Поэтому пагинация before / after / around в эндпоинте сообщений работает без отдельного поля с временной меткой.
// В массиве сообщений, от старых к новым:
messages.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0); Собственные данные и таблица псевдонимов
Внутри собственные объекты GameVox (серверы, каналы, пользователи, сообщения и т. д.) используют UUIDv4. Слой совместимости для ботов создаёт стабильный snowflake при первом показе объекта боту и сохраняет эту пару в таблице snowflake_aliases. Все последующие обращения к этому объекту, через REST или шлюз, используют тот же snowflake.
Для сообщений snowflake заполняются задним числом с created_at сообщения в качестве исходной временной метки, так что старые сообщения правильно сортируются, когда бот впервые заходит в канал. Для всего остального псевдоним создаётся лениво при первом показе с NOW() в качестве временной метки, поэтому snowflake отражает момент, когда бот увидел объект, а не обязательно момент его создания.
Хеш для шардинга
Стандартная формула шардов Discord работает как есть:
shardId = (BigInt(guildId) >> 22n) % BigInt(numShards); Боты, у которых меньше 2500 серверов, работают на одном шарде ([0, 1]), и им не нужно об этом думать. Более крупные боты получают такое же почти равномерное распределение хеша, как в Discord, потому что старшие 42 бита монотонно растут со временем, а не случайны.
Подводные камни
- Не сравнивайте snowflake через
==с числовым литералом. При передаче это строки: сравнивайте со строкой или приводите оба значения кBigInt. - 42-битная временная метка переполнится примерно через 139 лет после эпохи. Snowflake GameVox будут действительны до 2165 года.
- Не определяйте время создания сервера, канала или роли по snowflake. Собственные объекты, появившиеся до платформы ботов, получили snowflake при первом показе боту, а не при создании. Исключение составляют сообщения (заполнены по
created_at).