Инженер из команды Heroku API здесь: мы пошли с самым простым подходом к генерации имен приложений, который в основном и предлагал: хранить массивы прилагательных и существительных в памяти, произвольно выбирать элемент из каждого и комбинировать его с случайное число от 1000 до 9999.
Не самый захватывающий код, который я написал, но интересно посмотреть, что нам нужно было сделать, чтобы масштабировать это:
Сначала мы выбирали имя, пытались INSERT
, а затем спасали ошибку ограничения уникальности, чтобы выбрать другое имя. Это работало нормально, когда у нас был большой пул имен (и небольшой набор приложений, использующих их), но в определенном масштабе мы начали замечать много коллизий во время генерации имен.
Чтобы сделать его более устойчивым, мы решили выбрать несколько имен и проверить, какие из них по-прежнему доступны с помощью одного запроса. Очевидно, нам по-прежнему нужно проверять наличие ошибок и повторять попытки из-за условий гонки, но с таким количеством приложений в таблице это явно более эффективно.
Это также имеет дополнительное преимущество, заключающееся в том, что мы можем легко получать оповещения, если в нашем пуле имен мало (например, если выбрано 1/3 случайных имен, отправьте оповещение).
В первый раз, когда у нас возникли проблемы со столкновениями, мы просто радикально увеличили размер нашего пула имен, перейдя с 2 цифр до 4. С 61 прилагательным и 74 существительными это подняло нас от ~ 400k до ~ 40mi имен (61 * 74 * 8999
).
Но к тому времени, когда мы запустили 2 миллиона приложений, мы снова начали получать оповещения о столкновениях, причем с гораздо большей скоростью, чем ожидалось: примерно половина имен сталкивалась, что не имело смысла, учитывая размер и объем нашего пула запущенных приложений.
Виновник, как вы могли догадаться, заключается в том, что rand
довольно плохой генератор псевдослучайных чисел . Выбор случайных элементов и чисел с помощью SecureRandom
радикально уменьшил количество столкновений, сделав его таким, как мы ожидали.
Из-за огромного труда, необходимого для масштабирования этого подхода, нам пришлось спросить, есть ли лучший способ генерировать имена в первую очередь. Некоторые из обсуждаемых идей были:
Сделать генерацию имени функцией идентификатора приложения. Это было бы намного быстрее и полностью исключило бы проблему со столкновениями, но с другой стороны это привело бы к потере большого количества имен с удаленными приложениями (и, черт возьми, у нас МНОГО приложений, которые создаются и удаляются вскоре после этого как часть различных интеграционных тестов) .
Другой вариант сделать детерминированную генерацию имен - это иметь пул доступных имен в базе данных. Это упростит повторное использование имени через две недели после удаления приложения.
Рад видеть, что мы сделаем в следующий раз, когда появится предупреждение о столкновении!
Надеюсь, это поможет всем, кто работает над созданием дружеских имен.