3 pontos por GN⁺ 2024-08-27 | 1 comentários | Compartilhar no WhatsApp
  • UUIDs têm 8 versões, da v1 à v8; um número maior não significa que seja mais nova ou melhor, mas sim que diferentes métodos de geração estão definidos na RFC 9562
  • As escolhas mais comuns se reduzem a v4 e v7: v4 é o padrão para IDs aleatórios, enquanto v7 é adequada quando é preciso ordenação pela hora de geração
  • v1 e v6 usam os mesmos componentes, mas a v6 reorganiza a ordem dos campos para que, ao ordenar, o resultado siga a ordem de geração
  • v3 e v5 criam UUIDs aplicando hash aos dados de entrada; v3 usa MD5, v5 usa SHA-1, e DNS e URL podem ser candidatos a valores de entrada
  • Na prática, o fluxo mais útil é usar v7 em vez de v1/v6 quando possível; se for necessário um UUID baseado em dados de entrada, usar v5; e, se for necessário um UUID totalmente personalizado, avaliar v8

Como cada versão de UUID é gerada

  • As versões de UUID vão da v1 à v8 e todas são definidas na RFC 9562
  • UUIDs baseados em tempo

    • UUID Version 1 é gerado com timestamp, contador monotônico e endereço MAC
    • UUID Version 6 usa os mesmos dados da v1, mas muda a ordem para que, ao ordenar, fique em ordem de geração
    • UUID Version 7 é gerado com timestamp e dados aleatórios
  • UUIDs aleatórios e personalizados

    • UUID Version 4 é gerado com dados totalmente aleatórios e se aproxima da forma que muita gente imagina quando pensa em UUID
    • UUID Version 8 é totalmente personalizado, exceto pelos campos version/variant necessários em todas as versões
  • UUIDs baseados em hash dos dados de entrada

    • UUID Version 3 é gerado a partir do hash MD5 dos dados fornecidos pelo usuário
      • Entre os candidatos a valores de entrada na RFC estão DNS e URL
    • UUID Version 5 é gerado a partir do hash SHA-1 dos dados fornecidos pelo usuário
      • Assim como na v3, DNS e URL podem ser candidatos a entradas
  • UUID reservado

    • UUID Version 2 é reservado para IDs de segurança e não há detalhes conhecidos

Critérios práticos de escolha

  • Na maioria dos casos, a escolha é v4 ou v7
  • Se você precisa apenas de um ID aleatório, v4 é uma boa escolha padrão
  • Se o ID precisa ser ordenável, v7 pode ser considerada
    • Por exemplo, v7 pode ser candidata ao usar UUID como chave de banco de dados
  • v5 ou v8 são mais indicadas quando se quer colocar dados próprios dentro do UUID
    • Em geral, nesses casos o usuário já sabe que precisa desse tipo
  • Segundo a RFC, v7 melhora v1 e v6, então, quando possível, deve-se usar v7 em vez de v1/v6
    • Se v1 ou v6 for realmente necessária, v6 pode ser usada
  • v2 é reservada para usos de segurança não especificados
  • v3 foi substituída pela v5, que usa um hash mais forte, e é provável que quem precise da v3 já saiba disso

