increase username randomness to stop pages (#1261)

We were getting several username collisions a day under the old
algorithm
This commit is contained in:
Brandon Thomas authored and GitHub committed 2026-03-31 00:31:22 -04:00
1 parent a2a9b3b7de
commit 0c2b800494
5 files changed
+621 -41

No files matched your search

@@ -76,63 +76,88 @@ fn generate_candidate_username() -> Option<String> {
UsernameFormat::ScreamingSnakeCase => format!("{}_{}", adjective.to_uppercase(), noun.to_uppercase()),
};
if let Some(digit) = maybe_random_safe_digit() {
candidate_username = match format {
UsernameFormat::CamelCase => format!("{}{}", candidate_username, digit),
UsernameFormat::KebabCase
| UsernameFormat::CamelKebabCase => format!("{}-{}", candidate_username, digit),
UsernameFormat::SnakeCase
| UsernameFormat::CamelSnakeCase
| UsernameFormat::ScreamingSnakeCase => format!("{}_{}", candidate_username, digit),
};
}
let digit = random_digit_probably_safe();
candidate_username = match format {
UsernameFormat::CamelCase => format!("{}{}", candidate_username, digit),
UsernameFormat::KebabCase
| UsernameFormat::CamelKebabCase => format!("{}-{}", candidate_username, digit),
UsernameFormat::SnakeCase
| UsernameFormat::CamelSnakeCase
| UsernameFormat::ScreamingSnakeCase => format!("{}_{}", candidate_username, digit),
};
Some(candidate_username)
}
fn maybe_random_safe_digit() -> Option<u32> {
fn maybe_random_digit() -> Option<u32> {
// Give uniform probability for the number of digits
let num_digits = rand::thread_rng().gen_range(0..5u8);
match num_digits {
0 => None,
1 => Some(rand::thread_rng().gen_range(0..10)),
2 => Some(rand::thread_rng().gen_range(10..100)),
3 => Some(rand::thread_rng().gen_range(100..1000)),
4 => Some(rand::thread_rng().gen_range(1000..10000)),
_ => Some(rand::thread_rng().gen_range(10000..100000)),
fn random_digit_probably_safe() -> u32 {
for _ in 0..100 {
if let Some(digit) = maybe_safe_random_digit() {
return digit;
}
}
random_digit()
}
// Don't return potentially offensive numbers
match maybe_random_digit() {
None => None,
Some(69) => None,
Some(420) => None,
Some(666) => None,
Some(8008) => None,
Some(80085) => None,
Some(8008135) => None,
Some(digit) => Some(digit),
/// Don't return potentially offensive numbers
fn maybe_safe_random_digit() -> Option<u32> {
match random_digit() {
69 => None,
420 => None,
666 => None,
8008 => None,
80085 => None,
8008135 => None,
digit => Some(digit),
}
}
fn random_digit() -> u32 {
// Non-uniform probability for the number of digits
let num_digits = rand::thread_rng().gen_range(0..100);
match num_digits {
0..5 => rand::thread_rng().gen_range(0..10), // 5%
5..25 => rand::thread_rng().gen_range(10..100), // 20%
25..60 => rand::thread_rng().gen_range(100..1000), // 35%
60..90 => rand::thread_rng().gen_range(1000..10000), // 30%
_ => rand::thread_rng().gen_range(10000..100000), // 10%
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use crate::username::generate_random_username::maybe_safe_random_digit;
use crate::username::generate_random_username::generate_random_username;
#[test]
fn test_base_case_success() {
assert!(generate_random_username().len() > 0);
mod usernames {
use super::*;
#[test]
fn test_base_case_success() {
assert!(generate_random_username().len() > 0);
}
#[test]
fn generate_lots() {
let mut collection = HashSet::new();
for _ in 0..100 {
collection.insert(generate_random_username());
}
assert!(collection.len() > 95); // NB: Should be an easy bar to hit
}
}
#[test]
fn generate_lots() {
let mut collection = HashSet::new();
for _ in 0..100 {
collection.insert(generate_random_username());
mod random_numbers {
use super::*;
#[test]
fn generate_lots_of_numbers() {
let mut collection = HashSet::new();
for _ in 0..10000 {
collection.insert(maybe_safe_random_digit());
}
assert!(collection.len() > 1000);
}
assert!(collection.len() > 50); // NB: Should be an easy bar to hit
}
}
@@ -47,7 +47,7 @@ pub async fn handle_new_sso_account_for_new_user(
let mut maybe_user_token = None;
let mut maybe_user_display_name = None;
for _ in 0..3 {
for _ in 0..5 {
// NB: We try a few times to make sure we don't hit a username collision.
let display_name = generate_random_username();
let username = display_name.trim().to_lowercase();
@@ -14,64 +14,100 @@ adept
admirable
advanced
adventurous
aerial
agile
alpine
altruistic
amazing
ambitious
amused
amusing
ancient
animated
apt
aquatic
arctic
artistic
astral
authentic
awesome
balanced
best
better
blazing
bold
booming
boundless
brave
breezy
bright
brilliant
brisk
bronze
calm
capital
carefree
celestial
champion
charming
cheerful
classic
clever
coastal
colossal
complex
composed
confident
constant
conventional
cool
coordinated
copper
cosmic
courageous
crafty
crazy
creative
crisp
critical
crystal
dapper
daring
dazzling
decisive
deep
definite
definitive
delightful
determined
devoted
digital
diligent
distinct
dizzy
double
downright
dramatic
driven
dual
dynamic
eager
earnest
effortless
elaborate
elated
electric
elegant
elevated
emerald
enchanted
endless
energetic
enlightened
epic
essential
esteemed
eternal
evergreen
exalted
excellent
@@ -81,19 +117,40 @@ exciting
exemplary
experienced
expert
exquisite
famous
fancy
fantastic
favorable
favorite
fearless
fiery
fine
flagship
flawless
fleet
fluent
flying
focal
focused
forged
formal
formidable
fortunate
friendly
frosted
frosty
funny
fuzzy
gallant
gargantuan
general
generous
gentle
genuine
gigantic
glacial
glad
glamorous
glass
gleaming
@@ -101,15 +158,28 @@ glistening
glittering
glorious
glossy
glowing
golden
graceful
grand
granite
grateful
great
grounded
growing
growling
grumpy
hallowed
happy
harmonious
haunting
heavenly
heroic
highland
hilarious
honest
hopeful
humble
idealistic
illustrious
imaginary
@@ -117,6 +187,7 @@ imaginative
immaculate
immaterial
immediate
immense
impartial
impeccable
important
@@ -126,55 +197,108 @@ improbable
incredible
infamous
infinite
inspired
instant
intelligent
intrepid
inventive
invincible
iron
jolly
jovial
joyful
keen
kind
knowing
lavish
leading
legendary
light
linear
lively
loud
loyal
lucky
luminous
lunar
lush
magnetic
magnificent
majestic
major
marble
marvelous
masterful
mellow
melodic
memorable
meridian
metallic
mighty
mindful
modern
modest
monumental
moonlit
mosaic
mountain
moving
mysterious
mystic
natural
nautical
necessary
neighborly
nifty
nimble
noble
nocturnal
northern
notable
noteworthy
novel
obvious
oceanic
onyx
optimal
optimistic
opulent
orbital
original
ornate
outgoing
outrageous
outstanding
overland
pacific
parallel
passionate
pastel
patient
peak
perfect
persistent
pioneer
placid
plastic
polished
popular
posh
positive
possible
powerful
practical
premier
premium
prestigious
previous
primary
prime
pristine
probable
profound
prolific
prominent
proud
prudent
punctual
@@ -190,13 +314,22 @@ rare
real
realistic
recent
refined
regal
relentless
remarkable
resolute
rising
robust
royal
rugged
rustic
sarcastic
sardonic
scenic
scholarly
scientific
seasoned
second
secret
serious
@@ -208,15 +341,22 @@ shiny
shocking
silent
silly
silver
similar
sincere
skilled
sleepy
slight
slim
smart
snappy
sneaky
soaring
solar
solid
sonic
sophisticated
southern
sparkling
specific
spectacular
@@ -224,37 +364,65 @@ speedy
spiffy
spirited
splendid
spring
stalwart
starlit
starry
steadfast
steady
steel
stellar
stoic
stout
strategic
strong
studious
stupendous
stylish
subtle
suburban
summit
sunlit
sunny
super
superb
supreme
surprised
swift
talkative
tall
tenacious
terrific
thermal
third
thorough
thoughtful
thunderous
tidal
timely
titanium
topaz
tough
towering
tricky
triple
tropical
trusted
twilight
ultimate
uncommon
undaunted
unique
united
universal
unwavering
upbeat
urban
valid
valiant
vast
venerated
verdant
vibrant
victorious
vigilant
@@ -262,6 +430,8 @@ virtual
virtuous
visible
vivid
vocal
wandering
watchful
whimsical
wild
@@ -24,25 +24,48 @@ ambition
amount
analysis
analyst
anchor
angle
answer
anthem
anybody
anything
anywhere
application
arc
arena
armor
art
aspect
atlas
aurora
autumn
banner
bastion
battle
beacon
beach
beat
being
bell
bet
beyond
blade
blaze
bolt
book
boulder
bridge
brush
cadence
caliber
campaign
campus
candidate
canyon
captain
castle
catalyst
champion
chance
channel
@@ -51,71 +74,129 @@ character
charge
chemical
chemistry
chronicle
circuit
citadel
city
cliff
cloud
coast
code
coffee
comet
command
compass
computer
concept
concert
condition
connection
consideration
conquest
constant
construction
context
cookie
corner
cosmos
country
courage
craft
crest
creative
crew
crown
crystal
current
cycle
dance
data
dawn
delta
department
design
device
diamond
dimension
direction
director
discovery
disk
district
doctor
domain
drawing
dream
drift
drive
dusk
echo
eclipse
edge
effect
ember
empire
energy
engine
engineer
engineering
entertainment
envoy
epoch
factor
fall
fathom
final
finding
fix
flare
flash
flight
focus
force
forecast
forever
forge
formation
formula
fortress
fortune
foundation
fountain
frame
frontier
frost
future
galaxy
game
garden
gateway
gem
genesis
glacier
glass
gleam
glimpse
goal
gold
granite
grove
guardian
guitar
gust
harbor
harvest
haven
haze
helm
herald
highlight
highway
history
holiday
hollow
horizon
hub
ice
idea
ideal
@@ -123,47 +204,104 @@ image
imagination
impact
impression
infinity
instance
instrument
intention
introduction
invention
island
jewel
joke
journal
journey
junction
keystone
knowledge
lagoon
lake
land
landscape
lantern
lattice
league
legacy
legend
lens
level
light
lighthouse
limit
line
link
lock
lodge
loft
machine
marble
market
meadow
mechanism
melody
mention
meridian
mesa
message
metal
meteor
method
midnight
milestone
mind
mine
minute
mirage
mirror
mission
model
moment
momentum
monument
mosaic
motor
mountain
move
movie
mural
music
mystery
nerve
network
nexus
news
night
nimbus
nomad
north
notion
oasis
ocean
odyssey
opinion
opus
orbit
original
outpost
overture
page
painting
palace
panorama
parade
paragon
party
passage
passenger
passport
path
pattern
peak
pebble
perspective
phase
philosophy
@@ -174,20 +312,30 @@ physical
physics
piano
picture
pillar
pinnacle
pioneer
pixel
pizza
place
plan
plane
planet
player
plaza
plume
point
policy
polaris
position
possibility
potato
potential
power
praxis
present
president
prism
procedure
process
professor
@@ -196,45 +344,73 @@ program
progress
project
prompt
prospect
pulse
purpose
puzzle
pyramid
quantity
quantum
quest
question
quill
quote
radiance
rain
rampart
range
ratio
reach
reaction
reality
realm
reason
recipe
record
reference
reflection
relay
relic
resolution
resort
resource
result
return
ridge
rift
river
road
rock
rocket
rule
rune
sage
sail
sanctuary
sapphire
scene
science
scope
score
screen
script
scroll
season
sector
sentinel
shadow
shift
shimmer
shine
ship
show
sign
signal
sign
singer
sketch
sky
slate
smoke
snow
society
@@ -243,54 +419,94 @@ song
sound
source
space
spark
spectrum
speech
sphere
spire
spring
stable
stage
star
start
state
station
status
stone
storm
strategy
street
strength
stride
strike
string
stronghold
structure
studio
style
success
summit
summer
sunrise
sunset
surprise
survey
sword
symbol
system
tale
target
team
technology
tempest
tempo
terrain
theme
theory
thing
thought
threshold
thunder
tide
timber
time
title
token
torch
tower
track
trade
trail
train
travel
treasure
trick
trip
tribute
triumph
truck
tune
tundra
tunnel
twilight
unit
valor
vault
venture
version
vessel
video
view
vista
voice
volume
voyage
wave
way
weather
whirl
wind
wing
winter
wish
world
@@ -298,4 +514,6 @@ writer
writing
year
yesterday
zenith
zephyr
zone
@@ -2,35 +2,202 @@
# https://github.com/jneidel/animal-names/blob/master/animals-common.txt
# This has been trimmed to remove potentially negative, offensive, uncool, or otherwise bad choices
albatross
alligator
alpaca
antelope
armadillo
axolotl
badger
barracuda
bobcat
cardinal
caribou
cat
chameleon
cheetah
cobra
condor
corgi
coyote
crane
crocodile
dingo
dolphin
dragon
dragonfly
eagle
elk
falcon
finch
fox
gazelle
gecko
giraffe
hawk
hedgehog
heron
hyena
iguana
impala
jaguar
kangaroo
kiwi
kleekai
koala
komodo
lemur
leopard
lion
llama
lynx
mako
marlin
marmot
meerkat
narwhal
nighthawk
orca
osprey
owl
panda
parrot
penguin
phoenix
puma
python
rabbit
raccoon
raptor
raven
reindeer
roadrunner
shark
shiba
sparrow
sphinx
squirrel
taipan
tiger
toucan
unicorn
viper
wallaby
wildcat
wolf
wolverine
wombat
zebra
# Probably not good animals to use, for one reason or another:
# baboon
# bat
# bear
# beaver
# bison
# buffalo
# bunny
# butterfly
# camel
# chinchilla
# chipmunk
# cockatoo
# cougar
# crow
# cuttlefish
# deer
# dove
# duck
# eel
# elephant
# emu
# ermine
# ferret
# firefly
# flamingo
# frog
# goose
# gorilla
# grasshopper
# grizzly
# grouse
# hamster
# hare
# hippo
# hornet
# hound
# hummingbird
# husky
# ibex
# ibis
# jackal
# jackrabbit
# jay
# jellyfish
# kestrel
# kingfisher
# kookaburra
# lizard
# lobster
# macaw
# mammoth
# manatee
# mandrill
# mantis
# mockingbird
# mongoose
# moose
# mustang
# newt
# ocelot
# octopus
# okapi
# oriole
# otter
# ox
# panther
# peacock
# pelican
# peregrine
# pheasant
# piranha
# platypus
# porcupine
# porpoise
# quail
# quetzal
# ram
# rhino
# robin
# rooster
# sailfish
# salamander
# salmon
# seahorse
# seal
# squid
# stallion
# starfish
# starling
# stingray
# stork
# sturgeon
# swan
# swordfish
# tamarin
# tapir
# tarpon
# terrapin
# thrush
# trout
# tuna
# turtle
# vulture
# walrus
# warthog
# wasp
# weasel
# whale
# woodpecker
# wren
# yak