Usando ULIDs para criar ordem em dados não ordenados

E quanto ao UUID?

Eu descobri a necessidade de ULID / KSUID ao trabalhar com objetos S3 que precisavam ser nomeados, mas também queria poder consultar objetos recentes. Normalmente, quando preciso de um identificador aleatório, procuro UUID-v4. Por que v4?

  • UUID v1 e v2 contêm endereços MAC com base no host que os gera. Este não é realmente um problema de segurança, já que um endereço L2 não ajudará muito na Internet pública. No entanto, isso significa que se meus UUIDs forem gerados em Lambdas, os endereços MAC não terão valor semântico. Não consigo usar SSH em meu Lambda e procurar o endereço MAC ou usar essas informações de outra forma.
  • UUID v3 requer uma entrada, e eu usaria apenas random.randint() ou o equivalente para escolher meu valor de entrada. Qualquer sistema que requeira uma entrada significa que tenho que pensar sobre o que usar como entrada, como isso afeta a aleatoriedade e como isso pode afetar a segurança ou as colisões.
  • O UUID v4 é aleatório, mas, como é totalmente aleatório, não fornece sobrecarga semântica.

ULIDs para habilitar consultas de tempo no Amazon S3

O pensamento baseado em índices pode ser esclarecedor, especialmente porque a TI está repleta de sistemas de armazenamento classificados intrinsecamente. O Amazon S3 classifica as chaves e prefixos de seus objetos ao retorná-los, independentemente da ordem em que foram adicionados.

$ aws --profile personal s3 ls s3://t10-blog-ulids
2020-04-13 21:17:53 3 01E5V474WE4DE0N63ZWT7P6YWH
2020-04-13 21:17:54 3 01E5V475QFRCEHXKJAS3BRS6BV
2020-04-13 21:24:51 3 01E5V4KXFTP52C9M5DVPQ2XR8T
2020-04-13 21:48:33 3 01E5V5Z9J0GX72VFSENBCKMHF0
>>> import ulid
>>> u = ulid.new()
>>> u.str
'01E5V7GWA9CHP337PB8SR18ZP4'
>>> u.bytes
b'\x01qvxqIdl1\x9e\xcbFp\x14~\xc4'
>>> u.int
1918360407572615930874316424782053060
>>> u.uuid
UUID('01717a42-cde2-b5be-eed8-55222c867b58')
>>> u.float
1.918360407572616e+36
>>> bin(u.int)
'0b1011100010111011001111000011100010100100101100100011011000011000110011110110010110100011001110000000101000111111011000100'

Geração Descentralizada

Porque o formato ULID de timestamp de 48 bits + aleatoriedade de 100 bits significa que obtemos 100 bits por milissegundo, o que quase elimina a chance de colisões*. Compare isso com nossa coluna numérica de incremento automático. O incremento faz com que tenhamos que centralizar o gerenciamento desse número no banco de dados para evitar conflitos de ID. Com ULIDs, podemos gerar IDs em qualquer um de nossos Lambdas, Containers ou instâncias EC2.

ULIDs no DynamoDB

A nova tendência no DynamoDB são os designs de tabela única. Usando uma única tabela com um design que permite que diferentes GSIs atendam a várias consultas. Rick tuitou este exemplo do mundo real do serviço Kindle Collection Rights que atende a 9 consultas com 4 GSIs.

const monotonicFactory = require('ulid').monotonicFactory;
const ulid = monotonicFactory()
ulid(1586872590191)
'01E5WFM7VFPWCNF4DM76ADV80W'
ulid(1586872590191)
'01E5WFM7VFPWCNF4DM76ADV80X'
ulid(1586872590191)
'01E5WFM7VFPWCNF4DM76ADV80Y'
ulid(1586872590191)
'01E5WFM7VFPWCNF4DM76ADV80Z'
ulid(1586872590191)
'01E5WFM7VFPWCNF4DM76ADV810'

Jogando Xadrez com Amazon S3

Vamos voltar ao nosso exemplo anterior S3 por um momento. Ao procurar dados em um intervalo de tempo específico, você pode reduzir significativamente o número de objetos retornados por ListObjects. O argumento Delimiter permite estreitar o intervalo de sua pesquisa em incrementos de 5-bits. Um ULID possui 10 caracteres iniciais que representam um carimbo de data / hora de 48-bits com precisão de milissegundos, com cada caractere codificando 5-bits do número.

t = time character
r = randomness character
ttttttttttrrrrrrrrrrrrrrrr
  • 1º personagem: 407226 dias
  • 2º personagem: 12.725 dias
  • 3º personagem: 397 dias
  • 4º personagem: 12 dias, 10 horas
  • 5º personagem: 9 horas, 19 minutos
  • 6º personagem: 17 minutos, 28 segundos
  • 7º caractere: 32 segundos
  • 8º caractere: 1 segundo
  • 9º caractere: 30 milissegundos
  • 10º caractere: 1 milissegundo
2020-04-13 21:17:54          3 01E5V475QFRCEHXKJAS3BRS6BV
2020-04-13 21:24:51 3 01E5V4KXFTP52C9M5DVPQ2XR8T
2020-04-13 21:48:33 3 01E5V5Z9J0GX72VFSENBCKMHF0
>>> [k['Key'] for k in s3.list_objects_v2(
Bucket='t10-blog-ulids',
Delimiter='4',
Prefix='01E5V4'
)['Contents']]
['01E5V475QFRCEHXKJAS3BRS6BV', '01E5V4KXFTP52C9M5DVPQ2XR8T']


>>> [k['Key'] for k in s3.list_objects_v2(
Bucket='t10-blog-ulids',
Delimiter='5',
Prefix='01E5V5'
)['Contents']]
['01E5V5Z9J0GX72VFSENBCKMHF0']

Finalizando

Nesse artigo, abordamos algumas maneiras pelas quais identificadores carregados semanticamente podem ser úteis em sua camada de armazenamento. No geral, ULIDs e especificações semelhantes para identificadores classificáveis ​​são um aprimoramento do totalmente aleatório padrão do UUID. Eles podem tornar seu aplicativo mais rápido e, ao mesmo tempo, evitar colisões e ataques de enumeração, e também podem ser armazenados com mais eficiência (26 caracteres contra 36).

Créditos

--

--

☕🇳🇿 - https://eduardorabelo.me

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store