1 comentários

 
GN⁺ 2024-08-27
Opiniões no Hacker News
  • Dizer que não há detalhes sobre o UUID v2 só vale se você leu apenas o RFC notoriamente pouco claro: https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#t...
    Você quase nunca vai encontrá-lo, mas também existe o UUID version 0. Vale mencioná-lo porque ele é a origem dos bits reservados que permitiram que outras “versões” fossem definidas depois de forma compatível. Deixei a pesquisa relacionada organizada na minha biblioteca de UUID: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
    Resolvi dar suporte porque achei interessante, mas ainda preciso decidir como lidar com rollover de datas e com o formato mais antigo de UID do Apollo

    • Para ser justo, o RFC9562 cita dois documentos da especificação do UUID Version 2. Mas o RFC4122, para o meu gosto, parecia criptografado demais
      Estou começando a trabalhar em um RFC informativo sobre os tipos históricos de UUID definidos no espaço de Variant 0-7, que deve ajudar as pessoas a entendê-los. Se quiser discutir ou revisar a redação, veja https://github.com/yocto/draft-yocto-uuid
  • Deu para encontrar os detalhes em 2 minutos. Clique no link do artigo para ir à parte de definição de DCE no RFC 9562, entre na especificação pelo primeiro link desse parágrafo e pesquise por “UUID” para chegar ao Apêndice A; está tudo lá
    O nome é confuso, “Universal Unique Identifier”, mas o conteúdo necessário está todo ali. Seria bom ao menos clicar nos links que a própria pessoa colocou no texto

    • Há duas coisas que se podem aprender aqui. Uma é ler a especificação do UUID v2, e a outra é inferir, a partir de um UUID, informações sobre as circunstâncias em que ele foi gerado
      Pelo contexto, como as outras versões são descritas como geradas a partir de determinados elementos, essa frase claramente se refere à segunda coisa. A frase é um pouco ambígua, mas é difícil dizer que induz ao erro
    • Passei exatamente pelo mesmo processo e, como pareceu que o autor não tinha se preocupado muito com os detalhes, nem li o artigo até o fim
      Ainda assim, o apêndice foi interessante de ler, como um retrato daquele momento
    • Talvez seja um texto escrito por um modelo de linguagem
  • Seria bom existir um padrão de UUID curto como 73WakrfVbNJBaAmhQtEeDv ou bK7nP9xM
    Tecnicamente, como poderia haver duplicidade em algum lugar, não seria um UUID, mas eu queria uma combinação padronizada de IDs aleatórios e curtos o bastante para memorizar

    • O mais próximo que me vem à mente é o ULID. São 26 caracteres em base32, é curto, tem 128 bits e também permite ordenação lexicográfica
      Acho que o motivo de não haver um padrão mais popular é que é preciso abrir mão de alguma coisa. 128 bits têm baixo risco de colisão para quase todos os usos, mas, quanto menor fica, mais é preciso considerar o contexto específico e o impacto das colisões, o que dificulta transformar isso em padrão. Usar outras codificações, como base64 ou base85, deixaria mais curto, mas sacrificaria coisas como distinção entre maiúsculas e minúsculas e segurança em URLs: https://github.com/ulid/spec
    • Se você precisa de um UUID mais compacto, pode converter um UUID existente para base64 segura para URL e representá-lo em 22 caracteres
      É apenas outra representação do mesmo UUID, e é reversível. No fim, um UUID é um valor de 128 bits, então é mais uma notação alternativa do que uma conversão real
    • Sqids pode ser o que você procura. Ele gera IDs bem mais curtos que UUIDs, mas não são universalmente únicos e são gerados a partir de sequências de inteiros: https://sqids.org/
    • Normalmente, gera-se um número aleatório de N bits e codifica-se em base58. Você escolhe N como quiser
      Perde-se a vantagem de ordenação monotônica presente em algumas versões de UUID, mas base58 é segura para URLs e não inclui caracteres especiais. O valor ainda pode ser armazenado em binário. Por exemplo, no Postgres, dá para usar bytea em vez de uma coluna de texto
    • Há um trabalho em andamento para padronizar técnicas alternativas de codificação que permitam representar UUIDs de 128 bits em formato textual mais curto
      A discussão pode ser vista aqui: https://github.com/uuid6/new-uuid-encoding-techniques-ietf-d...
  • O timestamp do UUID v7 foi uma grande mudança para o Databend. Usamos isso para localizar rapidamente arquivos de metadados por timestamp no AWS S3, então operações como vacuum ficaram muito mais rápidas
    PR: https://github.com/datafuselabs/databend/pull/16049

    • É interessante, mas não me vem muito à cabeça usar UUID para buscar por tempo. Eu provavelmente teria um campo de timestamp separado
      A grande vantagem dos UUIDs ordenados por tempo é melhorar a localidade. Ao inserir novos itens em um índice, geralmente a operação passa a ser anexar ao fim, o que pode ser mais barato do que inserções aleatórias. Por outro lado, também pode aumentar a contenção, então vale considerar um híbrido que coloque alguns bits aleatórios antes do timestamp para criar “shards” ordenados. As leituras também costumam se concentrar nos dados mais recentes, então é útil ter os dados recentes em um só lugar e bem aproveitados no cache
    • Também é muito bom para armazenamentos chave-valor como o DynamoDB. É muito mais limpo do que uma chave composta com timestamp ou data ISO como prefixo, e melhor do que desperdiçar um índice secundário para timestamp
  • É difícil entender o objetivo do uuid2. Eu nem sabia que havia mais tipos assim, e vi uuid2 pela primeira vez quando pedi à Xandr a exclusão dos meus dados pessoais: https://news.ycombinator.com/item?id=40913915
    Mesmo lendo a Wikipedia, não fica muito claro por que, depois de criar algo chamado “identificador universalmente único”, ainda existem vários tipos, e por que alguns deles foram originalmente feitos de modo a permitir rastrear até o PC. Fico me perguntando se misturar parte do código MAC torna o uuid2 mais próximo de aleatório, ou se há outro motivo. Do ponto de vista de privacidade, também me pergunto se não bastaria usar apenas identificadores longos com um conjunto enorme de caracteres possíveis, eliminando na prática a possibilidade de colisão

    • O objetivo original era identificar mensagens na arquitetura de computação distribuída Apollo. O UID e, depois, o UUID eram uma forma reversível de marcar a interseção de duas dimensões
      Quaisquer duas máquinas geravam o mesmo UID/UUID para os mesmos dois inputs, e quem recebesse a mensagem identificada conseguia reverter o identificador para seus componentes originais. Como foi projetado como rótulo para mensagens temporárias, as duas dimensões eram tempo e ID de hardware; no início usava-se o número de série da Apollo e, depois, coisas como o endereço de hardware Ethernet
      Acho que boa parte da confusão vem do fato de que, na implementação inicial do AEGIS, engenheiros da Apollo começaram a usar UIDs “canned”, isto é, estáticos e bem conhecidos, para identificação no sistema de arquivos. Com o tempo, o uso comum do UUID mudou completamente: de identificador temporário em que colisões eram esperadas/intencionais para identificador canned em que colisões deveriam ser evitadas; e as duas dimensões viraram aleatoriedade e mais aleatoriedade
      A história é mais complexa. A Microsoft contratou uma das pessoas centrais da Apollo para criar o MSRPC para o Windows NT, e foi assim que surgiu também o GUID. O GUID tem uma disposição de campos diferente da do UUID e, ao contrário do que muitos materiais dizem, não é mixed-endian. A Microsoft gosta de usar GUIDs canned não só para identificar mensagens RPC temporárias, mas também para praticamente qualquer coisa que precise de identificadores bem conhecidos, como classes COM, codecs de mídia etc. Ex.: https://gix.github.io/media-types/
      Desculpem linkar meu repositório duas vezes na mesma seção de comentários, mas comecei a organizar essa história no README da minha biblioteca de UUID e preciso retomar isso. A Apollo começou em 1980 e o rascunho do RFC de UUID de Leach/Salz só apareceu em 1998, então há muita história que ficou de fora dos padrões modernos: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
  • UUID v4 é apenas um gerador de bytes aleatórios com hífens em posições especificadas. Você não precisa necessariamente usá-lo; pode gerar seus próprios bytes aleatórios e economizar espaço
    Também reduz coisas desnecessárias como hífens e informação de versão

    • UUID é um número de 128 bits, e a representação em string com hífens é apenas uma das várias formas de representar esse número
      É parecido com um endereço IPv4 ser um número de 32 bits, e “quatro grupos separados por pontos” ser uma de suas representações. Se você pensa em UUID como um formato de string, está errando já no conceito mais básico de UUID. Mesmo que você só queira um identificador aleatório, acho interessante haver pequenos bits de flag em um UUID aleatório dizendo “isto foi pensado para ser aleatório”. É útil quando você encontra um identificador isolado, sem contexto
    • O UUID v4 também define 4 bits como valor fixo para indicar que é a versão 4
      Dá para discutir se é útil criar namespaces distintos entre vários métodos de geração, mas um gerador aleatório comum só tem 1/16 de chance de produzir um UUIDv4 válido. Claro que, se você quiser criar seu próprio gerador de UUID, definir os bits corretamente é algo trivial
    • Certo, mas para a maioria dos desenvolvedores o atrativo é a simplicidade de implementação. Quase toda linguagem tem uma biblioteca de UUID que funciona em uma linha
      Em Go, basta uuid.New().String(), mas ler dados aleatórios com crypto/rand e convertê-los para base64 ou hex exige mais linhas e mais esforço
  • Recomendo não usar as versões baseadas em MAC. Em teoria, isso pode se aplicar a todas exceto v4 e v7, mas a v1 é a pior
    A v3 também tem o problema de que o MD5 foi seriamente quebrado

    • MD5 é “quebrado” como função hash criptográfica. Como função hash não criptográfica, ainda é completamente aceitável
  • Eu não conhecia os detalhes além da versão 4, mas o que parece realmente faltar e seria útil é um método usando dados SHA256 e um contador. Algo parecido com PBKDF2
    Poderia ser um identificador derivado que preserva a privacidade e também permitiria provar de forma flexível que um determinado UUID foi derivado de uma certa semente

    • Se você realmente precisar disso, pode tratar o UUIDv4 como um formato de codificação, gerar uma saída de 122 bits com um algoritmo criptográfico e então codificá-la como UUID
      Fora isso, você provavelmente vai querer uma saída mais longa
    • É parecido com a v3, mas em uma forma que permite especificar o algoritmo de hash
  • É só usar v7
    Agora é a vez dos especialistas em segurança dizerem que não

    • Se a data de geração puder ser uma informação sensível, ou se o UUID precisar ser totalmente imprevisível, use v4. Fora isso, use v7
    • O que sempre achei frustrante em UUID e ULID é que, até onde sei, não há uma boa forma de gerá-los deterministicamente
      Em muitos usos, seria muito útil conseguir gerar o mesmo ID ao reprocessar os dados, mas não conheço uma forma padrão de fazer isso
    • Não entendo por que tratam questões de segurança com tanta leveza