Столкновения при генерации UUID в JavaScript?

Это относится к этому вопросу. Я использую приведенный ниже код из этого ответа, чтобы сгенерировать UUID в JavaScript:

'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
});

Это решение работало нормально, но я получаю коллизии. Вот что у меня есть:

  • Веб-приложение, работающее в Google Chrome.
  • 16 пользователей.
  • за последние 2 месяца сгенерировано около 4000 UUID.
  • Я получил около 20 столкновений - например, новый UUID, сгенерированный сегодня, был таким же, как около 2 месяцев назад (другой пользователь).

Что является причиной этой проблемы и как я могу избежать ее?

Ответ 1

Я думаю, что Math.random() по какой-то причине не работает в вашей системе (как это ни странно звучит). Это первое сообщение о столкновениях, которое я видел.

node-uuid имеет тестовый комплект, который вы можете использовать для проверки распределения шестнадцатеричных цифр в этом коде. Если это выглядит хорошо, то это не Math.random(), поэтому попробуйте заменить используемую реализацию UUID на метод uuid() и посмотрите, все ли у вас хорошие результаты.

[Обновление: только что увидел Веселин отчет об ошибке с Math.random() при запуске. Поскольку проблема возникает только при запуске, тест node-uuid вряд ли будет полезен. Более подробно прокомментирую ссылку на devoluk.com.]

Ответ 2

Действительно, есть столкновения, но только под Google Chrome. Проверьте мой опыт по теме здесь

http://devoluk.com/google-chrome-math-random-issue.html

(Ссылка не работает с 2019 года. Архивная ссылка: https://web.archive.org/web/20190121220947/http://devoluk.com/google-chrome-math-random-issue.html.)

Кажется, что столкновения случаются только при первых нескольких вызовах Math.random. Потому что, если вы просто запустите метод createGUID/testGUIDs выше (который, очевидно, был первым, что я попробовал), он просто работает без каких-либо коллизий.

Таким образом, чтобы выполнить полный тест, нужно перезапустить Google Chrome, сгенерировать 32 байта, перезапустить Chrome, сгенерировать, перезапустить, сгенерировать...

Ответ 3

Просто, чтобы другие люди могли это знать, я столкнулся с неожиданно большим количеством явных столкновений, используя упомянутую здесь технику генерации UUID. Эти столкновения продолжались даже после того, как я переключился на seedrandom для моего генератора случайных чисел. Это заставило меня вырвать мои волосы, как вы можете себе представить.

В конце концов я понял, что проблема была (почти?) исключительно связана с бот-роботами Google. Как только я начал игнорировать запросы с "googlebot" в поле user-agent, столкновение исчезло. Я предполагаю, что они должны кэшировать результаты JS-скриптов каким-то полу-интеллектуальным способом, в результате чего их браузер spidering не может рассчитывать на то, как ведут себя обычные браузеры.

Просто FYI.

Ответ 4

Я хотел опубликовать это как комментарий к вашему вопросу, но, по-видимому, StackOverflow не позволяет мне.

Я только что провел рутинный тест 100 000 итераций в Chrome, используя алгоритм UUID, который вы опубликовали, и не получил никаких столкновений. Здесь фрагмент кода:

var createGUID = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

var testGUIDs = function(upperlimit) {
    alert('Doing collision test on ' + upperlimit + ' GUID creations.');
    var i=0, guids=[];
    while (i++<upperlimit) {
        var guid=createGUID();
        if (guids.indexOf(guid)!=-1) {
            alert('Collision with ' + guid + ' after ' + i + ' iterations');
        }
        guids.push(guid);
    }
    alert(guids.length + ' iterations completed.');
}

testGUIDs(100000);

Вы уверены, что здесь что-то еще не происходит?

Ответ 5

Ответ, который первоначально опубликовал это решение UUID, был обновлен в 2017-06-28:

A хорошая статья от разработчиков Chrome обсуждение состояния Math.random PRNG в Chrome, Firefox и Safari. tl; dr - По состоянию на конец 2015 года это "неплохо", но не криптографическое. Чтобы решить эту проблему, приведена обновленная версия вышеупомянутого решения, которое использует ES6, API crypto и немного JS wizardy, я не могу взять кредит:

  function uuidv4() {
 return ([1e7] + - 1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c = >
   (c ^ crypto.getRandomValues ​​(новый Uint8Array (1)) [0] & c/4).toString(16)
 )
}

console.log(uuidv4());код>

Ответ 6

Ответы здесь имеют дело с "чем вызвана проблема?" (Chrome Math.random семян), но не "как я могу избежать этого?"

Если вы все еще ищете, как избежать этой проблемы, я написал этот ответ некоторое время назад в качестве модифицированного подхода к функции Broofa, чтобы обойти эту проблему. Он работает путем смещения первых 13 шестнадцатеричных чисел на шестнадцатеричную часть метки времени, что означает, что даже если Math.random находится в том же начальном числе, он все равно будет генерировать другой UUID, если он не будет сгенерирован в ту же миллисекунду.